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

缩放UITextView时触摸事件位置异常的技术咨询

问题分析与解决方案

你遇到的是UITextView使用zoomScale缩放后触摸事件坐标不匹配的问题,本质是UITextView的触摸处理逻辑未完全适配UIScrollView的缩放特性——官方文档并未明确支持UITextView通过zoomScale实现文本缩放,因此这属于适配不足而非严格意义上的系统bug。

核心原因

当zoomScale > 1时,UITextView的滚动视图层级会对坐标进行缩放,但文本布局系统仍基于原始未缩放的坐标系计算光标位置和文本选择范围,导致触摸点(物理坐标)与文本逻辑坐标出现偏移,且文本越长、偏移量越大。

解决方案

1. 优先使用字体缩放替代zoomScale

如果仅需放大文本内容,直接调整字体大小是最稳妥的方案,完全规避坐标转换问题:

// 按比例放大字体
textView.font = UIFont.systemFont(ofSize: textView.font!.pointSize * 1.3)

2. 自定义UITextView修正触摸坐标转换

若必须使用zoomScale,可通过自定义UITextView重写触摸事件,手动将触摸点转换为适配缩放比例的逻辑坐标:

class FixedZoomTextView: UITextView {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        handleTouch(touches, event: event, isBeginning: true)
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        handleTouch(touches, event: event, isBeginning: false)
    }

    private func handleTouch(_ touches: Set<UITouch>, event: UIEvent?, isBeginning: Bool) {
        guard let touch = touches.first, zoomScale != 1.0 else {
            super.touchesBegan(touches, with: event)
            return
        }
        
        // 将触摸点转换到未缩放的文本逻辑坐标系
        let scaledPoint = CGPoint(
            x: touch.location(in: self).x / zoomScale,
            y: touch.location(in: self).y / zoomScale
        )
        
        guard let targetPosition = closestPosition(to: scaledPoint) else {
            super.touchesBegan(touches, with: event)
            return
        }
        
        if isBeginning {
            // 初始点击定位光标
            selectedTextRange = textRange(from: targetPosition, to: targetPosition)
        } else {
            // 拖动扩展文本选择范围
            if let startPosition = selectedTextRange?.start {
                selectedTextRange = textRange(from: startPosition, to: targetPosition)
            }
        }
    }

    // 修正滚动偏移问题
    override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
        let correctedOffset = CGPoint(
            x: contentOffset.x / zoomScale,
            y: contentOffset.y / zoomScale
        )
        super.setContentOffset(correctedOffset, animated: animated)
    }
}

3. 验证方法

在空白项目中替换默认UITextView为自定义类,设置缩放比例后测试:

let textView = FixedZoomTextView(frame: view.bounds)
textView.zoomScale = 1.3
textView.text = "你的长文本内容..."
view.addSubview(textView)

此方案可解决光标偏移、滚动抖动等问题,确保触摸位置与光标/选中文本的位置完全匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 08:53:26