UIScrollView嵌套UIImageView调用绘图方法后视图收缩问题咨询
看起来你遇到的问题大概率和绘图时没有正确处理UIImageView的缩放/变换状态,以及重新生成图片时没有保留当前视图尺寸有关。我来帮你拆解可能的原因和对应的解决办法:
1. 绘图时未转换坐标,忽略了ScrollView的缩放与偏移
当用户捏合缩放后,UIImageView的实际显示坐标和屏幕触摸坐标已经不对齐了,如果你的drawLineFrom直接用原始触摸点绘图,不仅会导致线条位置偏移,还可能因为坐标超出图片范围,间接触发UIImageView的尺寸收缩。
修复方案:
先把触摸点转换为UIImageView的本地坐标,同时考虑ScrollView的缩放比例:
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) { // 获取ScrollView当前的缩放比例 let zoomScale = scrollView.zoomScale // 将屏幕触摸点转换为UIImageView的本地坐标(消除ScrollView的偏移和缩放影响) let convertedFrom = imageView.convert(fromPoint, from: self.view) let convertedTo = imageView.convert(toPoint, from: self.view) // 基于UIImageView当前的bounds创建绘图上下文,而不是图片原始尺寸 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, UIScreen.main.scale) imageView.image?.draw(in: imageView.bounds) // 设置绘图样式 let context = UIGraphicsGetCurrentContext() context?.setStrokeColor(UIColor.black.cgColor) context?.setLineWidth(2.0) context?.setLineCap(.round) // 用转换后的坐标绘制线条 context?.move(to: convertedFrom) context?.addLine(to: convertedTo) context?.strokePath() imageView.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() }
2. 绘图时错误使用了图片原始尺寸创建上下文
如果你的代码里用imageView.image!.size来创建绘图上下文,而不是UIImageView当前的bounds.size,就会导致生成的新图片强制把ImageView拉回原始图片尺寸,出现收缩现象。
修复方案:
务必保证绘图上下文的尺寸和UIImageView的显示尺寸一致:
// ❌ 错误:用图片原始尺寸,会让ImageView收缩 // UIGraphicsBeginImageContextWithOptions(imageView.image!.size, false, UIScreen.main.scale) // ✅ 正确:用ImageView当前的bounds尺寸 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, UIScreen.main.scale)
3. 双指平移手势干扰了单指绘图的坐标追踪
你设置了平移手势需要双指触发,但在单指绘图时,ScrollView的contentOffset可能没有被正确锁定,导致触摸事件的坐标传递出现延迟,进而丢失绘图起始点。
修复方案:
在绘图期间暂时禁用ScrollView的平移手势,结束后再恢复:
// 在touchesBegan时禁用平移手势 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) scrollView.panGestureRecognizer.isEnabled = false } // 在touchesEnded时重新启用 override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) scrollView.panGestureRecognizer.isEnabled = true }
同时检查手势代理,确保绘图手势的优先级高于ScrollView的手势:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { // 绘图手势优先,避免和ScrollView手势冲突 if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer == scrollView.panGestureRecognizer { return false } return true }
4. 进阶优化:用CAShapeLayer代替直接修改UIImage
频繁重新生成UIImage不仅容易出尺寸问题,还会影响性能。可以在UIImageView上方加一个CAShapeLayer临时绘制线条,需要保存时再合并到图片上:
// 初始化时添加ShapeLayer private let drawLayer = CAShapeLayer() override func viewDidLoad() { super.viewDidLoad() drawLayer.strokeColor = UIColor.black.cgColor drawLayer.lineWidth = 2.0 drawLayer.lineCap = .round imageView.layer.addSublayer(drawLayer) } // 绘图时只更新Layer的路径 func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) { let convertedFrom = imageView.convert(fromPoint, from: view) let convertedTo = imageView.convert(toPoint, from: view) let path = UIBezierPath() path.move(to: convertedFrom) path.addLine(to: convertedTo) drawLayer.path = path.cgPath } // 需要保存到图片时再合并 func saveDrawingToImage() { UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, UIScreen.main.scale) imageView.layer.render(in: UIGraphicsGetCurrentContext()!) imageView.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() drawLayer.path = nil // 清空临时图层 }
以上几个方向应该能解决你遇到的ImageView收缩和起始点丢失的问题,你可以根据自己的代码情况逐一排查调整。
内容的提问来源于stack exchange,提问作者John Doe

