iphone ios animation uilabel

iphone - Animando el cambio de tamaño de fuente UILabel



ios animation (7)

Actualmente estoy haciendo una aplicación que usa un contenedor de controlador de vista personalizado. Múltiples vistas están en la pantalla a la vez y cuando se toca una, el controlador de vista seleccionado se anima a pantalla completa. Al hacerlo, los controles de vista seleccionados suben también la escala (fotograma, tamaño de fuente, etc.). Sin embargo, la propiedad de fuente de UILabel no es animable, lo que genera problemas. He intentado con múltiples soluciones, pero todo es una mierda.

Las soluciones que he probado son:

  1. Toma una captura de pantalla de la vista más grande y anima el cambio (similar a como lo hace Flipboard)
  2. Animar mediante el uso de la propiedad transformar
  3. Alejar un UIScrollView y acercarlo cuando aparece en pantalla completa.
  4. El ajuste ajustaFontSizeToFitWidth a YES y configura fontSize antes de la animación

Una ha sido la mejor solución hasta ahora, pero no estoy satisfecho con ella.

Estoy buscando otras sugerencias si alguien tiene alguno o un sustituto UILabel que se anima sin problemas utilizando [UIView animate ..].

Aquí hay un buen ejemplo que es similar a lo que me gustaría que hiciera mi UILabel: http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html

EDITAR: Este código funciona

// Load View self.label = [[UILabel alloc] init]; self.label.text = @"TEXT"; self.label.font = [UIFont boldSystemFontOfSize:20.0]; self.label.backgroundColor = [UIColor clearColor]; [self.label sizeToFit]; [self.view addSubview:self.label]; // Animation self.label.font = [UIFont boldSystemFontOfSize:80.0]; self.label.transform = CGAffineTransformScale(self.label.transform, .25, .25); [self.label sizeToFit]; [UIView animateWithDuration:1.0 animations:^{ self.label.transform = CGAffineTransformScale(self.label.transform, 4.0, 4.0); self.label.center = self.view.center; } completion:^(BOOL finished) { self.label.font = [UIFont boldSystemFontOfSize:80.0]; self.label.transform = CGAffineTransformScale(self.label.transform, 1.0, 1.0); [self.label sizeToFit]; }];


Para 2017 en adelante ...

Swift 3.0, 4.0

UIView.animate(withDuration: 0.5) { label.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) //Scale label area }


Para alguien que quiere ajustar la dirección de la animación

He creado una extensión para UILabel para animar el cambio de tamaño de fuente

extension UILabel { func animate(fontSize: CGFloat, duration: TimeInterval) { let startTransform = transform let oldFrame = frame var newFrame = oldFrame let scaleRatio = fontSize / font.pointSize newFrame.size.width *= scaleRatio newFrame.size.height *= scaleRatio newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * 0.5 newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * 0.5 frame = newFrame font = font.withSize(fontSize) transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio); layoutIfNeeded() UIView.animate(withDuration: duration, animations: { self.transform = startTransform newFrame = self.frame }) { (Bool) in self.frame = newFrame } }

Si desea ajustar la dirección de la animación, utilice el método a continuación y coloque un punto de anclaje adecuado.

RÁPIDO

