39

I want a UILabel to swell slightly when selected like in some game menu screens. To get smooth resizing I presume I should put some change to the label's properties in an animation block.

The obvious thing to try is to change the label.font.pointSize property but that's readonly.

Scaling the label's .transform property with CGAffineTransformationMakeScale() makes the text blurry.

Is there some other way to do this?

Hiren
  • 12,720
  • 7
  • 52
  • 72
willc2
  • 38,991
  • 25
  • 88
  • 99

5 Answers5

57

Set the font on the UILabel to be the size that you want when it is enlarged. Then scale it down. When you want the label to swell, scale it back up to it's original size.

    messageLabel.font = [UIFont boldSystemFontOfSize:45]; 
    messageLabel.transform = CGAffineTransformScale(messageLabel.transform, 0.25, 0.25); 
    [self.view addSubview:messageLabel]; 
    [UIView animateWithDuration:1.0 animations:^{
        messageLabel.transform = CGAffineTransformScale(messageLabel.transform, 4, 4);
    }];
Bart Whiteley
  • 1,426
  • 12
  • 10
  • @Vishal, to revert it I guess you can wait for completion (you can pass a callback block to get notified) and then start another animation. – Ferran Maylinch Mar 08 '16 at 14:11
  • This actually scales the label, which means you should set the initial font size to the be as large as possible and then scale down; if you scale up you'll see pixelated text. – BallpointBen Jun 26 '17 at 12:28
14

UPDATED for Xcode 8 / Swift 3 / iOS 10 SDK.

I've created UILabel extension in Swift. Uncomment commented lines if your label is left aligned.

import UIKit

extension UILabel {
    func animateToFont(_ font: UIFont, withDuration duration: TimeInterval) {
        let oldFont = self.font
        self.font = font
        // let oldOrigin = frame.origin
        let labelScale = oldFont!.pointSize / font.pointSize
        let oldTransform = transform
        transform = transform.scaledBy(x: labelScale, y: labelScale)
        // let newOrigin = frame.origin
        // frame.origin = oldOrigin
        setNeedsUpdateConstraints()
        UIView.animate(withDuration: duration) {
        //    self.frame.origin = newOrigin
            self.transform = oldTransform
            self.layoutIfNeeded()
        }
    }
}

Objective-C version:

@interface UILabel(FontAnimation)

- (void) animateToFont:(UIFont*)font withDuration:(NSTimeInterval) duration;

@end

@implementation UILabel(FontAnimation)

- (void) animateToFont:(UIFont*)font withDuration:(NSTimeInterval) duration {
    UIFont * oldFont = self.font;
    self.font = font;
    // CGPoint oldOrigin = self.frame.origin;
    CGFloat labelScale = oldFont.pointSize / font.pointSize;
    CGAffineTransform oldTransform = self.transform;
    self.transform = CGAffineTransformScale(self.transform, labelScale, labelScale);
    // CGPoint newOrigin = self.frame.origin;
    // self.frame = CGRectMake(oldOrigin.x, oldOrigin.y, self.frame.size.width, self.frame.size.height);
    [self setNeedsUpdateConstraints];
    [UIView animateWithDuration:duration animations: ^{
        // self.frame = CGRectMake(newOrigin.x, newOrigin.y, self.frame.size.width, self.frame.size.height);
        self.transform = oldTransform;
        [self layoutIfNeeded];
    }];
}

@end
mixel
  • 25,177
  • 13
  • 126
  • 165
5

If you want animating from fontSize to fontSize you can use CATextLayer class

// create text layer 
let textLayer = CATextLayer()
textLayer.font = UIFont.systemFontOfSize(50)
textLayer.fontSize = 50
textLayer.string = "text"
textLayer.foregroundColor = UIColor.redColor().CGColor
textLayer.backgroundColor = UIColor.blackColor().CGColor
textLayer.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
view.layer.addSublayer(textLayer)

// animation
let animation = CABasicAnimation(keyPath: "fontSize")
animation.toValue = 10
animation.duration = 3
textLayer.layer.addAnimation(animation, forKey: nil)
ober
  • 2,363
  • 1
  • 19
  • 17
2

I've just made a solution for this. It's based on CGAffineTransformScale too, but it also has some other tricks up its sleeve and handles all the border cases:

Check it out: https://github.com/ivankovacevic/ARLabel

Ivan Kovacevic
  • 1,322
  • 12
  • 30
-1

You can't change the point size of the font, but you can replace the UILabel's font with a new UIFont that uses the new point size you desire.

label.font = [UIFont fontWithName:@"Arial-BoldMT" size:12.0];
// ... some time later
label.font = [UIFont fontWithName:@"Arial-BoldMT" size:14.0];

You'll probably also want to modify the UILabel's frame to accommodate the new point size, especially if you're increasing the point size. Otherwise, your label will get cropped.

Jay
  • 807
  • 10
  • 9
  • 1
    Not on its own. Scale the UILabel using a transformation like you already mentioned and once the animation is complete, assign the larger UIFont to the UILabel. This should give you the same effect you see when you zoom in and out in mobile Safari. The text is slightly blurry during the pinch/zoom, but once the gesture is complete, the text is rendered using a different font size that matches the current zoom level. – Jay Oct 04 '10 at 13:22