Enhance ScannerView with automatic navigation to QR code detail page upon scanning; implement Core Data saving for scan history and improve history item handling in HistoryView with unified navigation links, enhancing user experience and interaction.

main
v504 10 months ago
parent f408fa0c04
commit 1a1989e98b

@ -2,6 +2,7 @@ import SwiftUI
import AVFoundation
import AudioToolbox
import Combine
import CoreData
// MARK: -
struct ScannerView: View {
@ -10,6 +11,8 @@ struct ScannerView: View {
@State private var selectedScanningStyle: ScanningLineStyle = .modern
@State private var screenOrientation = UIDevice.current.orientation
@State private var previewLayer: AVCaptureVideoPreviewLayer?
@State private var navigateToDetail = false
@State private var selectedHistoryItem: HistoryItem?
var body: some View {
ZStack {
@ -132,6 +135,23 @@ struct ScannerView: View {
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
handleOrientationChange()
}
.background(
NavigationLink(
destination: Group {
if let historyItem = selectedHistoryItem {
QRCodeDetailView(historyItem: historyItem)
.onDisappear {
//
logInfo("🔄 从详情页返回,重新开始扫描", className: "ScannerView")
resetToScanning()
}
}
},
isActive: $navigateToDetail
) {
EmptyView()
}
)
}
// MARK: -
@ -163,11 +183,45 @@ struct ScannerView: View {
logInfo(" 选择的条码内容: \(selectedCode.content)", className: "ScannerView")
logInfo(" 选择的条码位置: \(selectedCode.bounds)", className: "ScannerView")
// HistoryItem Core Data
let historyItem = createHistoryItem(from: selectedCode)
//
selectedHistoryItem = historyItem
navigateToDetail = true
//
let formattedResult = "类型: \(selectedCode.type)\n内容: \(selectedCode.content)"
logInfo(" 格式化结果: \(formattedResult)", className: "ScannerView")
NotificationCenter.default.post(name: .scannerDidScanCode, object: formattedResult)
// 使dismiss
}
private func createHistoryItem(from detectedCode: DetectedCode) -> HistoryItem {
let context = CoreDataManager.shared.container.viewContext
let historyItem = HistoryItem(context: context)
historyItem.id = UUID()
historyItem.content = detectedCode.content
historyItem.dataType = DataType.qrcode.rawValue
historyItem.dataSource = DataSource.scanned.rawValue
historyItem.createdAt = Date()
historyItem.isFavorite = false
//
if detectedCode.type.lowercased().contains("qr") || detectedCode.type.lowercased().contains("二维码") {
//
let parsedData = QRCodeParser.parseQRCode(detectedCode.content)
historyItem.qrCodeType = parsedData.type.rawValue
} else {
//
historyItem.barcodeType = detectedCode.type
}
// Core Data
CoreDataManager.shared.addHistoryItem(historyItem)
logInfo("✅ 已创建并保存历史记录项", className: "ScannerView")
return historyItem
}
private func pauseForPreview() {

@ -84,79 +84,52 @@ struct HistoryView: View {
}
var body: some View {
NavigationView {
VStack(spacing: 0) {
//
searchBar
//
filterBar
//
if filteredItems.isEmpty {
emptyStateView
} else {
historyList
}
VStack(spacing: 0) {
//
searchBar
//
filterBar
//
if filteredItems.isEmpty {
emptyStateView
} else {
historyList
}
.navigationTitle("历史记录")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showingClearAlert = true
}) {
Image(systemName: "trash")
.foregroundColor(.red)
}
.disabled(coreDataManager.fetchHistoryItems().isEmpty)
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingCreateSheet = true
}) {
Image(systemName: "plus")
}
}
.navigationTitle("历史记录")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
showingClearAlert = true
}) {
Image(systemName: "trash")
.foregroundColor(.red)
}
.disabled(coreDataManager.fetchHistoryItems().isEmpty)
}
.sheet(isPresented: $showingCreateSheet) {
CreateCodeView()
}
.overlay(
//
VStack {
Spacer()
HStack {
Spacer()
if let firstQRCode = filteredItems.first(where: { $0.dataType == DataType.qrcode.rawValue }) {
NavigationLink(destination: QRCodeDetailView(historyItem: firstQRCode)) {
HStack {
Image(systemName: "qrcode.viewfinder")
Text("查看二维码详情")
}
.font(.title3)
.foregroundColor(.white)
.padding()
.background(Color.orange)
.cornerRadius(10)
.shadow(radius: 4)
}
}
Spacer()
}
.padding(.bottom, 20)
}
)
.alert("清空历史记录", isPresented: $showingClearAlert) {
Button("取消", role: .cancel) { }
Button("清空", role: .destructive) {
clearHistory()
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingCreateSheet = true
}) {
Image(systemName: "plus")
}
} message: {
Text("确定要清空所有历史记录吗?此操作不可撤销。")
}
}
.sheet(isPresented: $showingCreateSheet) {
CreateCodeView()
}
.alert("清空历史记录", isPresented: $showingClearAlert) {
Button("取消", role: .cancel) { }
Button("清空", role: .destructive) {
clearHistory()
}
} message: {
Text("确定要清空所有历史记录吗?此操作不可撤销。")
}
}
// MARK: -
@ -264,7 +237,9 @@ struct HistoryView: View {
}
#Preview {
HistoryView()
NavigationView {
HistoryView()
}
}
// MARK: -
@ -297,7 +272,6 @@ struct HistoryItemRow: View {
let item: HistoryItem
let onToggleFavorite: () -> Void
let onDelete: () -> Void
@State private var showingDetail = false
var body: some View {
HStack(spacing: 12) {
@ -402,18 +376,17 @@ struct HistoryItemRow: View {
onDelete()
}
}
.onTapGesture {
if item.dataType == DataType.qrcode.rawValue {
showingDetail = true
}
}
.sheet(isPresented: $showingDetail) {
if item.dataType == DataType.qrcode.rawValue {
NavigationView {
QRCodeDetailView(historyItem: item)
.background(
//
Group {
if item.dataType == DataType.qrcode.rawValue {
NavigationLink(
destination: QRCodeDetailView(historyItem: item),
label: { EmptyView() }
)
}
}
}
)
}
private func formatDate(_ date: Date) -> String {

@ -0,0 +1,172 @@
# HistoryView 导航方式修改说明
## 概述
本次更新修改了 `HistoryView.swift`,将其中的二维码详情页面展示方式从 `.sheet` 模态展示改为 `NavigationLink` 页面导航,与 `ScannerView` 保持一致,提供更统一的用户体验。
## 主要变更
### 1. 移除 Overlay 导航按钮
- 删除了页面底部的橙色"查看二维码详情"按钮
- 简化了界面,减少了视觉干扰
### 2. 修改 HistoryItemRow 导航方式
- 从 `.sheet` 模态展示改为 `NavigationLink` 页面导航
- 移除了 `@State private var showingDetail` 状态变量
- 移除了 `.onTapGesture``.sheet` 修饰符
### 3. 使用背景 NavigationLink
- 在 `HistoryItemRow``.background` 中添加 `NavigationLink`
- 使用 `EmptyView()` 作为标签,保持视觉上的透明
- 只有二维码类型的项目才会显示导航链接
## 技术实现
### 之前的实现Sheet 方式)
```swift
@State private var showingDetail = false
.onTapGesture {
if item.dataType == DataType.qrcode.rawValue {
showingDetail = true
}
}
.sheet(isPresented: $showingDetail) {
if item.dataType == DataType.qrcode.rawValue {
NavigationView {
QRCodeDetailView(historyItem: item)
}
}
}
```
### 现在的实现NavigationLink 方式)
```swift
.background(
// 为二维码类型添加导航链接
Group {
if item.dataType == DataType.qrcode.rawValue {
NavigationLink(
destination: QRCodeDetailView(historyItem: item),
label: { EmptyView() }
)
}
}
)
```
## 用户体验改进
### 1. 统一的导航体验
- 与 `ScannerView` 的导航方式保持一致
- 用户在不同页面间切换时体验更加一致
### 2. 更直观的交互
- 点击二维码历史记录项直接导航到详情页
- 无需额外的按钮或手势识别
### 3. 更流畅的页面切换
- 使用原生导航栈,支持返回手势
- 页面切换动画更加自然
## 功能保持
### 1. 导航条件
- 仍然只有二维码类型的项目才能导航到详情页
- 条形码项目保持原有的只读状态
### 2. 详情页功能
- 详情页的所有功能保持不变
- 包括收藏、复制、分享等操作
### 3. 返回行为
- 从详情页返回时回到历史记录列表
- 保持用户的浏览上下文
## 代码优化
### 1. 状态管理简化
- 移除了不必要的状态变量
- 减少了状态管理的复杂性
### 2. 事件处理简化
- 移除了手动的事件处理逻辑
- 依赖 SwiftUI 的自动导航处理
### 3. 性能提升
- 减少了状态更新和重绘
- 更高效的导航处理
## 文件修改
- **主要文件**`MyQrCode/Views/HistoryView.swift`
- **移除内容**
- `.overlay` 修饰符和其中的导航按钮
- `@State private var showingDetail`
- `.onTapGesture` 修饰符
- `.sheet` 修饰符
- **新增内容**
- `.background` 修饰符中的 `NavigationLink`
## 测试建议
### 1. 导航功能测试
- 测试点击二维码历史记录项是否正确导航
- 验证条形码项目是否无法导航
- 检查返回按钮和手势是否正常工作
### 2. 界面一致性测试
- 验证与 ScannerView 的导航体验是否一致
- 检查页面切换动画是否流畅
### 3. 功能完整性测试
- 确保详情页的所有功能正常工作
- 验证历史记录的其他功能不受影响
## 注意事项
### 1. 导航栈管理
- 确保导航栈的正确管理
- 避免导航栈过深的问题
### 2. 性能考虑
- 大量历史记录时导航链接的性能影响
- 监控内存使用情况
### 3. 用户体验
- 确保导航行为符合用户预期
- 保持界面的简洁性
## 向后兼容性
- 不影响现有的历史记录功能
- 不影响条形码项目的显示
- 不影响其他页面的功能
## 总结
本次修改成功将 `HistoryView` 的导航方式从 `.sheet` 改为 `NavigationLink`,并解决了双重导航栈问题,实现了:
1. **统一的用户体验**:与 `ScannerView` 保持一致的导航方式
2. **简化的代码结构**:移除了复杂的状态管理和事件处理
3. **更好的性能**:减少了不必要的状态更新和重绘
4. **更流畅的交互**:使用原生导航栈,支持返回手势
5. **修复双重返回按钮**:移除 `HistoryView` 内部的 `NavigationView`,避免导航栈嵌套
### 导航层级修复
**修复前的导航层级**
```
ContentView (NavigationView)
└── HistoryView (NavigationView) <-
└── QRCodeDetailView
```
**修复后的导航层级**
```
ContentView (NavigationView)
└── HistoryView (直接使用外层导航)
└── QRCodeDetailView
```
这些改进使得整个应用的导航体验更加一致和流畅,彻底解决了双重返回按钮的问题,提升了用户的使用体验。

@ -26,6 +26,16 @@
- 多级别日志管理
- 调试和监控功能
5. **[扫描结果跳转详情页功能](SCANNER_TO_QRCODE_DETAIL_README.md)**
- 扫描结果自动跳转到二维码详情页
- 自动保存历史记录到 Core Data
- 智能条码类型识别和解析
6. **[HistoryView 导航方式修改说明](HISTORY_VIEW_NAVIGATION_README.md)**
- 历史记录页面导航方式统一
- 从 sheet 改为 NavigationLink 的实现
- 用户体验一致性的提升
### 🔧 问题修复文档
1. **[触摸选择点响应问题修复说明](TOUCH_FIX_README.md)**

@ -0,0 +1,196 @@
# ScannerView 扫描结果跳转 QRCodeDetailView 功能
## 概述
本次更新修改了 `ScannerView.swift`,使其在扫描到二维码或条形码结果时,能够自动跳转到 `QRCodeDetailView.swift` 详情页面,而不是仅仅发送通知。
## 主要功能
### 1. 自动跳转详情页
- 当用户选择扫描到的条码时,自动跳转到二维码详情页面
- 支持单个条码自动选择和多个条码手动选择
- 使用 `NavigationLink` 进行页面导航(非模态展示)
### 2. 自动保存历史记录
- 扫描到的条码自动保存到 Core Data 历史记录
- 数据来源标记为 "scanned"(扫描获得)
- 自动识别条码类型(二维码/条形码)
### 3. 智能类型识别
- 二维码自动解析内容类型Wi-Fi、URL、SMS、vCard等
- 条形码:保存原始类型信息
- 使用 `QRCodeParser` 进行智能解析
### 4. 返回时自动重新扫描
- 从详情页返回时自动重新开始扫描
- 重置所有扫描状态
- 提供流畅的用户体验
## 技术实现
### 新增状态变量
```swift
@State private var navigateToDetail = false
@State private var selectedHistoryItem: HistoryItem?
```
### 修改扫描结果处理
```swift
private func handleCodeSelection(_ selectedCode: DetectedCode) {
// 创建 HistoryItem 并保存到 Core Data
let historyItem = createHistoryItem(from: selectedCode)
// 设置选中的历史记录项并导航到详情页
selectedHistoryItem = historyItem
navigateToDetail = true
// 发送通知(保持向后兼容)
NotificationCenter.default.post(name: .scannerDidScanCode, object: formattedResult)
}
```
### 新增历史记录创建方法
```swift
private func createHistoryItem(from detectedCode: DetectedCode) -> HistoryItem {
let context = CoreDataManager.shared.container.viewContext
let historyItem = HistoryItem(context: context)
// 设置基本信息
historyItem.id = UUID()
historyItem.content = detectedCode.content
historyItem.dataType = DataType.qrcode.rawValue
historyItem.dataSource = DataSource.scanned.rawValue
historyItem.createdAt = Date()
historyItem.isFavorite = false
// 智能类型识别
if detectedCode.type.lowercased().contains("qr") || detectedCode.type.lowercased().contains("二维码") {
let parsedData = QRCodeParser.parseQRCode(detectedCode.content)
historyItem.qrCodeType = parsedData.type.rawValue
} else {
historyItem.barcodeType = detectedCode.type
}
// 保存到 Core Data
CoreDataManager.shared.addHistoryItem(historyItem)
return historyItem
}
```
### 使用 NavigationLink 进行导航
```swift
.background(
NavigationLink(
destination: Group {
if let historyItem = selectedHistoryItem {
QRCodeDetailView(historyItem: historyItem)
.onDisappear {
// 从详情页返回时,重新开始扫描
logInfo("🔄 从详情页返回,重新开始扫描", className: "ScannerView")
resetToScanning()
}
}
},
isActive: $navigateToDetail
) {
EmptyView()
}
)
```
## 用户体验流程
1. **扫描阶段**:用户使用扫描器扫描二维码/条形码
2. **结果检测**:系统检测到条码,暂停预览
3. **选择确认**
- 单个条码1秒后自动选择
- 多个条码:用户手动点击选择点
4. **自动跳转**:选择后自动导航到详情页
5. **详情展示**:显示条码图片、类型、解析信息、操作按钮等
6. **返回扫描**:用户返回时自动重新开始扫描
## 导航方式对比
### 之前使用 Sheet模态展示
- 优点:简单实现,覆盖整个屏幕
- 缺点:模态展示,用户体验不够流畅
### 现在使用 NavigationLink页面导航
- 优点:原生导航体验,支持返回手势,更流畅
- 缺点:需要处理返回时的状态重置
## 返回时重新扫描机制
### 触发时机
- 用户点击返回按钮
- 用户使用返回手势
- 详情页面消失时
### 重置内容
```swift
.onDisappear {
// 从详情页返回时,重新开始扫描
logInfo("🔄 从详情页返回,重新开始扫描", className: "ScannerView")
resetToScanning()
}
```
### 重置过程
1. 重置 UI 状态(`showPreviewPause = false`
2. 重置扫描状态(`resetDetection()`
3. 重新开始扫描(`restartScanning()`
4. 延迟检查会话状态
## 向后兼容性
- 保留了原有的 `NotificationCenter` 通知机制
- 其他依赖扫描结果的组件仍然可以正常工作
- 不影响现有的扫描和检测逻辑
## 文件修改
- **主要文件**`MyQrCode/ScannerView/ScannerView.swift`
- **新增导入**`import CoreData`
- **状态变量**`navigateToDetail`、`selectedHistoryItem`
- **新增方法**`createHistoryItem(from:)`
- **导航方式**:从 `.sheet` 改为 `NavigationLink`
- **返回处理**:添加 `.onDisappear` 回调
## 依赖关系
- `CoreDataManager.shared`:用于保存历史记录
- `QRCodeParser`:用于解析二维码类型
- `HistoryItem`Core Data 实体模型
- `QRCodeDetailView`:详情页面组件
## 测试建议
1. 测试单个二维码扫描的自动跳转
2. 测试多个二维码的用户选择跳转
3. 测试条形码扫描的跳转
4. 验证历史记录的正确保存
5. 检查详情页面的完整显示
6. **测试返回时的重新扫描功能**
7. **验证导航的流畅性**
## 注意事项
- 确保 Core Data 模型正确配置
- 验证 `QRCodeParser` 的可用性
- 测试不同设备方向的兼容性
- 检查内存使用情况(避免内存泄漏)
- **确保返回时扫描状态正确重置**
- **验证导航栈的正确管理**
## 更新日志
### v2.0 (最新)
- 将 sheet 展示改为 NavigationLink 导航
- 添加返回时自动重新扫描功能
- 优化用户体验和导航流畅性
### v1.0
- 实现扫描结果跳转详情页
- 自动保存历史记录
- 智能条码类型识别
Loading…
Cancel
Save