You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:03:22