struct LabelAnimateAnchorPoint { // You can add more suitable archon point for your needs static let leadingCenterY = CGPoint.init(x: 0, y: 0.5) static let trailingCenterY = CGPoint.init(x: 1, y: 0.5) static let centerXCenterY = CGPoint.init(x: 0.5, y: 0.5) static let leadingTop = CGPoint.init(x: 0, y: 0) } extension UILabel { func animate(fontSize: CGFloat, duration: TimeInterval, animateAnchorPoint: CGPoint) { let startTransform = transform let oldFrame = frame var newFrame = oldFrame let archorPoint = layer.anchorPoint let scaleRatio = fontSize / font.pointSize layer.anchorPoint = animateAnchorPoint newFrame.size.width *= scaleRatio newFrame.size.height *= scaleRatio newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y frame = newFrame font = font.withSize(fontSize) transform = CGAffineTransform.init(scaleX: 1 / scaleRatio, y: 1 / scaleRatio); layoutIfNeeded() UIView.animate(withDuration: duration, animations: { self.transform = startTransform newFrame = self.frame }) { (Bool) in self.layer.anchorPoint = archorPoint self.frame = newFrame } } }

C OBJETIVO

// You can add more suitable archon point for your needs #define kLeadingCenterYAnchorPoint CGPointMake(0.f, .5f) #define kTrailingCenterYAnchorPoint CGPointMake(1.f, .5f) #define kCenterXCenterYAnchorPoint CGPointMake(.5f, .5f) #define kLeadingTopAnchorPoint CGPointMake(0.f, 0.f) @implementation UILabel (FontSizeAnimating) - (void)animateWithFontSize:(CGFloat)fontSize duration:(NSTimeInterval)duration animateAnchorPoint:(CGPoint)animateAnchorPoint { CGAffineTransform startTransform = self.transform; CGRect oldFrame = self.frame; __block CGRect newFrame = oldFrame; CGPoint archorPoint = self.layer.anchorPoint; CGFloat scaleRatio = fontSize / self.font.pointSize; self.layer.anchorPoint = animateAnchorPoint; newFrame.size.width *= scaleRatio; newFrame.size.height *= scaleRatio; newFrame.origin.x = oldFrame.origin.x - (newFrame.size.width - oldFrame.size.width) * animateAnchorPoint.x; newFrame.origin.y = oldFrame.origin.y - (newFrame.size.height - oldFrame.size.height) * animateAnchorPoint.y; self.frame = newFrame; self.font = [self.font fontWithSize:fontSize]; self.transform = CGAffineTransformScale(self.transform, 1.f / scaleRatio, 1.f / scaleRatio); [self layoutIfNeeded]; [UIView animateWithDuration:duration animations:^{ self.transform = startTransform; newFrame = self.frame; } completion:^(BOOL finished) { self.layer.anchorPoint = archorPoint; self.frame = newFrame; }]; } @end

Por ejemplo, para animar cambiando el tamaño de la fuente de la etiqueta a 30, la duración 1 desde el centro y la escala más grande. Simplemente llame

RÁPIDO

YOUR_LABEL.animate(fontSize: 30, duration: 1, animateAnchorPoint: LabelAnimateAnchorPoint.centerXCenterY)

C OBJETIVO

[YOUR_LABEL animateWithFontSize:30 duration:1 animateAnchorPoint:kCenterXCenterYAnchorPoint];


Encontré cada una de las sugerencias aquí inadecuadas por estas razones:

