旋转设备后保持AspectFit模式下UIView在图片上的位置不变
解决AspectFit模式下旋转设备时形状位置偏移的问题
这个问题我之前做图片标注工具的时候也踩过坑!核心原因是你把形状的位置直接绑定在了UIImageView的坐标系上,但AspectFit模式下,设备旋转会改变UIImageView的宽高比,导致图片在视图内的显示区域(也就是实际能看到图片的部分)发生偏移和缩放,形状自然就“跑位”了。
解决思路很清晰:把形状的位置锚定在图片本身的坐标系上,而不是UIImageView的可变坐标系。具体来说,就是将形状的frame转换为相对于图片的坐标比例,旋转后再根据新的UIImageView显示区域把比例转换回视图坐标系的frame。
步骤1:计算图片在UIImageView中的实际显示区域
首先需要一个方法,算出AspectFit模式下图片在UIImageView里的真实显示矩形(排除黑边部分),同时考虑UIScrollView的缩放因子:
func getImageContentRect(in imageView: UIImageView, scrollView: UIScrollView) -> CGRect { guard let image = imageView.image else { return .zero } // 先获取去除缩放后的ImageView实际尺寸 let unscaledViewSize = imageView.bounds.size.applying(CGAffineTransform(scaleX: 1/scrollView.zoomScale, y: 1/scrollView.zoomScale)) let imageSize = image.size // 计算AspectFit下的缩放比例 let widthRatio = unscaledViewSize.width / imageSize.width let heightRatio = unscaledViewSize.height / imageSize.height let fitScale = min(widthRatio, heightRatio) // 计算图片实际显示的宽高 let contentWidth = imageSize.width * fitScale let contentHeight = imageSize.height * fitScale // 计算居中后的位置,再还原缩放 let x = (unscaledViewSize.width - contentWidth) / 2 * scrollView.zoomScale let y = (unscaledViewSize.height - contentHeight) / 2 * scrollView.zoomScale return CGRect(x: x, y: y, width: contentWidth * scrollView.zoomScale, height: contentHeight * scrollView.zoomScale) }
步骤2:添加形状时保存相对图片的坐标
当用户添加形状(比如自定义的UIView)时,不要直接保存它的frame,而是把位置和大小转换为相对于图片本身的坐标:
// shape是用户添加的UIView func saveShapeRelativeToImage(shape: UIView, imageView: UIImageView, scrollView: UIScrollView) -> CGRect { let contentRect = getImageContentRect(in: imageView, scrollView: scrollView) guard let image = imageView.image else { return .zero } // 将形状的frame转换为相对于图片的坐标 let shapeFrameInView = shape.frame let normalizedX = (shapeFrameInView.origin.x - contentRect.origin.x) / contentRect.width let normalizedY = (shapeFrameInView.origin.y - contentRect.origin.y) / contentRect.height let normalizedWidth = shapeFrameInView.width / contentRect.width let normalizedHeight = shapeFrameInView.height / contentRect.height // 返回相对于图片尺寸的rect(也可以直接存归一化比例,按需选择) return CGRect( x: normalizedX * image.size.width, y: normalizedY * image.size.height, width: normalizedWidth * image.size.width, height: normalizedHeight * image.size.height ) }
把这个返回的CGRect存在你的数据模型里,每个形状对应一个这样的坐标信息。
步骤3:旋转后重新计算形状的frame
当设备旋转(或者UIImageView尺寸变化)时,把保存的相对图片的rect转换回当前UIImageView的坐标系,更新形状的位置:
func restoreShapeFrame(from imageRelativeRect: CGRect, imageView: UIImageView, scrollView: UIScrollView) -> CGRect { let contentRect = getImageContentRect(in: imageView, scrollView: scrollView) guard let image = imageView.image else { return .zero } // 将相对图片的坐标转换为当前视图的坐标 let normalizedX = imageRelativeRect.origin.x / image.size.width let normalizedY = imageRelativeRect.origin.y / image.size.height let normalizedWidth = imageRelativeRect.width / image.size.width let normalizedHeight = imageRelativeRect.height / image.size.height return CGRect( x: normalizedX * contentRect.width + contentRect.origin.x, y: normalizedY * contentRect.height + contentRect.origin.y, width: normalizedWidth * contentRect.width, height: normalizedHeight * contentRect.height ) }
遍历所有形状,用这个方法更新它们的frame即可。
额外注意点
- 处理
UIScrollView缩放事件时,记得在scrollViewDidEndZooming方法里也调用一次restoreShapeFrame,更新所有形状的位置。 - 如果形状支持拖拽调整,拖拽结束后要重新调用
saveShapeRelativeToImage更新保存的相对坐标。
这样不管设备怎么旋转,或者怎么缩放图片,形状都会牢牢贴在图片的对应位置上!
内容的提问来源于stack exchange,提问作者Jayfray
相关产品推荐
相关产品推荐

