问题
I have a basic Mac app with a view animation done through Auto Layout:
- I add a new view to the right of the current view
- I update the constraints so that the new view ends up filling the window
→ The animation will make it appear as if the view slides in from the right.
The recommended way for animating Auto Layout changes is:
- Update the constraints
- Use
NSAnimationContext.runAnimationGroup()
- Set
allowsImplicitAnimation
totrue
inside the animation block - Call
view.layoutSubtreeIfNeeded()
inside the animation block
I followed this recommendation and everything worked fine on macOS Sierra, but on macOS High Sierra, the animation does not take place anymore. Instead the view shows up at its final position without the animation.
I found a workaround: I schedule the animation on the next runloop cycle using DispatchQueue.main.async
. However, that seems like a hack and I'm wondering if there is something else I'm missing here.
Here is my actual code:
private func appendSlideViewControllerAnimated(_ viewController:NSViewController, to viewToTheLeft:NSView)
{
// Insert the new view on the very right, just outside the parent:
viewController.view.frame = self.view.bounds
viewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(viewController.view)
viewController.view.topAnchor.constraint( equalTo: view.topAnchor ).isActive = true
viewController.view.bottomAnchor.constraint( equalTo: view.bottomAnchor ).isActive = true
viewController.view.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
viewController.view.leadingAnchor.constraint(equalTo: viewToTheLeft.trailingAnchor).isActive = true
// Update the layout after we just added the view on the right:
view.layoutSubtreeIfNeeded()
// Starting with macOS High Sierra, animating constraint changes for the newly inserted view
// only works if scheduled on the next runloop:
//DispatchQueue.main.async {
// Update the constraints to pin the view to the left:
self.view.removeConstraint(self.activeSlideLeadingConstraint!)
self.activeSlideLeadingConstraint = viewController.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
self.activeSlideLeadingConstraint?.constant = 0
self.activeSlideLeadingConstraint?.isActive = true
NSAnimationContext.runAnimationGroup( { context in
self.isAnimating = true
context.duration = self.slidingAnimationDuration
context.allowsImplicitAnimation = true
self.view.layoutSubtreeIfNeeded()
}, completionHandler: {
viewToTheLeft.removeFromSuperview()
self.clearUndoHistory()
self.updateFirstResponder()
self.isAnimating = false
})
//}
}
回答1:
Enable Core Animation backing for root view you trying to animate. It can be done in Interface Builder or programmatically:
override func viewDidLoad()
{
super.viewDidLoad()
view.wantsLayer = true
}
来源:https://stackoverflow.com/questions/47283066/animating-auto-layout-constraints-with-nsview-layoutsubtreeifneeded-not-workin