基于macOS的SwiftUI与Xcode:获取CGImage上的鼠标图像坐标
解决SwiftUI中鼠标悬停获取图像原始坐标的问题
需求说明
在SwiftUI的CGImageView中显示已缩放为UInt8格式的240×320灰度图像,需要在鼠标悬停时获取相对于图像原点的坐标,以此读取原始UInt16数据对应位置的值。目前已实现鼠标悬停切换光标,但无法获取目标坐标。
解决步骤
- 利用
onContinuousHover的Phase参数获取鼠标相对当前View的位置 - 通过嵌套
GeometryReader获取图像实际显示的区域(处理.aspectRatio(contentMode: .fit)带来的留白偏移) - 计算缩放比例,将鼠标相对坐标转换为图像原始坐标系下的坐标
修改后的完整代码
struct CGImageView: View { @Binding var filename: String? @Binding var minMax: [UInt16] @State private var opacity = 0.0 // 存储转换后的图像原始坐标 @State private var imageCoordinate: CGPoint? var body: some View { // 建议将数据处理移到异步任务或ViewModel中,避免body重复计算 let data = importAndScaleImageData(filename!) let grayCGImageRef = createCGImageFromUint8(data, height: 240, width: 320) let originalSize = CGSize(width: grayCGImageRef.width, height: grayCGImageRef.height) HStack { GeometryReader { containerGeo in Image(decorative: grayCGImageRef, scale: 1.0) .aspectRatio(contentMode: .fit) .border(Color.white) .overlay( // 嵌套GeometryReader获取图像实际显示的frame GeometryReader { imageGeo in Color.clear .onContinuousHover { phase in switch phase { case .active(let location): // 计算图像在容器中的偏移量与缩放比例 let imageFrame = imageGeo.frame(in: .parent) let scale = min(imageFrame.width / originalSize.width, imageFrame.height / originalSize.height) let adjustedX = (location.x - imageFrame.minX) / scale let adjustedY = (location.y - imageFrame.minY) / scale // 过滤图像范围外的坐标 if adjustedX >= 0 && adjustedX < originalSize.width && adjustedY >= 0 && adjustedY < originalSize.height { imageCoordinate = CGPoint(x: adjustedX, y: adjustedY) // 此处可通过imageCoordinate读取原始UInt16数据 // let originalValue = originalUInt16Data[Int(adjustedY) * 320 + Int(adjustedX)] } else { imageCoordinate = nil } case .ended: imageCoordinate = nil NSCursor.pop() } } .onHover { isHovering in if isHovering { NSCursor.crosshair.push() } else { NSCursor.pop() } } } ) } .frame(width: CGFloat(grayCGImageRef.width), height: CGFloat(grayCGImageRef.height)) .padding(10) } // 可选:显示当前坐标或原始数据值 .overlay( Text(imageCoordinate != nil ? "坐标: (\(Int(imageCoordinate!.x)), \(Int(imageCoordinate!.y)))" : "") .foregroundColor(.white) .padding(4) .background(Color.black.opacity(0.7)) .cornerRadius(4), alignment: .bottomTrailing ) } }
关键细节
- 坐标转换逻辑:先通过内部
GeometryReader获取图像实际显示的imageFrame,计算fit模式下的缩放比例(取宽高缩放比例的较小值),再将鼠标位置减去图像偏移量后除以缩放比例,得到原始坐标。 - 边界判断:添加坐标范围检查,确保只有鼠标在图像实际区域内时才记录有效坐标。
- 性能优化:原代码中
importAndScaleImageData和createCGImageFromUint8在body中执行会导致视图刷新时重复计算,建议移至ViewModel或异步任务中处理。
内容的提问来源于stack exchange,提问作者Nate Lockwood
相关产品推荐
相关产品推荐

