Swift中如何将视图约束到已应用仿射变换的视图?
这个问题我太熟了!本质原因是Auto Layout和CGAffineTransform是完全独立的两个环节:Auto Layout负责计算视图的「原始布局frame」,而CGAffineTransform是在布局完成后,对视图的渲染结果做的变换,不会影响Auto Layout计算出的frame和anchors。所以你绑定的约束自然还是基于按钮变换前的状态。
下面给你两种可行的解决方案,看你需求选:
方案一:用Auto Layout代替transform(推荐)
既然你用了Auto Layout,最好全程遵循它的规则,不要混合transform,这样能避免很多奇怪的布局问题。你要实现的平移效果,完全可以通过修改按钮的约束constant值来实现:
// 假设你之前给addListButton绑定了leading和top约束,比如: // @IBOutlet weak var addListButtonLeading: NSLayoutConstraint! // @IBOutlet weak var addListButtonTop: NSLayoutConstraint! // 直接修改约束的constant来实现平移 addListButtonLeading.constant += 25 addListButtonTop.constant -= 80 // 触发布局更新(如果需要动画可以包在animate里) UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() }
这样修改后,按钮的frame是Auto Layout计算出来的,你的showHighlightView函数不需要做任何改动,约束会自动跟随按钮的新位置,完美适配。
方案二:如果必须用transform,手动同步highlightView的位置
如果你因为某些原因一定要用CGAffineTransform,那你需要手动获取按钮变换后的实际位置,然后更新highlightView的布局:
修改你的showHighlightView函数,替换原来的约束部分,改用基于变换后frame的布局:
func showHighlightView(viewToHighlight: UIView, height: CGFloat) { self.view.addSubview(highlightView) // 开启手动布局控制 highlightView.translatesAutoresizingMaskIntoConstraints = false // 获取按钮变换后在父视图中的实际显示区域 let transformedFrame = viewToHighlight.convert(viewToHighlight.bounds, to: self.view) // 设置highlightView的正方形尺寸 highlightView.widthAnchor.constraint(equalToConstant: height).isActive = true highlightView.heightAnchor.constraint(equalTo: highlightView.widthAnchor).isActive = true // 绑定中心到变换后的按钮中心 highlightView.centerXAnchor.constraint(equalToConstant: transformedFrame.midX).isActive = true highlightView.centerYAnchor.constraint(equalToConstant: transformedFrame.midY).isActive = true highlightView.layer.cornerRadius = height/2 highlightView.layer.add(self.scaleAnimation, forKey: "scale") self.view.bringSubviewToFront(viewToHighlight) }
这里的关键是viewToHighlight.convert(viewToHighlight.bounds, to: self.view),它能拿到按钮经过transform变换后,在父视图中的实际显示区域,然后我们基于这个区域的中心来设置highlightView的约束。
额外提醒
尽量避免Auto Layout和CGAffineTransform混合使用,除非是做临时的动画效果(比如缩放、旋转),如果是持久的位置变化,用Auto Layout修改约束是更可靠的方式,能避免很多布局冲突和不一致的问题。
内容的提问来源于stack exchange,提问作者Chris

