developer tip

UILabel 글꼴 크기 변경 애니메이션

copycodes 2020. 11. 20. 09:05
반응형

UILabel 글꼴 크기 변경 애니메이션


현재 사용자 지정 View Controller 컨테이너를 사용하는 응용 프로그램을 만들고 있습니다. 한 번에 여러보기가 화면에 표시되고 하나를 탭하면 선택한보기 컨트롤러가 전체 화면으로 애니메이션됩니다. 이렇게하면 선택한 뷰 컨트롤러 하위 뷰도 크기가 조정됩니다 (프레임, 글꼴 크기 등).하지만 UILabel의 글꼴 속성은 애니메이션 할 수 없어 문제가 발생합니다. 나는 여러 솔루션을 시도했지만 모두 완전히 형편 없다.

내가 시도한 솔루션은 다음과 같습니다.

  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];

}];

다음 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);
    }];

도움이 되었기를 바랍니다 ..


2017 년 이후로 ....

스위프트 3.0, 4.0

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

위독한:

흐려짐을 방지하기위한 중요한 점은 가장 큰 크기로 시작하여 축소해야한다는 것입니다. 그런 다음 필요한 경우 "1"로 확장합니다.

빠른 "팝"(예 : 하이라이트 애니메이션)의 경우 1 이상으로 확장해도 좋지만 두 크기 사이를 전환 하는 경우 더 큰 크기를 "올바른"일반 크기로 만드십시오 .


UILabelSwift에서 확장 기능을 만들었습니다 .

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

레이블 텍스트가 왼쪽 또는 오른쪽 정렬 된 경우 줄의 주석 처리를 제거합니다.


애니메이션 가능한 속성으로 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를 만들고 제약 조건에 애니메이션을 적용하여 필요하고 해결했습니다).


애니메이션 방향을 조정하고 싶은 분

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];

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      

    }

변환을 찾지 않고 실제 값 변경을 찾는 사람들을 위해 :

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

여기에 이미지 설명 입력


다음과 같은 이유로 여기에서 각 제안이 부적절하다는 것을 알았습니다.

  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

Now, the problem with this is that the layout can stutter upon completion, because the view's frame is sized based on the original font all the way until the animation completion, at which point the frame updates to accommodate the target font without animation.

Fixing this problem is a little harder because we need to override intrinsicContentSize. You can do this either by subclassing UILabel or by swizzling the method. I personally swizzle the method, because it lets me keep a generic fontSize property available to all UILabels, but that depends on some library code I can't share here. Here is how you would go about this using subclassing.

Interface:

@interface AnimatableLabel : UILabel

@property(nonatomic) CGFloat fontSize;

@end

Implementation:

@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

참고URL : https://stackoverflow.com/questions/14494566/animating-uilabel-font-size-change

반응형