Анимация изменения размера шрифта UILabel - программирование
Подтвердить что ты не робот

Анимация изменения размера шрифта 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];

}];
4b9b3361

Ответ 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

Я нашел каждое из предложений здесь неадекватным по следующим причинам:

  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