  1. En realidad, no cambian el tamaño de la fuente.
  2. No juegan bien con el tamaño del marco y el diseño automático.
  3. Su interfaz no es trivial y / o no juega bien dentro de los bloques de animación.

Para conservar todas estas características y obtener una transición de animación uniforme, he combinado el enfoque de transformación y el enfoque de fuente.

La interfaz es simple. Simplemente actualice la propiedad fontSize y actualizará el tamaño de la fuente. Haga esto dentro de un bloque de animación y animará.

@interface UILabel(MPFontSize) @property(nonatomic) CGFloat fontSize; @end

En cuanto a la implementación, está la manera simple, y está la mejor manera.

Sencillo:

@implementation UILabel(MPFontSize) - (void)setFontSize:(CGFloat)fontSize { CGAffineTransform originalTransform = self.transform; UIFont *targetFont = [self.font fontWithSize:fontSize]; [UIView animateWithDuration:0 delay:0 options:0 animations:^{ self.transform = CGAffineTransformScale( originalTransform, fontSize / self.fontSize, fontSize / self.fontSize ); } completion:^(BOOL finished) { self.transform = originalTransform; if (finished) self.font = targetFont; }]; } - (CGFloat)fontSize { return self.font.pointSize; }; @end

Ahora, el problema con esto es que el diseño puede tartamudear al completarse, porque el marco de la vista se dimensiona en función de la fuente original hasta la finalización de la animación, en cuyo momento el marco se actualiza para acomodar la fuente objetivo sin animación.

Reparar este problema es un poco más difícil porque necesitamos anular intrinsicContentSize . Puedes hacerlo subclasificando UILabel o modificando el método. Personalmente agito el método, porque me permite mantener una propiedad fontSize genérica disponible para todos los UILabel , pero eso depende de algún código de biblioteca que no puedo compartir aquí. Aquí es cómo harías con subclases.

Interfaz:

@interface AnimatableLabel : UILabel @property(nonatomic) CGFloat fontSize; @end

Implementación:

@interface AnimatableLabel() @property(nonatomic) UIFont *targetFont; @property(nonatomic) UIFont *originalFont; @end @implementation AnimatableLabel - (void)setFontSize:(CGFloat)fontSize { CGAffineTransform originalTransform = self.transform; self.originalFont = self.font; self.targetFont = [self.font fontWithSize:fontSize]; [self invalidateIntrinsicContentSize]; [UIView animateWithDuration:0 delay:0 options:0 animations:^{ self.transform = CGAffineTransformScale( originalTransform, fontSize / self.fontSize, fontSize / self.fontSize ); } completion:^(BOOL finished) { self.transform = originalTransform; if (self.targetFont) { if (finished) self.font = self.targetFont; self.targetFont = self.originalFont = nil; [self invalidateIntrinsicContentSize]; } }]; } - (CGFloat)fontSize { return self.font.pointSize; }; - (CGSize)intrinsicContentSize { @try { if (self.targetFont) self.font = self.targetFont; return self.intrinsicContentSize; } @finally { if (self.originalFont) self.font = self.originalFont; } } @end


Puedes cambiar el tamaño y la fuente de tu UILabel con animación como la siguiente. Aquí solo pongo el ejemplo de cómo cambiar la fuente de UILabel con la animación de transformación.

yourLabel.font = [UIFont boldSystemFontOfSize:35]; // set font size which you want instead of 35 yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 0.35, 0.35); [UIView animateWithDuration:1.0 animations:^{ yourLabel.transform = CGAffineTransformScale(yourLabel.transform, 5, 5); }];

Espero que esto te sea útil ..


También podría usar CATextLayer que tiene fontSize como una propiedad animable.

let startFontSize: CGFloat = 20 let endFontSize: CGFloat = 80 let textLayer = CATextLayer() textLayer.string = "yourText" textLayer.font = yourLabel.font.fontName as CFTypeRef? textLayer.fontSize = startFontSize textLayer.foregroundColor = UIColor.black.cgColor textLayer.contentsScale = UIScreen.main.scale //for some reason CATextLayer by default only works for 1x screen resolution and needs this line to work properly on 2x, 3x, etc. ... textLayer.frame = parentView.bounds parentView.layer.addSublayer(textLayer) //animation: let duration: TimeInterval = 1 textLayer.fontSize = endFontSize //because upon completion of the animation CABasicAnimation resets the animated CALayer to its original state (as opposed to changing its properties to the end state of the animation), setting fontSize to endFontSize right BEFORE the animation starts ensures the fontSize doesn''t jump back right after the animation. let fontSizeAnimation = CABasicAnimation(keyPath: "fontSize") fontSizeAnimation.fromValue = startFontSize fontSizeAnimation.toValue = endFontSize fontSizeAnimation.duration = duration fontSizeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) textLayer.add(fontSizeAnimation, forKey: nil)

Lo utilicé en mi proyecto: https://github.com/yinanq/AngelListJobs

Esta animación mantiene la fuente en la parte superior izquierda alineada (a diferencia de CGAffineTransformScale escalando la etiqueta desde el centro), pro o contra dependiendo de sus necesidades. Una desventaja de CATextLayer es que los CALayers no funcionan con la animación de restricción de autolayout (que casualmente necesitaba) y la resolvió haciendo una UIView que contenía solo CATextLayer y animaba sus restricciones).


UILabel extensión UILabel en Swift.

import UIKit extension UILabel { func animate(font: UIFont, duration: TimeInterval) { // let oldFrame = frame let labelScale = self.font.pointSize / font.pointSize self.font = font let oldTransform = transform transform = transform.scaledBy(x: labelScale, y: labelScale) // let newOrigin = frame.origin // frame.origin = oldFrame.origin // only for left aligned text // frame.origin = CGPoint(x: oldFrame.origin.x + oldFrame.width - frame.width, y: oldFrame.origin.y) // only for right aligned text setNeedsUpdateConstraints() UIView.animate(withDuration: duration) { //L self.frame.origin = newOrigin self.transform = oldTransform self.layoutIfNeeded() } } }

Descomente las líneas si el texto de la etiqueta está alineado a la izquierda o a la derecha.


Swift 3.0 y Swift 4.0

UIView.animate(withDuration: 0.5, delay: 0.1, options: .curveLinear, animations: { label.transform = label.transform.scaledBy(x:4,y:4) //Change x,y to get your desired effect. } ) { (completed) in //Animation Completed }