如何通过平移手势限制UITextView仅在指定UIView范围内拖动?
限制UITextView拖动范围到指定View的Frame内
我看你之前的代码没处理好坐标系和边界校验的问题,导致UITextView能拖出canvas范围。下面是修正后的完整实现,我会一步步给你解释关键要点:
@IBOutlet weak var canvas: UIView! @IBAction func handleDragOfCaption(_ sender: UIPanGestureRecognizer) { guard let senderView = sender.view else { return } switch sender.state { case .began: // 拖动开始时把TextView放到最上层,避免被其他视图遮挡 senderView.layer.zPosition = 100 case .changed: // 关键:以canvas作为位移计算的坐标系,这样位移是相对于父容器的 let translation = sender.translation(in: canvas) // 计算拖动后的新中心位置 var newCenter = CGPoint( x: senderView.center.x + translation.x, y: senderView.center.y + translation.y ) // 计算canvas内的可拖动边界:要考虑TextView自身的宽高,避免边缘超出canvas let halfWidth = senderView.bounds.width / 2 let halfHeight = senderView.bounds.height / 2 let minX = canvas.bounds.minX + halfWidth let maxX = canvas.bounds.maxX - halfWidth let minY = canvas.bounds.minY + halfHeight let maxY = canvas.bounds.maxY - halfHeight // 把新中心限制在计算好的边界内 newCenter.x = max(minX, min(newCenter.x, maxX)) newCenter.y = max(minY, min(newCenter.y, maxY)) // 更新TextView的位置 senderView.center = newCenter // 重置手势的位移,否则下次拖动会累加之前的位移,导致拖动速度异常 sender.setTranslation(.zero, in: canvas) case .ended, .cancelled: // 拖动结束后恢复原层级 senderView.layer.zPosition = 0 default: break } }
关键修正点说明:
- 坐标系切换:之前用
sender.translation(in: sender.view)是错误的,改用canvas作为坐标系后,位移计算是相对于父容器的,更准确。 - 边界校验逻辑:不能只限制在canvas的bounds内,必须加上TextView自身的半宽半高,这样TextView的边缘才不会超出canvas的范围。
- 位移重置:每次更新位置后重置translation为零,避免手势位移累加,保证拖动的流畅性。
- 层级处理:拖动时提升TextView的层级,防止被其他视图挡住,结束后恢复正常。
额外注意事项:
- 确保你的UITextView已经开启了
userInteractionEnabled,并且正确添加了PanGestureRecognizer。 - 如果canvas是UIScrollView这类可滚动视图,需要处理手势冲突(比如设置
cancelsTouchesInView = false)。 - 如果UITextView的大小会动态变化(比如自动调整高度),要确保每次计算边界时用的是实时的
bounds.size。
内容的提问来源于stack exchange,提问作者itsfaraaz
相关产品推荐
相关产品推荐

