UIPanGestureRecognizer拖动后ImageView回弹:如何固定已放置的图片?
解决UIPanGestureRecognizer拖动后已固定图片回弹的问题
我一眼就看出问题出在哪了——你用Auto Layout约束定义了图片的初始位置,但拖动固定时直接修改了center属性,这和原约束产生了冲突!当你拖动第二张图时,系统触发布局更新,Auto Layout就会强制把第一张图拉回约束规定的初始位置,导致回弹。
下面是修复方案,核心是用约束来持久固定图片位置,而不是直接修改frame/center:
第一步:保存初始约束
先给你的视图控制器添加两个属性,用来保存image1和image2的初始布局约束:
private var image1Constraints: [NSLayoutConstraint] = [] private var image2Constraints: [NSLayoutConstraint] = []
然后在viewDidLoad里把初始约束存起来:
override func viewDidLoad() { super.viewDidLoad() target1flg = true target2flg = true // 保存图片的初始约束(水平+垂直方向) image1Constraints = image1.constraintsAffectingLayout(for: .horizontal) + image1.constraintsAffectingLayout(for: .vertical) image2Constraints = image2.constraintsAffectingLayout(for: .horizontal) + image2.constraintsAffectingLayout(for: .vertical) addPanGesture(v: image1) addPanGesture(v: image2) }
第二步:修改固定图片的逻辑
原来的snapView方法直接改center,现在改成移除原约束,添加新约束让图片和目标视图完全对齐:
func snapView(v:UIView, targetBox:UIImageView){ // 移除当前图片的初始约束 if v == image1 { NSLayoutConstraint.deactivate(image1Constraints) } else if v == image2 { NSLayoutConstraint.deactivate(image2Constraints) } // 开启自动布局,添加新的居中约束 v.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ v.centerXAnchor.constraint(equalTo: targetBox.centerXAnchor), v.centerYAnchor.constraint(equalTo: targetBox.centerYAnchor), v.widthAnchor.constraint(equalTo: targetBox.widthAnchor), v.heightAnchor.constraint(equalTo: targetBox.heightAnchor) ]) // 动画更新布局并禁用交互 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() // 强制刷新布局,应用新约束 v.isUserInteractionEnabled = false } }
第三步:修改返回初始位置的逻辑
同样用约束来控制返回,而不是直接改frame:
func returnViewToOrigin(v: UIView) { // 重新激活初始约束 if v == image1 { NSLayoutConstraint.activate(image1Constraints) } else if v == image2 { NSLayoutConstraint.activate(image2Constraints) } // 动画回到初始位置 UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } }
第四步:更新手势处理逻辑
把handlePan里的.ended分支修改一下,移除原来的imageViewOrigin相关代码,调用新的返回方法:
@objc func handlePan(sender: UIPanGestureRecognizer) { let pannedImageView = sender.view! switch sender.state { case .began: view.bringSubview(toFront: pannedImageView) // 不需要保存origin了 case .changed: moveViewWithPan(v: pannedImageView, sender: sender) case .ended: if pannedImageView.frame.intersects(target1.frame) && target1flg == true { snapView(v: pannedImageView, targetBox: target1) target1flg = false } else if pannedImageView.frame.intersects(target2.frame) && target2flg == true { snapView(v: pannedImageView, targetBox: target2) target2flg = false } else { returnViewToOrigin(v: pannedImageView) } default: break } }
最后:清理冗余代码
把原来的imageViewOrigin属性删掉,现在用不到它了。
这样修改后,图片固定到目标位置后是用约束来维持的,不会再和初始约束冲突,拖动第二张图时第一张图也不会回弹了。
内容的提问来源于stack exchange,提问作者Miki S
相关产品推荐
相关产品推荐

