web-dev-qa-db-fra.com

Animation du changement de taille de police UILabel

Je crée actuellement une application qui utilise un conteneur View Controller personnalisé. Plusieurs vues sont affichées à l'écran en même temps et lorsqu'une est tapée, le contrôleur de vue sélectionné s'anime en plein écran. Ce faisant, les sous-vues des contrôleurs de vue sélectionnés sont également mises à l'échelle (cadre, taille de police, etc.). Cependant, la propriété de police d'UILabel n'est pas animable, ce qui entraîne des problèmes. J'ai essayé plusieurs solutions, mais tout est nul.

Les solutions que j'ai essayées sont:

  1. Prenez une capture d'écran de la vue agrandie et animez le changement (comme le fait Flipboard)
  2. Animer en utilisant la propriété transform
  3. Zoom arrière sur un UIScrollView et zoom avant lorsqu'il est porté en plein écran.
  4. Le réglage ajusteFontSizeToFitWidth sur YES et le réglage de fontSize avant l'animation

L'une a été la meilleure solution jusqu'à présent, mais je n'en suis pas satisfaite.

Je recherche d'autres suggestions si quelqu'un en a un ou un substitut UILabel qui s'anime facilement en utilisant [UIView animate ..].

Voici un bon exemple similaire à ce que j'aimerais que mon UILabel fasse: http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html

EDIT: Ce code fonctionne

// 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];

}];
62
morcutt

Vous pouvez changer la taille et la police de votre UILabel avec une animation comme ci-dessous .. ici je viens de mettre l'exemple de comment changer la police de UILabel avec transformer Animation ..

    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);
    }];

J'espère que cela vous sera utile ..

55
Paras Joshi

À partir de 2017 ....

Swift 3.0, 4.

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

J'ai créé l'extension UILabel dans 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()
        }
    }
}

Décommentez les lignes si le texte de l'étiquette est aligné à gauche ou à droite.

21
mixel

Vous pouvez également utiliser CATextLayer qui a fontSize comme propriété 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)

Je l'ai utilisé dans mon projet: https://github.com/yinanq/AngelListJobs

Cette animation maintient la police en haut à gauche alignée (contrairement à CGAffineTransformScale qui met à l'échelle l'étiquette depuis le centre), pro ou con selon vos besoins. Un inconvénient de CATextLayer est que les CALayers ne fonctionnent pas avec l'animation de contraintes de mise en page automatique (dont j'ai eu besoin et que j'ai résolue en créant une UIView contenant uniquement le CATextLayer et en animant ses contraintes).

13
Yinan Qiu

Pour quelqu'un qui veut ajuster la direction de l'animation

J'ai créé une extension pour UILabel pour animer le changement de taille de police

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 vous souhaitez ajuster la direction de l'animation, utilisez la méthode ci-dessous et mettez un point d'ancrage approprié.

rapide

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
    }
  }
}

OBJECTIF-C

// 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

Par exemple, pour animer la modification de la taille de la police d'étiquette à 30, la durée est de 1 seconde à partir du centre et d'une échelle plus grande. Appelez simplement

rapide

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

OBJECTIF-C

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

Swift 3.0 & Swift 4.

 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      

    }
4
Sam Bing

J'ai trouvé chacune des suggestions ici inadéquate pour ces raisons:

  1. Ils ne changent pas réellement la taille de la police.
  2. Ils ne fonctionnent pas bien avec le dimensionnement du cadre et la mise en page automatique.
  3. Leur interface n'est pas triviale et/ou ne joue pas Nice à l'intérieur des blocs d'animation.

Afin de conserver toutes ces fonctionnalités et d'obtenir une transition d'animation fluide, j'ai combiné l'approche de transformation et l'approche de police.

L'interface est simple. Mettez simplement à jour la propriété fontSize et vous mettrez à jour la taille de la police. Faites-le dans un bloc d'animation et il s'animera.

@interface UILabel(MPFontSize)

@property(nonatomic) CGFloat fontSize;

@end

Quant à l'implémentation, il y a la manière simple, et il y a la meilleure manière.

Simple:

@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

Maintenant, le problème avec cela est que la mise en page peut bégayer à la fin, car le cadre de la vue est dimensionné en fonction de la police d'origine jusqu'à la fin de l'animation, moment auquel le cadre est mis à jour pour accueillir la police cible sans animation.

La résolution de ce problème est un peu plus difficile car nous devons remplacer intrinsicContentSize. Vous pouvez le faire en sous-classant UILabel ou en accélérant la méthode. Personnellement, j'ai accéléré la méthode, car elle me permet de conserver une propriété générique fontSize disponible pour tous les UILabel, mais cela dépend du code de bibliothèque que je ne peux pas partager ici. Voici comment procéder en utilisant le sous-classement.

Interface:

@interface AnimatableLabel : UILabel

@property(nonatomic) CGFloat fontSize;

@end

La mise en oeuvre:

@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
1
lhunath

Pour ceux qui ne recherchent pas une transformation, mais un changement de valeur réel:

UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
    self.label.font = UIFont.systemFont(ofSize: 15)
}) { isFinished in }

quand il y a du texte, faites ceci:

UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
    self.label.font = UIFont.boldSystemFont(ofSize: 15)
}) { isFinished in }

(Gif montre une police différente)

enter image description here

1
SirRupertIII