SwiftUI macOS端拖拽矩形裁剪图片横竖屏适配问题求解
你现有代码的核心问题是没有计算scaledToFit后图片在600x400容器内的实际显示尺寸和留白偏移,直接拿GeometryReader的容器尺寸做坐标转换,竖屏图片会在容器左右两侧留下大量空白,导致坐标映射完全错位。
核心修改方案
1. 先计算图片缩放后的实际显示参数
根据原始图片的宽高比和容器固定尺寸,先算出图片实际渲染的宽高,以及在容器中的上下/左右留白偏移,这是坐标转换正确的前提:
// 在DragGesture回调最前面添加这段计算 let containerSize = CGSize(width: 600, height: 400) let imgRatio = CGFloat(inputImage.width) / CGFloat(inputImage.height) let containerRatio = containerSize.width / containerSize.height var renderedImgSize: CGSize var imgOffsetX: CGFloat = 0 var imgOffsetY: CGFloat = 0 if imgRatio > containerRatio { // 横屏图:宽度撑满容器,上下有留白 renderedImgSize = CGSize(width: containerSize.width, height: containerSize.width / imgRatio) imgOffsetY = (containerSize.height - renderedImgSize.height) / 2 } else { // 竖屏图:高度撑满容器,左右有留白 renderedImgSize = CGSize(width: containerSize.height * imgRatio, height: containerSize.height) imgOffsetX = (containerSize.width - renderedImgSize.width) / 2 }
2. 修正拖拽坐标获取与映射逻辑
把DragGesture的坐标空间改为容器本地,不需要转全局坐标,简化计算流程,同时适配Core Graphics图片的Y轴坐标规则(原点在左下角):
// 把DragGesture的coordinateSpace改为.local .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged { dragGesture in // 此处插入上面的显示参数计算代码 let start = dragGesture.startLocation let current = dragGesture.location // 计算选框相对于容器的标准rect let selectionRect = CGRect( x: min(start.x, current.x), y: min(start.y, current.y), width: abs(start.x - current.x), height: abs(start.y - current.y) ) // 赋值给界面选框显示参数 positionX = selectionRect.midX positionY = selectionRect.midY selectionWidth = selectionRect.width selectionHeight = selectionRect.height // 计算原始图片和显示图片的缩放比例 let scale = CGFloat(inputImage.width) / renderedImgSize.width // 把选框坐标转换到实际图片坐标系(减掉容器留白) let adjustedX = max(0, selectionRect.minX - imgOffsetX) let adjustedY = max(0, selectionRect.minY - imgOffsetY) // 映射到原始图片坐标,同时翻转Y轴适配CGImage的坐标规则 cropPositionX = adjustedX * scale cropPositionY = (renderedImgSize.height - adjustedY - selectionRect.height) * scale // 做边界限制避免裁剪区域超出图片范围 cropWidth = min(selectionRect.width * scale, CGFloat(inputImage.width) - cropPositionX) cropHeight = min(selectionRect.height * scale, CGFloat(inputImage.height) - cropPositionY) })
3. 简化蓝色选框布局代码
去掉冗余的offset逻辑,直接用中点坐标布局即可:
Rectangle() .border(Color.blue, width: 3) .foregroundColor(.clear) .frame(width: selectionWidth, height: selectionHeight) .position(x: positionX, y: positionY)
第三方方案推荐
如果不需要完全自定义实现,可直接使用成熟的裁剪组件TOCropViewController,支持拖拽选框、固定裁剪比例、旋转等常用功能,无需手动处理坐标映射逻辑,适配横竖屏所有场景。
内容的提问来源于stack exchange,提问作者n savoini
相关产品推荐
相关产品推荐

