Remove ScannerView and its associated components; this includes the camera handling, UI elements, and scanning logic, streamlining the project by eliminating unused code.
parent
1fcc3dbbc0
commit
fd18b7b683
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,81 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
// MARK: - 相机权限视图
|
||||||
|
struct CameraPermissionView: View {
|
||||||
|
let authorizationStatus: AVAuthorizationStatus
|
||||||
|
let onRequestPermission: () -> Void
|
||||||
|
let onOpenSettings: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 30) {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// 相机图标
|
||||||
|
Image(systemName: "camera.fill")
|
||||||
|
.font(.system(size: 80))
|
||||||
|
.foregroundColor(.gray)
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text("camera_permission_title".localized)
|
||||||
|
.font(.largeTitle)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
|
||||||
|
// 描述文本
|
||||||
|
Text(getDescriptionText())
|
||||||
|
.font(.body)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
|
||||||
|
// 操作按钮
|
||||||
|
VStack(spacing: 15) {
|
||||||
|
if authorizationStatus == .notDetermined {
|
||||||
|
Button(action: onRequestPermission) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "camera.badge.ellipsis")
|
||||||
|
Text("request_camera_permission".localized)
|
||||||
|
}
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.blue)
|
||||||
|
.cornerRadius(12)
|
||||||
|
}
|
||||||
|
} else if authorizationStatus == .denied || authorizationStatus == .restricted {
|
||||||
|
Button(action: onOpenSettings) {
|
||||||
|
HStack {
|
||||||
|
Image(systemName: "gear")
|
||||||
|
Text("open_settings".localized)
|
||||||
|
}
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.padding()
|
||||||
|
.background(Color.orange)
|
||||||
|
.cornerRadius(12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 40)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.background(Color(.systemBackground))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getDescriptionText() -> String {
|
||||||
|
switch authorizationStatus {
|
||||||
|
case .notDetermined:
|
||||||
|
return "camera_permission_description".localized
|
||||||
|
case .denied:
|
||||||
|
return "camera_permission_denied".localized
|
||||||
|
case .restricted:
|
||||||
|
return "camera_permission_restricted".localized
|
||||||
|
default:
|
||||||
|
return "camera_permission_unknown".localized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
import SwiftUI
|
||||||
|
import AVFoundation
|
||||||
|
|
||||||
|
// MARK: - 相机预览视图
|
||||||
|
struct CameraPreviewView: UIViewRepresentable {
|
||||||
|
let session: AVCaptureSession
|
||||||
|
@Binding var previewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> UIView {
|
||||||
|
let view = UIView()
|
||||||
|
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
|
||||||
|
previewLayer.frame = view.bounds
|
||||||
|
previewLayer.videoGravity = .resizeAspectFill
|
||||||
|
view.layer.addSublayer(previewLayer)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.previewLayer = previewLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UIView, context: Context) {
|
||||||
|
if let previewLayer = uiView.layer.sublayers?.first as? AVCaptureVideoPreviewLayer {
|
||||||
|
previewLayer.frame = uiView.bounds
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.previewLayer = previewLayer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import Foundation
|
||||||
|
import CoreGraphics
|
||||||
|
|
||||||
|
// MARK: - 检测到的条码数据结构
|
||||||
|
struct DetectedCode: Identifiable {
|
||||||
|
let id = UUID()
|
||||||
|
let type: String
|
||||||
|
let content: String
|
||||||
|
let bounds: CGRect
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 通知名称扩展
|
||||||
|
extension Notification.Name {
|
||||||
|
static let scannerDidScanCode = Notification.Name("scannerDidScanCode")
|
||||||
|
}
|
@ -0,0 +1,141 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - 扫描线视图
|
||||||
|
struct ScanningLineView: View {
|
||||||
|
let style: ScanningLineStyle
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
switch style {
|
||||||
|
case .modern:
|
||||||
|
ModernScanningLine()
|
||||||
|
case .classic:
|
||||||
|
ClassicScanningLine()
|
||||||
|
case .neon:
|
||||||
|
NeonScanningLine()
|
||||||
|
case .minimal:
|
||||||
|
MinimalScanningLine()
|
||||||
|
case .retro:
|
||||||
|
RetroScanningLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 扫描线样式枚举
|
||||||
|
enum ScanningLineStyle: String, CaseIterable {
|
||||||
|
case modern = "style_modern"
|
||||||
|
case classic = "style_classic"
|
||||||
|
case neon = "style_neon"
|
||||||
|
case minimal = "style_minimal"
|
||||||
|
case retro = "style_retro"
|
||||||
|
|
||||||
|
var localizedName: String {
|
||||||
|
switch self {
|
||||||
|
case .modern: return "style_modern".localized
|
||||||
|
case .classic: return "style_classic".localized
|
||||||
|
case .neon: return "style_neon".localized
|
||||||
|
case .minimal: return "style_minimal".localized
|
||||||
|
case .retro: return "style_retro".localized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 扫描线动画修饰符
|
||||||
|
struct ScanningLineModifier: ViewModifier {
|
||||||
|
@State private var isAnimating = false
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.offset(y: isAnimating ? 150 : -150)
|
||||||
|
.onAppear {
|
||||||
|
withAnimation(
|
||||||
|
Animation.linear(duration: 2)
|
||||||
|
.repeatForever(autoreverses: false)
|
||||||
|
) {
|
||||||
|
isAnimating = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 脉冲动画修饰符
|
||||||
|
struct PulseAnimationModifier: ViewModifier {
|
||||||
|
@State private var isPulsing = false
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content
|
||||||
|
.scaleEffect(isPulsing ? 1.5 : 1.0)
|
||||||
|
.opacity(isPulsing ? 0.0 : 0.8)
|
||||||
|
.onAppear {
|
||||||
|
withAnimation(
|
||||||
|
Animation.easeInOut(duration: 1.5)
|
||||||
|
.repeatForever(autoreverses: false)
|
||||||
|
) {
|
||||||
|
isPulsing = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 现代扫描线
|
||||||
|
struct ModernScanningLine: View {
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.fill(
|
||||||
|
LinearGradient(
|
||||||
|
colors: [.blue, .cyan, .blue],
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.frame(width: 200, height: 3)
|
||||||
|
.shadow(color: .blue, radius: 5, x: 0, y: 0)
|
||||||
|
.modifier(ScanningLineModifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 经典扫描线
|
||||||
|
struct ClassicScanningLine: View {
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.green)
|
||||||
|
.frame(width: 150, height: 2)
|
||||||
|
.modifier(ScanningLineModifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 霓虹扫描线
|
||||||
|
struct NeonScanningLine: View {
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.purple)
|
||||||
|
.frame(width: 180, height: 4)
|
||||||
|
.shadow(color: .purple, radius: 8, x: 0, y: 0)
|
||||||
|
.modifier(ScanningLineModifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 极简扫描线
|
||||||
|
struct MinimalScanningLine: View {
|
||||||
|
var body: some View {
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.white)
|
||||||
|
.frame(width: 100, height: 1)
|
||||||
|
.modifier(ScanningLineModifier())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 复古扫描线
|
||||||
|
struct RetroScanningLine: View {
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 2) {
|
||||||
|
ForEach(0..<5, id: \.self) { _ in
|
||||||
|
Rectangle()
|
||||||
|
.fill(Color.orange)
|
||||||
|
.frame(width: 2, height: 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.modifier(ScanningLineModifier())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - 扫描界面覆盖层
|
||||||
|
struct ScanningOverlayView: View {
|
||||||
|
let showPreviewPause: Bool
|
||||||
|
@Binding var selectedStyle: ScanningLineStyle
|
||||||
|
let detectedCodesCount: Int
|
||||||
|
let onClose: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// 扫描线组件
|
||||||
|
if !showPreviewPause {
|
||||||
|
ScanningLineView(style: selectedStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提示文本
|
||||||
|
ScanningInstructionView(
|
||||||
|
showPreviewPause: showPreviewPause,
|
||||||
|
detectedCodesCount: detectedCodesCount
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
// 底部按钮区域
|
||||||
|
ScanningBottomButtonsView(
|
||||||
|
showPreviewPause: showPreviewPause,
|
||||||
|
selectedStyle: $selectedStyle,
|
||||||
|
onClose: onClose
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 扫描指令视图
|
||||||
|
struct ScanningInstructionView: View {
|
||||||
|
let showPreviewPause: Bool
|
||||||
|
let detectedCodesCount: Int
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if showPreviewPause {
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
Text("detected_codes".localized)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
if detectedCodesCount == 1 {
|
||||||
|
Text("auto_result_1s".localized)
|
||||||
|
.foregroundColor(.green)
|
||||||
|
.font(.subheadline)
|
||||||
|
} else {
|
||||||
|
Text("select_code_instruction".localized)
|
||||||
|
.foregroundColor(.white.opacity(0.8))
|
||||||
|
.font(.subheadline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.top, 20)
|
||||||
|
} else {
|
||||||
|
Text("scan_instruction".localized)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.headline)
|
||||||
|
.padding(.top, 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 扫描底部按钮视图
|
||||||
|
struct ScanningBottomButtonsView: View {
|
||||||
|
let showPreviewPause: Bool
|
||||||
|
@Binding var selectedStyle: ScanningLineStyle
|
||||||
|
let onClose: () -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(spacing: 15) {
|
||||||
|
// 扫描线样式选择器
|
||||||
|
if !showPreviewPause {
|
||||||
|
ScanningStyleSelectorView(selectedStyle: $selectedStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭按钮 - 只在非预览选择状态时显示
|
||||||
|
if !showPreviewPause {
|
||||||
|
Button("close_button".localized) {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding()
|
||||||
|
.background(Color.black.opacity(0.6))
|
||||||
|
.cornerRadius(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - 扫描线样式选择器
|
||||||
|
struct ScanningStyleSelectorView: View {
|
||||||
|
@Binding var selectedStyle: ScanningLineStyle
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 10) {
|
||||||
|
ForEach(ScanningLineStyle.allCases, id: \.self) { style in
|
||||||
|
Button(style.localizedName) {
|
||||||
|
selectedStyle = style
|
||||||
|
}
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.horizontal, 8)
|
||||||
|
.padding(.vertical, 4)
|
||||||
|
.background(selectedStyle == style ? Color.green : Color.gray.opacity(0.6))
|
||||||
|
.cornerRadius(8)
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.bottom, 10)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - 测试自动选择按钮
|
||||||
|
struct TestAutoSelectButton: View {
|
||||||
|
let detectedCode: DetectedCode
|
||||||
|
let onSelect: (DetectedCode) -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Button("test_auto_select".localized) {
|
||||||
|
onSelect(detectedCode)
|
||||||
|
}
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(8)
|
||||||
|
.background(Color.red)
|
||||||
|
.cornerRadius(8)
|
||||||
|
.padding(.trailing, 20)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue