动画UILabel字体大小更改

时间:2013-01-24 05:26:52

标签: iphone ios animation uilabel

我目前正在制作一个使用自定义View Controller容器的应用程序。屏幕上一次显示多个视图,当点击一个视图时,所选视图控制器将动画显示为全屏。在这样做时,所选视图控制器子视图也会缩放(帧,字体大小等)。但是,UILabel的字体属性不可动画导致问题。我尝试了多种解决方案,但都很糟糕。

我尝试的解决方案是:

  1. 截取较大视图的屏幕截图并设置更改动画(类似于Flipboard的工作方式)
  2. 使用转换属性
  3. 进行动画处理
  4. 缩小UIScrollView并在全屏显示时将其放大。
  5. 将adjustsFontSizeToFitWidth设置为YES并在动画之前设置fontSize
  6. 到目前为止,一个是最好的解决方案,但我对此并不满意。

    我正在寻找其他建议,如果有人有任何或UILabel替代品使用[UIView animate ..]平滑动画。

    这是一个很好的例子,类似于我想要的UILabel: http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html

    编辑:此代码有效

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

9 个答案:

答案 0 :(得分:53)

您可以使用下面的动画来更改UILabel的大小和字体。这里我只是举例说明如何使用变换动画更改UILabel的字体。

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

我希望这对你有帮助..

答案 1 :(得分:27)

2017年起......

Swift 3.0,4.0

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

答案 2 :(得分:21)

我在Swift中创建了UILabel扩展名。

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

如果标签文本左对齐或右对齐,则取消注释行。

答案 3 :(得分:12)

您也可以使用CATextLayer,它将fontSize作为可动画的属性。

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)

我在我的项目中使用它:https://github.com/yinanq/AngelListJobs

此动画可使字体左上对齐(与CGAffineTransformScale不同,从中心缩放标签),pro或con,具体取决于您的需要。 CATextLayer的一个缺点是CALayers无法使用自动布局约束动画(我碰巧需要并通过制作一个仅包含CATextLayer并动画其约束的UIView来解决它。)

答案 4 :(得分:7)

对于想要调整动画方向的人

我为UILabel创建了一个扩展程序来设置字体大小更改动画

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

如果要调整动画的方向,请使用以下方法并放置合适的锚点。

<强> SWIFT

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

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

例如,要将标签字体大小更改为30,持续时间为1s,从中心开始,缩放比例更大。只需致电

<强> SWIFT

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

<强>目的-C

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

答案 5 :(得分:4)

Swift 3.0&amp; 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      

    }

答案 6 :(得分:1)

对于那些不希望转换但实际值发生变化的人:

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

有文字时,请执行以下操作:

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

(Gif显示不同的字体)

enter image description here

答案 7 :(得分:1)

如果要从另一个锚点设置文本大小的动画,请使用 Swift 5 解决方案:

如何申请:

yourLabel.setAnimatedFont(.systemFont(ofSize: 48), duration: 0.2, anchorPointX: 0, anchorPointY: 1)

扩展名:

extension UILabel {
  /// Animate font size from a given anchor point of the label.
  /// - Parameters:
  ///   - duration: Animation measured in seconds
  ///   - anchorPointX: 0 = left, 0.5 = center, 1 = right
  ///   - anchorPointY: 0 = top, 0.5 = center, 1 = bottom
  func setAnimatedFont(_ font: UIFont, duration: TimeInterval, anchorPointX: CGFloat, anchorPointY: CGFloat) {
    guard let oldFont = self.font else { return }

    setAnchorPoint(CGPoint(x: anchorPointX, y: anchorPointY))
    self.font = font

    let scaleFactor = oldFont.pointSize / font.pointSize
    let oldTransform = transform
    transform = transform.scaledBy(x: scaleFactor, y: scaleFactor)
    setNeedsUpdateConstraints()

    UIView.animate(withDuration: duration) {
      self.transform = oldTransform
      self.layoutIfNeeded()
    }
  }
}

extension UIView {
  /// Change the anchor point without moving the view's position.
  /// - Parameters:
  ///   - point: The layer's bounds rectangle.
  func setAnchorPoint(_ point: CGPoint) {
    let oldOrigin = frame.origin
    layer.anchorPoint = point
    let newOrigin = frame.origin

    let translation = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)
    translatesAutoresizingMaskIntoConstraints = true
    center = CGPoint(x: center.x - translation.x, y: center.y - translation.y)
  }
}

答案 8 :(得分:0)

我发现这里的每一条建议都不合适,原因如下:

  1. 它们实际上并没有改变字体大小。
  2. 他们不适合使用框架尺寸和尺寸。自动布局。
  3. 他们的界面非常重要和/或在动画块中不能很好用。
  4. 为了保留所有这些功能&amp;我仍然得到了平滑的动画过渡,我将变换方法和字体方法相结合。

    界面很简单。只需更新fontSize属性,即可更新字体大小。在动画块中执行此操作并将其设置为动画。

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

    至于实施,有简单的方法,而且有更好的方法。

    简单:

    @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
    

    现在,问题在于布局在完成时可能会断断续续,因为视图的框架基于原始字体的大小一直到动画完成为止,此时框架会更新以适应目标字体而不需要动画

    修复此问题有点困难,因为我们需要覆盖intrinsicContentSize。您可以通过继承UILabel或通过调整方法来完成此操作。我个人调用了这个方法,因为它让我可以为所有fontSize保留一个通用UILabel属性,但这取决于我在这里无法分享的一些库代码。以下是使用子类化的方法。

    接口:

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

    实现:

    @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