ChatGPT解决这个技术问题 Extra ChatGPT

动画 UILabel 字体大小更改

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

我尝试过的解决方案是:

截取较大视图的屏幕截图并为更改设置动画(类似于 Flipboard 所做的),使用 transform 属性设置动画 缩小 UIScrollView 并在进入全屏时将其放大。将 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];

}];

N
Nimantha

您可以使用如下动画更改 UILabel 的大小和字体 .. 这里我只是举例说明如何使用 transform Animation 更改 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);
    }];

我试过这个。似乎变换是从中心点发生的。当我为放大效果设置动画时,它的显示速度似乎与帧变化不同。我可能没有多大意义,但动画不会以与 UIImageViews 的缩放相同的方式复制。
我用上面的代码做同样的事情,但我只是改变标签和 UIImageView 的大小......你到底想要什么?当你使用上面的代码时有什么问题?
@morcutt 如果你想用你的标签大小改变字体大小..意味着用你的标签大小调整然后使用 uilable 的 adjustsFontSizeToFitWidth 属性,更多信息请参阅此链接 stackoverflow.com/questions/10475513/uilabel-font-size
我也尝试过adjustsFontSizeToFitWidth。动画发生,但在动画期间位置始终关闭。
此方法的唯一问题是 CGAffineTransform 不考虑字体跟踪。因此,如果尝试使用 San Francisco 字体缩放 UILabel,则缩放后的标签将具有不正确的跟踪值。
C
Community

从 2017 年开始......

斯威夫特 3.0、4.0

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

批判的:

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

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


唯一的出路!
不幸的是,如果您需要将字体放大不止一点,这会导致字体变得模糊。
@AlexScanlan 但您可以在动画完成后更改字体大小,所以它不会模糊:)
啊,@AlexScanlan 必须从较大的尺寸开始,您需要的最大尺寸,然后将其缩小到较小的尺寸。
m
mixel

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

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


如果标签右对齐怎么办?我遇到的问题是我有一个右对齐的标签,当我使用此代码时,标签向右移动并且不再对齐。
@JideO 更新了答案。
完美运行。谢谢!
这是唯一真正对我有用的东西。谢谢!
Y
Yinan Qiu

您还可以使用具有 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 来解决它)。


@ShubhamNaik 我猜你没有在我上面的答案中的“//动画:”部分下实现第二行? 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
要使动画保持最终状态,您还可以执行以下操作:fontSizeAnimation.isRemovedOnCompletion = false fontSizeAnimation.fillMode = CAMediaTimingFillMode.forwards
S
SirRupertIII

对于那些不寻找转换但实际值变化的人:

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

https://i.stack.imgur.com/UDLhF.gif


这正是我一直在寻找的。这是在不转换标签的情况下为字体设置动画的正确方法。谢谢!
C
Community

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

我为 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 的动画,从中心持续 1 秒并缩放更大。只需调用

迅速

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

目标-C

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

S
Sam Bing

斯威夫特 3.0 和斯威夫特 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      

    }

l
lhunath

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

他们实际上并没有改变字体大小。他们不能很好地调整框架大小和自动布局。他们的界面很重要和/或在动画块中播放效果不佳。

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

界面很简单。只需更新 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 或调整方法来做到这一点。我个人调整了该方法,因为它让我可以为所有 UILabel 保留一个通用 fontSize 属性,但这取决于我无法在此处共享的一些库代码。以下是使用子类化的方法。

界面:

@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

感谢您的回复。记得现在是 2018 年,你能提供一个 Swift 版本的答案吗?谢谢!
a
appsunited

如果你想从另一个锚点动画文本大小,这里是 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)
  }
}