动画UILabel字体大小更改


70

我目前正在制作一个使用自定义View Controller容器的应用程序。一次在屏幕上显示多个视图,而当一个视图被点击时,选定的视图控制器会设置为全屏动画。这样做时,选定的视图控制器子视图也会缩放(帧,字体大小等)。尽管如此,UILabel的font属性无法设置动画,从而导致问题。我已经尝试了多种解决方案,但全部失败了。

我尝试过的解决方案是:

  1. 截取较大视图的屏幕截图并为更改添加动画效果(类似于Flipboard的操作)
  2. 通过使用transform属性进行动画处理
  3. 缩小UIScrollView并在全屏显示时将其放大。
  4. 设置将adjustsFontSizeToFitWidth设置为YES,并在动画之前设置fontSize

到目前为止,这是最好的解决方案,但我对此并不满意。

我正在寻找其他建议,如果有人可以使用[UIView animate ..]平滑制作动画或使用UILabel替换动画。

这是一个很好的示例,类似于我希望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];

}];

Answers:


64

您可以UILabel使用以下动画来更改动画的大小和字体,例如波纹管..在这里,我仅举一个如何UILabel使用transform 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);
    }];

希望对您有帮助。


我已经试过了。似乎转换是从中心点开始的。当我为放大效果设置动画时,显示的速率似乎与帧变化的速率不同。我可能没有多大意义,但动画不会以UIImageViews缩放的相同方式复制。
morcutt 2013年

我用上面的代码做同样的事情,但我只是改变了标签和UIImageView的大小...你到底想要什么?当您使用上面的代码时会出什么问题?
Paras Joshi

@morcutt也是,如果您想使用标签大小更改字体大小。.表示使用标签大小进行调整,然后使用uilable的AdjustsFontSizeToFitWidth属性,更多信息请参见此链接stackoverflow.com/questions/10475513/uilabel-font-size
Paras乔希(Joshi)2013年

我也尝试过AdjustsFontSizeToFitWidth。发生动画,但是在动画过程中该位置始终处于关闭状态。
morcutt

4
此方法的唯一问题是CGAffineTransform不考虑字体跟踪。因此,如果尝试缩放UILabel使用San Francisco字体的字体,则缩放后的标签将具有错误的跟踪值。
滑块

42

从2017年开始...

雨燕3.0,4.0

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

危急:

避免模​​糊的关键点是必须从最大尺寸开始,然后缩小尺寸。然后在需要时扩展为“ 1”。

对于快速的“弹出”(如高亮动画),可以扩展到1以上,但是如果要在两种尺寸之间切换,请将较大的尺寸设为“正确”的正常尺寸


唯一的办法!
Fattie

1
不幸的是,如果您需要放大一个很小的数量,这会导致字体变得模糊。
亚历克斯·斯坎兰'18

@AlexScanlan,但是您可以在动画结束后更改fontSize,因此它不会模糊:)
Freeubi

2
啊,@ AlexScanlan是 从大尺寸开始,您将需要的最大尺寸,然后将其缩小为较小尺寸重要
Fattie

27

我已经UILabel在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()
        }
    }
}

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


如果标签正确对齐怎么办?我遇到的问题是我有一个正确对齐的标签,当我使用此代码时,标签向右偏移并且不再对齐。
JideO

@JideO更新了答案。
mixel

完美的作品。谢谢!
巴兰·埃姆雷

这是唯一对我有效的方法。谢谢!
维克多·卡玛戈

15

您也可以使用具有fontSize作为动画属性的CATextLayer。

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从中心缩放标签),根据您的需要赞成或反对。CATextLayer的一个缺点是CALayers无法与自动布局约束动画一起使用(我碰巧需要并通过制作仅包含CATextLayer的UIView并对其约束进行动画处理来解决该问题)。


1
@ShubhamNaik我猜你没有在上面我的答案的“ // animation:”部分下实现第二行? 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. 重置为fromValue是任何CABasicAnimation的行为,幸运的是,此解决方案对于任何CABasicAnimation也是通用的
Yian Qiu

要使动画保持其最终状态,您还可以执行以下操作: fontSizeAnimation.isRemovedOnCompletion = false fontSizeAnimation.fillMode = CAMediaTimingFillMode.forwards
Ota面具

8

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

我创建了一个扩展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
    }
  }

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

迅速

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,并缩放更大。只需致电

迅速

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

目标-C

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

7

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

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

在此处输入图片说明


5

Swift 3.0和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      

    }

1

由于以下原因,我发现这里的每个建议都不足够:

  1. 它们实际上并没有更改字体大小。
  2. 它们在框架大小调整和自动布局方面效果不佳。
  3. 他们的界面很简单,并且/或者在动画块中的播放效果不佳。

为了保留所有这些功能并仍然获得平滑的动画过渡,我将转换方法和字体方法结合在一起。

界面很简单。只需更新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

3
感谢您的回复。记得是2018年,您能提供答案的Swift版本吗?谢谢!
加百利

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)
  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.