动画 UILabel 字体大小变化

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

我尝试过的解决办法是:

  1. 获取大视图的屏幕快照并将更改动画化(类似于 Flipboard 的做法)
  2. 使用转换属性进行动画创建
  3. 放大 UIScrollView 并在全屏显示时将其放大。
  4. 在动画制作之前设置调整 FontSizeToFitWidth 为 YES 并设置 fontSize

一个是迄今为止最好的解决方案,但我对它并不满意。

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

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


}];
50036 次浏览

您可以改变大小和字体的 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);
}];

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

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

您还可以使用 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 从中间缩放标签) ,赞成或反对取决于您的需要。CATextLayer 的一个缺点是 CALayers 不能处理自动布局约束动画(我碰巧需要并通过制作一个仅包含 CATextLayer 的 UIView 并对其约束进行动画化来解决这个问题)。

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


}

2017年以后。

Swift 3.04.0

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

严重:

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

对于快速“弹出”(像一个突出的动画) ,它的确可以扩展超过1,但如果你是在两个大小之间转换,使较大的尺寸成为“正确”的正常尺寸

对于那些想要调整动画方向的人来说

我已经为 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];

由于以下原因,我发现这里的每一条建议都不够充分:

  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

对于那些不寻求转换,而是寻求实际价值变化的人:

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

enter image description here

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