Animate sizeToFit

I change the text of a label dynamically (so I do not know the text of the label) how can I animate the size to fit. I am looking for a smooth, elegant transition for design purposes.

What I have tried:

        NSAnimationContext.runAnimationGroup({ (context) in
            context.duration = 4.0
            label1.stringValue = "long, long text"
            label1.animator().sizeToFit()
        })

Replies

Could you detail what you get and what you expect ?

AFAIU, animation works by layering an image "capture" over the view to animate and then apply the animation on this layer. Thus, I don't think it will change the font size, but scale the image. I don't think sizeToFit is appropriate here.

May have a look at using transform:

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

  • I get: When I change the text of a label, that label, and all the stack change abruptly.

    I expect: When I change the text of a label, that label and all the stack changes the width slowly (in my example, in 4 seconds). (If I knew what text the user put I could change the width of the elements? But I cannot know that).

    On the other side, I am in macOS. I tried transform to see what happens but it says: Value of type NSTextField does not have transform. In any case, I do not know the size of the label. So, I do not think transform is the way to go?

Add a Comment

So I understand you want to animate (through NSAnimation), the change of the label's text in a MacOS app ?

And the change occurs only abruptly (probably after the 4 seconds) ?

It would help to see or reproduce exactly what you get. Could you post the complete code the the class ?

I want to animate (using any method) the change of the label's text in a macOS app. With the code I provide, the change occurs when I click the button. I see no animation and no delay.

import Cocoa
class ViewController: NSViewController {
    @IBOutlet weak var label1: NSTextField!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func button2(_ sender: NSButton) {
        NSAnimationContext.runAnimationGroup({ (context) in
            context.duration = 4.0
            label1.stringValue = "long, long text"
            label1.animator().sizeToFit()
        })
    }
}

What I understand from what you show:

  • the change of text should be done in the completion handler of the animation
  • but it will not be possible to animate the sizeToFit
  • stated otherwise, the change from "oldText" to "long, long text" cannot be animated the way you want ; the animation is an image "morphing" animation, not a text substitution.

In your case, as sizeToFit is not a property for animation, the delay is not applied !

Test the following and see difference: prints after 4 seconds now.

    @IBAction func animate2LabelAction(_ sender: NSButton) {
        self.labelForAnimation.stringValue = "short text"
        NSAnimationContext.runAnimationGroup({ (context) in
            context.duration = 4.0
//            labelForAnimation.animator().sizeToFit()
            labelForAnimation.animator().alphaValue = 0.5  // This is animatable
        }, completionHandler: {
            print("Completion") } )
 
    }

Or add label change in completion:

    @IBAction func animate2LabelAction(_ sender: NSButton) {
        self.labelForAnimation.stringValue = "short text"
        NSAnimationContext.runAnimationGroup({ (context) in
            context.duration = 4.0
            //            labelForAnimation.animator().sizeToFit()
            labelForAnimation.animator().alphaValue = 0.5
        }, completionHandler: {
            self.labelForAnimation.stringValue = "long, long text"
            print("Completion") } )
 
    }
Add a Comment

You can probably try something to get an approaching result.

  • In the IBAction, create a hidden TextField, with same font as label1.
  • Compute initialWidth, the original width of final text in this textField.

https://stackoverflow.com/questions/19128797/calculating-uilabel-text-size

  • If initialWidth is larger than label1Width (label1), then apply the animation, with the following
  • compute let scaleRatio = initialWidth / label1Width
  • in the animation, apply a transform with this scale factor
  • in the completion handler, set the finalText in label1 and sizeToFit()