import SwiftUI import AVFoundation // MARK: - 条码位置标记覆盖层 struct CodePositionOverlay: View { let detectedCodes: [DetectedCode] let previewLayer: AVCaptureVideoPreviewLayer? let onCodeSelected: (DetectedCode) -> Void var body: some View { ZStack { GeometryReader { geometry in ForEach(detectedCodes) { code in CodePositionMarker( code: code, screenSize: geometry.size, previewLayer: previewLayer, onCodeSelected: onCodeSelected ) } // 调试信息:显示触摸区域边界 #if DEBUG ForEach(detectedCodes) { code in let position = calculateDebugPosition(code: code, screenSize: geometry.size) Rectangle() .stroke(Color.red, lineWidth: 1) .frame(width: 80, height: 80) .position(x: position.x, y: position.y) .opacity(0.3) } #endif } .ignoresSafeArea() } .allowsHitTesting(true) .contentShape(Rectangle()) // 确保整个区域都可以接收触摸事件 .zIndex(1000) // 确保在最上层,不被其他视图遮挡 } // 调试用的位置计算方法 private func calculateDebugPosition(code: DetectedCode, screenSize: CGSize) -> CGPoint { guard let previewLayer = previewLayer else { return CGPoint(x: screenSize.width / 2, y: screenSize.height / 2) } let metadataObject = code.bounds let convertedPoint = previewLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint( x: metadataObject.midX, y: metadataObject.midY )) let clampedX = max(40, min(screenSize.width - 40, convertedPoint.x)) let clampedY = max(40, min(screenSize.height - 40, convertedPoint.y)) return CGPoint(x: clampedX, y: clampedY) } } // MARK: - 单个条码位置标记 struct CodePositionMarker: View { let code: DetectedCode let screenSize: CGSize let previewLayer: AVCaptureVideoPreviewLayer? let onCodeSelected: (DetectedCode) -> Void var body: some View { GeometryReader { geometry in let position = calculatePosition(screenSize: geometry.size) ZStack { // 触摸区域背景(透明,但可以接收触摸事件) Circle() .fill(Color.clear) .frame(width: 80, height: 80) .contentShape(Circle()) .onTapGesture { logDebug("🎯 CodePositionMarker 被点击!", className: "CodePositionMarker") logDebug(" 条码ID: \(code.id)", className: "CodePositionMarker") logDebug(" 条码类型: \(code.type)", className: "CodePositionMarker") logDebug(" 条码内容: \(code.content)", className: "CodePositionMarker") logDebug(" 点击位置: x=\(position.x), y=\(position.y)", className: "CodePositionMarker") // 添加触觉反馈 let impactFeedback = UIImpactFeedbackGenerator(style: .medium) impactFeedback.impactOccurred() onCodeSelected(code) } // 外圈 Circle() .stroke(Color.green, lineWidth: 3) .frame(width: 40, height: 40) // 内圈 Circle() .fill(Color.green.opacity(0.3)) .frame(width: 20, height: 20) // 中心点 Circle() .fill(Color.green) .frame(width: 6, height: 6) } .position(x: position.x, y: position.y) .zIndex(1001) // 确保触摸区域在最上层 .onAppear { logDebug("CodePositionMarker appeared at: x=\(position.x), y=\(position.y)", className: "CodePositionMarker") logDebug("Screen size: \(geometry.size)", className: "CodePositionMarker") logDebug("Code bounds: \(code.bounds)", className: "CodePositionMarker") } } } private func calculatePosition(screenSize: CGSize) -> CGPoint { guard let previewLayer = previewLayer else { logWarning("No preview layer available, using screen center", className: "CodePositionMarker") return CGPoint(x: screenSize.width / 2, y: screenSize.height / 2) } guard previewLayer.session?.isRunning == true else { logWarning("Preview layer session not running, using screen center", className: "CodePositionMarker") return CGPoint(x: screenSize.width / 2, y: screenSize.height / 2) } let metadataObject = code.bounds let convertedPoint = previewLayer.layerPointConverted(fromCaptureDevicePoint: CGPoint( x: metadataObject.midX, y: metadataObject.midY )) guard convertedPoint.x.isFinite && convertedPoint.y.isFinite else { logWarning("Invalid converted point: \(convertedPoint), using screen center", className: "CodePositionMarker") return CGPoint(x: screenSize.width / 2, y: screenSize.height / 2) } let clampedX = max(20, min(screenSize.width - 20, convertedPoint.x)) let clampedY = max(20, min(screenSize.height - 20, convertedPoint.y)) logDebug("AVFoundation bounds: \(code.bounds)", className: "CodePositionMarker") logDebug("Converted point: \(convertedPoint)", className: "CodePositionMarker") logDebug("Screen size: \(screenSize)", className: "CodePositionMarker") logDebug("Clamped: x=\(clampedX), y=\(clampedY)", className: "CodePositionMarker") return CGPoint(x: clampedX, y: clampedY) } } // MARK: - 重新扫描按钮样式 struct RescanButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .scaleEffect(configuration.isPressed ? 0.95 : 1.0) .opacity(configuration.isPressed ? 0.8 : 1.0) .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) } }