You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
7.2 KiB

import SwiftUI
// MARK: -
struct ScanningOverlayView: View {
let showPreviewPause: Bool
let detectedCodesCount: Int
let onImageDecode: () -> Void
var body: some View {
VStack {
Spacer()
// 线
if !showPreviewPause {
ScanningLineView(style: .modern)
}
//
ScanningInstructionView(
showPreviewPause: showPreviewPause,
detectedCodesCount: detectedCodesCount
)
Spacer()
//
ScanningBottomButtonsView(
showPreviewPause: showPreviewPause,
onImageDecode: onImageDecode
)
}
}
}
// 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
let onImageDecode: () -> Void
var body: some View {
VStack(spacing: 15) {
//
if !showPreviewPause {
Button(action: {
onImageDecode()
}) {
HStack(spacing: 8) {
Image(systemName: "photo.on.rectangle.angled")
.font(.system(size: 16, weight: .semibold))
Text("图片解码")
.font(.subheadline)
.fontWeight(.medium)
}
.foregroundColor(.white)
.padding(.horizontal, 16)
.padding(.vertical, 10)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color.blue.opacity(0.3))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.blue.opacity(0.6), lineWidth: 1)
)
)
}
.buttonStyle(PlainButtonStyle())
}
// 使
}
.padding(.bottom, 50)
}
}
// MARK: - 线
struct ScanningStyleSelectorView: View {
@Binding var selectedStyle: ScanningLineStyle
var body: some View {
VStack(spacing: 12) {
//
Text("扫描线样式")
.font(.caption)
.foregroundColor(.white.opacity(0.8))
.padding(.bottom, 4)
//
HStack(spacing: 8) {
ForEach(ScanningLineStyle.allCases, id: \.self) { style in
Button(action: {
withAnimation(.easeInOut(duration: 0.2)) {
selectedStyle = style
}
//
let impactFeedback = UIImpactFeedbackGenerator(style: .light)
impactFeedback.impactOccurred()
}) {
VStack(spacing: 4) {
//
stylePreview(style)
.frame(width: 24, height: 24)
//
Text(style.localizedName)
.font(.caption2)
.foregroundColor(.white)
}
.frame(width: 60, height: 50)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(selectedStyle == style ?
Color.green.opacity(0.8) :
Color.black.opacity(0.6))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(selectedStyle == style ?
Color.green :
Color.white.opacity(0.3),
lineWidth: selectedStyle == style ? 2 : 1)
)
)
}
.buttonStyle(PlainButtonStyle())
}
}
}
.padding(.horizontal, 16)
.padding(.vertical, 12)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(Color.black.opacity(0.7))
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.white.opacity(0.2), lineWidth: 1)
)
)
.padding(.bottom, 10)
}
//
@ViewBuilder
private func stylePreview(_ style: ScanningLineStyle) -> some View {
switch style {
case .modern:
Rectangle()
.fill(
LinearGradient(
colors: [.blue, .cyan, .blue],
startPoint: .leading,
endPoint: .trailing
)
)
.frame(width: 20, height: 2)
.shadow(color: .blue, radius: 2, x: 0, y: 0)
case .classic:
Rectangle()
.fill(Color.green)
.frame(width: 16, height: 2)
case .neon:
Rectangle()
.fill(Color.purple)
.frame(width: 18, height: 3)
.shadow(color: .purple, radius: 3, x: 0, y: 0)
case .minimal:
Rectangle()
.fill(Color.white)
.frame(width: 14, height: 1)
case .retro:
Rectangle()
.fill(Color.orange)
.frame(width: 20, height: 2)
.overlay(
Rectangle()
.stroke(Color.yellow, lineWidth: 0.5)
.frame(width: 18, height: 1.5)
)
}
}
}