Анимация изменения размера шрифта UILabel
В настоящее время я создаю приложение, которое использует специальный контейнер View Controller. Несколько видов одновременно отображаются на экране, и когда один из них используется, выбранный контроллер просмотра анимируется в полноэкранном режиме. При этом выбранные контроллеры представлений также рассматривают масштаб (кадр, размер шрифта и т.д.). Хотя свойство шрифта UILabel не является анимированным, что приводит к проблемам. Я попробовал несколько решений, но все плоские сосать.
Решения, которые я пробовал, следующие:
- Сделайте снимок экрана большего размера и анимируйте изменения (подобно тому, как работает Flipboard)
- Анимация с использованием свойства transform
- Уменьшение UIScrollView и масштабирование его при вводе на полный экран.
- Настройка настраиваетFontSizeToFitWidth на YES и устанавливает fontSize перед анимацией
Один из них был лучшим решением, но я не удовлетворен этим.
Я ищу другие предложения, если у кого-либо есть или UILabel-подстановка, которая анимирует плавно, используя [UIView animate..].
Вот хороший пример, который похож на то, что я хотел бы, чтобы мой UILabel сделал:
http://www.cocoawithlove.com/2010/09/zoomingviewcontroller-to-animate-uiview.html
EDIT: этот код работает
// 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];
}];
Ответы
Ответ 1
Вы можете изменить размер и шрифт вашего UILabel
с помощью анимации, как показано ниже. Здесь я просто приведу пример того, как изменить шрифт UILabel
с помощью преобразования 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);
}];
Я надеюсь, что это вам поможет..
Ответ 2
Начиная с 2017 года....
Swift 3.0, 4.0
UIView.animate(withDuration: 0.5) {
label.transform = CGAffineTransform(scaleX: 1.1, y: 1.1) //Scale label area
}
Critical:
Критическая точка, чтобы избежать размытия, - вы должны начать с самого большого размера и уменьшить его. Затем расширьте до "1", когда это необходимо.
Для быстрых "всплывающих окон" (например, анимации выделения) можно расширить за пределы 1, но если вы переходите между двумя размерами, сделайте больший размер "правильным" нормальным.
Ответ 3
Я создал расширение 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()
}
}
}
Разомните строки, если текст ярлыка выровнен влево или вправо.
Ответ 4
Вы также можете использовать 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 не работают с анимацией ограничений автоопределения (что мне было необходимо и решило, сделав UIView, содержащий только CATextLayer и анимацию его ограничений).
Ответ 5
Для тех, кто хочет настроить направление анимации
Я создал расширение для 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
}
}
}
Objective-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)
Objective-C
[YOUR_LABEL animateWithFontSize:30
duration:1
animateAnchorPoint:kCenterXCenterYAnchorPoint];
Ответ 6
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
}
Ответ 7
Для тех, кто не ищет преобразования, но меняет действительное значение:
UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.label.font = UIFont.systemFont(ofSize: 15)
}) { isFinished in }
![enter image description here]()
Ответ 8
Я нашел каждое из предложений здесь неадекватным по следующим причинам:
- Они на самом деле не меняют размер шрифта.
- Они не очень хорошо подходят для определения размера кадра и автоматическая разметка.
- Их интерфейс нетривиален и/или плохо воспроизводится внутри блоков анимации.
Для того, чтобы сохранить все эти функции & все еще получаю плавный переход анимации. Я объединил подходы преобразования и шрифта.
Интерфейс прост. Просто обновите свойство 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