如何带动画更新父视图约束且不丢失子视图约束
如何带动画更新父视图约束且不丢失子视图约束
嘿,我来帮你搞定这三个问题!你的代码里确实有几个小坑,咱们一步步来修正:
问题根源分析
- 约束冲突:你直接调用
blueView.removeConstraints(blueView.constraints),但这里的constraints不仅包含blueView自身的布局约束,还包括redView相对于blueView的约束——这就导致你误删了子视图的约束,同时旧的父视图约束可能还没完全失效,新约束加上后就冲突了。 - 子视图约束丢失:正是上面说的误操作,把redView绑定到blueView的约束删掉了,自然就丢了布局关系。
- 无动画:更新约束后没有触发动画上下文,系统只会直接生硬刷新布局,不会有过渡效果。
修正后的完整代码
下面是调整后的代码,我会逐段解释改动点:
class ViewController: NSViewController { // 1. 先把blueView的核心约束存为实例变量,方便后续修改 private var blueViewCenterXConstraint: NSLayoutConstraint! private var blueViewCenterYConstraint: NSLayoutConstraint! private var blueViewWidthConstraint: NSLayoutConstraint! private var blueViewHeightConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() let blueView: NSView = NSView() blueView.wantsLayer = true blueView.layer?.backgroundColor = NSColor.blue.cgColor self.view.addSubview(blueView) blueView.translatesAutoresizingMaskIntoConstraints = false // 2. 创建约束时直接保存引用,而不是直接activate后就不管了 blueViewCenterXConstraint = blueView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 50.0) blueViewCenterYConstraint = blueView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 50.0) blueViewWidthConstraint = blueView.widthAnchor.constraint(equalToConstant: 200.0) blueViewHeightConstraint = blueView.heightAnchor.constraint(equalToConstant: 200.0) // 批量激活约束 NSLayoutConstraint.activate([ blueViewCenterXConstraint, blueViewCenterYConstraint, blueViewWidthConstraint, blueViewHeightConstraint ]) let redView: NSView = NSView() redView.wantsLayer = true redView.layer?.backgroundColor = NSColor.red.cgColor blueView.addSubview(redView) redView.translatesAutoresizingMaskIntoConstraints = false // redView的约束正常创建,不用管,因为我们不会动blueView上属于子视图的约束 NSLayoutConstraint.activate([ redView.centerXAnchor.constraint(equalTo: blueView.centerXAnchor), redView.centerYAnchor.constraint(equalTo: blueView.centerYAnchor), redView.widthAnchor.constraint(equalToConstant: 100.0), redView.heightAnchor.constraint(equalToConstant: 100.0) ]) // 3. 延迟更新约束并添加动画 DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // 修改已有的约束参数,不用删除再重建 self.blueViewCenterXConstraint.constant = 0.0 self.blueViewCenterYConstraint.constant = 0.0 self.blueViewWidthConstraint.constant = 300.0 self.blueViewHeightConstraint.constant = 300.0 // 用macOS的动画上下文来实现平滑过渡 NSAnimationContext.runAnimationGroup({ context in context.duration = 0.5 // 动画时长,可根据需求调整 context.allowsImplicitAnimation = true self.view.layoutIfNeeded() // 触发布局刷新,带动画 }, completionHandler: nil) } } }
关键改动说明
- 保存约束引用:把blueView的核心约束存为实例变量,后续只需要修改约束的
constant值,不用删除再重建,从根源避免误删子视图约束。 - 放弃批量删除约束:不再调用
blueView.removeConstraints(blueView.constraints),这个方法会把blueView上所有约束(包括子视图的)都清空,这是你子视图丢失约束的核心原因。 - 添加动画上下文:使用
NSAnimationContext.runAnimationGroup包裹布局刷新,开启隐式动画,让约束更新的过程平滑过渡。你可以调整context.duration来控制动画快慢。
额外提示
如果以后你需要替换约束(而不是修改constant),正确的做法是:
- 先把旧约束设为
isActive = false - 创建并激活新约束
- 同样在动画上下文里调用
layoutIfNeeded()
这样就不会出现约束冲突,也不会影响子视图的布局啦!
备注:内容来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

