如何获取SwiftUI中屏幕触摸点处的像素亮度?
获取SwiftUI中触摸点像素亮度的方法
要实现这个需求,你需要结合SwiftUI的触摸手势和UIKit的图像捕获能力,步骤如下:
核心思路
- 给视图添加触摸手势,获取触摸在视图内的坐标
- 对当前视图进行快照,生成UIImage
- 根据触摸坐标从快照中提取对应像素的颜色
- 通过颜色的RGB值计算亮度(标准公式:
亮度 = 0.299×R + 0.587×G + 0.114×B,如果颜色带透明度,还需乘以alpha值)
完整实现代码
import SwiftUI struct ContentView: View { @State var brightness = 0.0 var body: some View { VStack { Rectangle() .frame(width: 100, height: 100) .foregroundColor(.black) Circle() .frame(width: 100, height: 100) .foregroundColor(.gray) RoundedRectangle(cornerRadius: 30) .frame(width: 100, height: 100) .foregroundColor(.gray.opacity(0.5)) Text("brightness = \(String(format: "%.2f", brightness))") .font(.largeTitle) } .gesture( DragGesture(minimumDistance: 0) .onChanged { value in self.getBrightness(at: value.location) } ) } private func getBrightness(at point: CGPoint) { // 获取当前视图的快照 guard let image = self.snapshot() else { return } // 将SwiftUI坐标转换为图像坐标(图像坐标系原点在左下角) let imagePoint = CGPoint( x: point.x, y: image.size.height - point.y ) // 检查坐标是否在图像范围内 guard imagePoint.x >= 0, imagePoint.x < image.size.width, imagePoint.y >= 0, imagePoint.y < image.size.height else { brightness = 0.0 return } // 获取像素颜色 guard let pixelColor = image.getPixelColor(at: imagePoint) else { brightness = 0.0 return } // 计算亮度(考虑透明度) let red = CGFloat(pixelColor.red) / 255.0 let green = CGFloat(pixelColor.green) / 255.0 let blue = CGFloat(pixelColor.blue) / 255.0 let alpha = CGFloat(pixelColor.alpha) / 255.0 brightness = (0.299 * red + 0.587 * green + 0.114 * blue) * alpha } } // 扩展View,添加快照方法 extension View { func snapshot() -> UIImage? { let controller = UIHostingController(rootView: self) let view = controller.view let targetSize = controller.view.intrinsicContentSize view?.bounds = CGRect(origin: .zero, size: targetSize) view?.backgroundColor = .clear let renderer = UIGraphicsImageRenderer(size: targetSize) return renderer.image { _ in view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true) } } } // 扩展UIImage,添加获取像素颜色的方法 extension UIImage { func getPixelColor(at point: CGPoint) -> (red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8)? { guard let cgImage = self.cgImage else { return nil } let width = cgImage.width let height = cgImage.height let bytesPerPixel = 4 let bytesPerRow = bytesPerPixel * width let bitsPerComponent = 8 var pixelData = [UInt8](repeating: 0, count: width * height * bytesPerPixel) guard let context = CGContext( data: &pixelData, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue ) else { return nil } context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) let pixelIndex = Int((point.y * CGFloat(width)) + point.x) * bytesPerPixel return ( red: pixelData[pixelIndex], green: pixelData[pixelIndex + 1], blue: pixelData[pixelIndex + 2], alpha: pixelData[pixelIndex + 3] ) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
关键说明
- 触摸手势:使用
DragGesture(minimumDistance: 0)实现实时触摸检测,只要手指在视图上移动就会触发亮度计算 - 坐标转换:SwiftUI的坐标系原点在左上角,而UIImage的坐标系原点在左下角,所以需要转换y轴坐标
- 亮度计算:采用ITU-R BT.601标准的亮度计算公式,同时考虑颜色的透明度,确保半透明元素的亮度计算准确
- 快照方法:通过
UIHostingController将SwiftUI视图转换为UIKit视图,再进行截图,保证获取的是当前视图的真实像素
内容的提问来源于stack exchange,提问作者Peter_22
相关产品推荐
相关产品推荐

