Using NSAnimationContext.runAnimationGroup(_:_:)
as demonstrated in the NSAnimationContext
documentation to animate both the frame origin and size works as expected for some view types (including NSImageView
). However, it does not work as expected for an NSButton
unless I add an explicit frame size change after the animation
Animating frame size for NSImageView
The following works as expected for an NSImageView. It is moved to the origin, and resized to 200x200:
NSAnimationContext.runAnimationGroup({(let context) -> Void in
context.duration = 2.0
// Get the animator for an NSImageView
let a = self.theImage.animator()
// Move and resize the NSImageView
a.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
}) {
print("Animation done")
}
Animating frame size for NSButton
When performing the same with an NSButton, the button will move but not resize:
NSAnimationContext.runAnimationGroup({(let context) -> Void in
context.duration = 2.0
// Get the animator for an NSButton
let a = self.button.animator()
// Move and resize the NSImageView
a.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
}) {
print("Animation done")
}
However, if I add the following line of code to the end, after all of the animation code, it works as expected!
self.button.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
The final, working listing for NSButton
is:
NSAnimationContext.runAnimationGroup({(let context) -> Void in
context.duration = 2.0
// Get the animator for an NSButton
let a = self.button.animator()
// Move and resize the NSImageView
a.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
}) {
print("Animation done")
}
self.button.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
I'm not looking a gift horse in the mouth here, but I don't understand why this is required for NSButton, or even what makes it work. Can anyone explain why explicitly setting the frame of the NSButton
after the animation code makes the animation work?
I suspect this has something to do with implicit autolayout constraints being generated at runtime. After modifying the frame, the autolayout simply reverts it back to the original size.
I abandoned the original approach in favor of the following:
NSLayoutConstraint
. This was tricky in IB: I had to double-click the constraint in the measurements tab of the inspector which then opened the constraint object in the inspector. Then you can select the connections tab and connect the outlet.NSViewController
subclass I use anchors to define the new height or width: theWidthConstraint.constant = 200
followed by self.view.needsUpdateConstraints = true
This approach is much cleaner and more compatible with the autolayout system. In addition, it makes it easy to animate the entire layout change that results from the new autolayout:
NSAnimationContext.runAnimationGroup({(let context) -> Void in
context.duration = 1.0
self.theWidthConstraint.animator().constant = 200
// Other constraint modifications can go here
}) {
print("Animation done")
}