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

不使用transform捏合缩放UIView出现剧烈缩放偏移问题排查

问题分析与解决方案

你的代码核心问题出在对UIPinchGestureRecognizer的scale属性理解错误,以及没有正确记录初始缩放基准:

  • sender.scale是手势启动以来的总缩放比例(从1开始变化),不是每一次手势变化的增量比例。你每次用当前frame的宽高乘以这个scale,会导致缩放效果被重复累积——比如第一次scale到1.1,第二次到1.2,你的代码会计算成初始宽*1.1*1.2,而实际应该是初始宽*1.2,这就导致了轻微捏合就剧烈放大的问题。
  • 每次修改frame时先把origin设为(0,0)再重置center,逻辑冗余,还可能在复杂布局下引发位置计算偏差。

修正后的代码

首先在你的类中添加两个变量,用来保存手势启动时视图的初始尺寸:

private var initialViewWidth: CGFloat = 0
private var initialViewHeight: CGFloat = 0

然后修改捏合手势的处理方法:

@objc func didPinch(sender: UIPinchGestureRecognizer) {
    guard let subView = sender.view else { return }
    
    switch sender.state {
    case .began:
        // 手势启动时记录初始尺寸,作为缩放基准
        initialViewWidth = subView.frame.width
        initialViewHeight = subView.frame.height
    case .changed:
        // 基于初始尺寸和当前总缩放比例计算目标尺寸
        let targetWidth = initialViewWidth * sender.scale
        let targetHeight = initialViewHeight * sender.scale
        
        // 保持视图中心不变,计算新的frame
        let currentCenter = subView.center
        subView.frame = CGRect(
            x: currentCenter.x - targetWidth / 2,
            y: currentCenter.y - targetHeight / 2,
            width: targetWidth,
            height: targetHeight
        )
    default:
        break
    }
}

补充说明

  • 这种方式通过直接修改frame来缩放,视图的layer.borderWidth不会被缩放,完全符合你的需求。
  • 如果想要实现以捏合手势的触摸点为中心缩放(而非视图自身中心),可以在.began时记录触摸点相对于视图的位置,再在.changed时重新计算center,代码调整如下:
private var initialViewWidth: CGFloat = 0
private var initialViewHeight: CGFloat = 0
private var initialTouchPointInView: CGPoint = .zero

@objc func didPinch(sender: UIPinchGestureRecognizer) {
    guard let subView = sender.view else { return }
    
    switch sender.state {
    case .began:
        initialViewWidth = subView.frame.width
        initialViewHeight = subView.frame.height
        // 记录触摸点在视图坐标系中的位置
        initialTouchPointInView = sender.location(in: subView)
    case .changed:
        let targetWidth = initialViewWidth * sender.scale
        let targetHeight = initialViewHeight * sender.scale
        
        // 计算缩放后触摸点在父视图中的位置
        let touchPointInSuperview = sender.location(in: subView.superview)
        // 计算新的center:触摸点位置减去缩放后触摸点在视图内的偏移量
        let newCenterX = touchPointInSuperview.x - initialTouchPointInView.x * (targetWidth / initialViewWidth)
        let newCenterY = touchPointInSuperview.y - initialTouchPointInView.y * (targetHeight / initialViewHeight)
        
        subView.frame = CGRect(
            x: newCenterX - targetWidth / 2,
            y: newCenterY - targetHeight / 2,
            width: targetWidth,
            height: targetHeight
        )
    default:
        break
    }
}

内容的提问来源于stack exchange,提问作者XiOS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 16:24:31