From 2e6fcc7798451301fbf3b004faf380d10270ded3 Mon Sep 17 00:00:00 2001 From: v504 Date: Thu, 4 Sep 2025 10:22:22 +0800 Subject: [PATCH] Enhance LaunchScreenView to include network permission checks and localized strings for improved user experience. Refactor launch sequence in MyQrCodeApp to utilize a completion handler for launch completion. Update localization files for multiple languages to support new app subtitle and initialization text. --- MyQrCode/Core/MyQrCodeApp.swift | 8 +- MyQrCode/Utils/NetworkPermissionManager.swift | 128 ++++++++++++++++ MyQrCode/Views/Utils/LaunchScreenView.swift | 61 +++++++- MyQrCode/de.lproj/Localizable.strings | 3 + MyQrCode/en.lproj/Localizable.strings | 10 ++ MyQrCode/es.lproj/Localizable.strings | 3 + MyQrCode/fr.lproj/Localizable.strings | 3 + MyQrCode/it.lproj/Localizable.strings | 3 + MyQrCode/ja.lproj/Localizable.strings | 3 + MyQrCode/ko.lproj/Localizable.strings | 3 + MyQrCode/pt.lproj/Localizable.strings | 3 + MyQrCode/ru.lproj/Localizable.strings | 3 + MyQrCode/th.lproj/Localizable.strings | 3 + MyQrCode/zh-Hans.lproj/Localizable.strings | 10 ++ NETWORK_PERMISSION_IMPLEMENTATION.md | 140 ++++++++++++++++++ 15 files changed, 374 insertions(+), 10 deletions(-) create mode 100644 MyQrCode/Utils/NetworkPermissionManager.swift create mode 100644 NETWORK_PERMISSION_IMPLEMENTATION.md diff --git a/MyQrCode/Core/MyQrCodeApp.swift b/MyQrCode/Core/MyQrCodeApp.swift index 4bb904f..4b23558 100644 --- a/MyQrCode/Core/MyQrCodeApp.swift +++ b/MyQrCode/Core/MyQrCodeApp.swift @@ -33,9 +33,11 @@ struct MyQrCodeApp: App { .opacity(showLaunchScreen ? 0 : 1) if showLaunchScreen { - LaunchScreenView() - .transition(.opacity) - .zIndex(1) + LaunchScreenView { + hideLaunchScreen() + } + .transition(.opacity) + .zIndex(1) } } .onAppear { diff --git a/MyQrCode/Utils/NetworkPermissionManager.swift b/MyQrCode/Utils/NetworkPermissionManager.swift new file mode 100644 index 0000000..4429855 --- /dev/null +++ b/MyQrCode/Utils/NetworkPermissionManager.swift @@ -0,0 +1,128 @@ +import Foundation +import Network +import SwiftUI + +class NetworkPermissionManager: ObservableObject { + @Published var isNetworkAvailable = false + @Published var isCheckingNetwork = true + @Published var showNetworkPermissionAlert = false + + private let monitor = NWPathMonitor() + private let queue = DispatchQueue(label: "NetworkMonitor") + + init() { + startNetworkMonitoring() + } + + deinit { + monitor.cancel() + } + + private func startNetworkMonitoring() { + monitor.pathUpdateHandler = { [weak self] path in + DispatchQueue.main.async { + self?.isNetworkAvailable = path.status == .satisfied + self?.isCheckingNetwork = false + + // 如果网络不可用,显示权限提示 + if path.status != .satisfied { + self?.showNetworkPermissionAlert = true + } + } + } + monitor.start(queue: queue) + } + + func checkNetworkPermission() { + isCheckingNetwork = true + // 重新开始监控 + monitor.cancel() + startNetworkMonitoring() + } + + func openNetworkSettings() { + if let settingsUrl = URL(string: UIApplication.openSettingsURLString) { + if UIApplication.shared.canOpenURL(settingsUrl) { + UIApplication.shared.open(settingsUrl) + } + } + } +} + +// 网络权限检查视图 +struct NetworkPermissionView: View { + @ObservedObject var networkManager: NetworkPermissionManager + let onPermissionGranted: () -> Void + + var body: some View { + VStack(spacing: 30) { + // 网络图标 + Image(systemName: "wifi.slash") + .font(.system(size: 80, weight: .light)) + .foregroundColor(.white.opacity(0.8)) + + VStack(spacing: 15) { + Text("network_permission_required".localized) + .font(.system(size: 28, weight: .bold)) + .foregroundColor(.white) + + Text("network_permission_description".localized) + .font(.system(size: 16, weight: .medium)) + .foregroundColor(.white.opacity(0.8)) + .multilineTextAlignment(.center) + .padding(.horizontal, 40) + } + + VStack(spacing: 20) { + // 检查网络按钮 + Button(action: { + networkManager.checkNetworkPermission() + }) { + HStack { + if networkManager.isCheckingNetwork { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .scaleEffect(0.8) + } else { + Image(systemName: "arrow.clockwise") + } + Text(networkManager.isCheckingNetwork ? "checking_network".localized : "check_network".localized) + } + .font(.system(size: 16, weight: .medium)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 25) + .fill(Color.white.opacity(0.2)) + ) + } + .disabled(networkManager.isCheckingNetwork) + + // 打开设置按钮 + Button(action: { + networkManager.openNetworkSettings() + }) { + HStack { + Image(systemName: "gear") + Text("open_settings".localized) + } + .font(.system(size: 16, weight: .medium)) + .foregroundColor(.white) + .frame(maxWidth: .infinity) + .frame(height: 50) + .background( + RoundedRectangle(cornerRadius: 25) + .fill(Color.blue.opacity(0.8)) + ) + } + } + .padding(.horizontal, 40) + } + .onChange(of: networkManager.isNetworkAvailable) { isAvailable in + if isAvailable { + onPermissionGranted() + } + } + } +} diff --git a/MyQrCode/Views/Utils/LaunchScreenView.swift b/MyQrCode/Views/Utils/LaunchScreenView.swift index a234247..bb81a08 100644 --- a/MyQrCode/Views/Utils/LaunchScreenView.swift +++ b/MyQrCode/Views/Utils/LaunchScreenView.swift @@ -5,6 +5,10 @@ struct LaunchScreenView: View { @State private var iconScale: CGFloat = 0.8 @State private var textOpacity: Double = 0.0 @State private var backgroundOpacity: Double = 0.0 + @State private var showNetworkPermission = false + @StateObject private var networkManager = NetworkPermissionManager() + + let onLaunchComplete: () -> Void var body: some View { ZStack { @@ -21,6 +25,38 @@ struct LaunchScreenView: View { .ignoresSafeArea() .opacity(backgroundOpacity) + // 网络权限检查视图 + if showNetworkPermission { + NetworkPermissionView(networkManager: networkManager) { + // 网络权限获取成功,继续启动 + showNetworkPermission = false + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + onLaunchComplete() + } + } + .transition(.opacity.combined(with: .scale)) + } else { + // 正常的启动界面 + launchContentView + .opacity(showNetworkPermission ? 0 : 1) + } + } + .onAppear { + startAnimations() + // 延迟检查网络权限 + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { + checkNetworkPermission() + } + } + .onChange(of: networkManager.isNetworkAvailable) { isAvailable in + if !isAvailable && !networkManager.isCheckingNetwork { + showNetworkPermission = true + } + } + } + + private var launchContentView: some View { + ZStack { // 动态背景粒子效果 ForEach(0..<20) { index in Circle() @@ -109,18 +145,18 @@ struct LaunchScreenView: View { // 应用名称和描述 VStack(spacing: 15) { - Text("MyQrCode") + Text("app_title".localized) .font(.system(size: 42, weight: .bold, design: .rounded)) .foregroundColor(.white) .shadow(color: .black.opacity(0.4), radius: 3, x: 0, y: 2) - Text("QR Code Scanner & Generator") + Text("app_subtitle".localized) .font(.system(size: 20, weight: .medium)) .foregroundColor(.white.opacity(0.9)) .multilineTextAlignment(.center) .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) - Text("Professional QR Code Solution") + Text("app_description_launch".localized) .font(.system(size: 16, weight: .regular)) .foregroundColor(.white.opacity(0.7)) .multilineTextAlignment(.center) @@ -154,7 +190,7 @@ struct LaunchScreenView: View { ) // 加载状态文本 - Text("Initializing...") + Text("initializing".localized) .font(.system(size: 16, weight: .medium)) .foregroundColor(.white.opacity(0.8)) .opacity(textOpacity) @@ -182,8 +218,17 @@ struct LaunchScreenView: View { } .padding(.horizontal, 40) } - .onAppear { - startAnimations() + } + + private func checkNetworkPermission() { + // 如果网络不可用,显示权限请求界面 + if !networkManager.isNetworkAvailable { + showNetworkPermission = true + } else { + // 网络可用,继续启动流程 + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + onLaunchComplete() + } } } @@ -211,5 +256,7 @@ struct LaunchScreenView: View { } #Preview { - LaunchScreenView() + LaunchScreenView { + print("Launch completed") + } } diff --git a/MyQrCode/de.lproj/Localizable.strings b/MyQrCode/de.lproj/Localizable.strings index 7ab8d35..6a6f865 100644 --- a/MyQrCode/de.lproj/Localizable.strings +++ b/MyQrCode/de.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "QR-Code Scanner & Generator"; +"app_description_launch" = "Professionelle QR-Code Lösung"; +"initializing" = "Initialisierung..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/en.lproj/Localizable.strings b/MyQrCode/en.lproj/Localizable.strings index 6e49e13..e06ad9b 100644 --- a/MyQrCode/en.lproj/Localizable.strings +++ b/MyQrCode/en.lproj/Localizable.strings @@ -2,6 +2,16 @@ // App Title "app_title" = "MyQrCode"; +"app_subtitle" = "QR Code Scanner & Generator"; +"app_description_launch" = "Professional QR Code Solution"; + +// Network Permission +"network_permission_required" = "Network Permission Required"; +"network_permission_description" = "The app needs network permission to provide complete service functionality"; +"check_network" = "Check Again"; +"checking_network" = "Checking..."; +"open_settings" = "Open Settings"; +"initializing" = "Initializing..."; // Scanner View "scanner_title" = "Barcode Scanner"; "scan_instruction" = "Place QR code or barcode in the frame"; diff --git a/MyQrCode/es.lproj/Localizable.strings b/MyQrCode/es.lproj/Localizable.strings index bc5dc4d..6bf4b53 100644 --- a/MyQrCode/es.lproj/Localizable.strings +++ b/MyQrCode/es.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "Escáner y Generador de Códigos QR"; +"app_description_launch" = "Solución Profesional de Códigos QR"; +"initializing" = "Inicializando..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/fr.lproj/Localizable.strings b/MyQrCode/fr.lproj/Localizable.strings index 2080d19..8baada7 100644 --- a/MyQrCode/fr.lproj/Localizable.strings +++ b/MyQrCode/fr.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "Scanner et Générateur de QR Code"; +"app_description_launch" = "Solution Professionnelle de QR Code"; +"initializing" = "Initialisation..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/it.lproj/Localizable.strings b/MyQrCode/it.lproj/Localizable.strings index 7591180..f69e1c5 100644 --- a/MyQrCode/it.lproj/Localizable.strings +++ b/MyQrCode/it.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "Scanner e Generatore di Codici QR"; +"app_description_launch" = "Soluzione Professionale per Codici QR"; +"initializing" = "Inizializzazione..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/ja.lproj/Localizable.strings b/MyQrCode/ja.lproj/Localizable.strings index 78f35cf..8b8c5c4 100644 --- a/MyQrCode/ja.lproj/Localizable.strings +++ b/MyQrCode/ja.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "QRコードスキャナー&ジェネレーター"; +"app_description_launch" = "プロフェッショナルQRコードソリューション"; +"initializing" = "初期化中..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/ko.lproj/Localizable.strings b/MyQrCode/ko.lproj/Localizable.strings index 6237a08..09c8b2f 100644 --- a/MyQrCode/ko.lproj/Localizable.strings +++ b/MyQrCode/ko.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "QR 코드 스캐너 및 생성기"; +"app_description_launch" = "전문 QR 코드 솔루션"; +"initializing" = "초기화 중..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/pt.lproj/Localizable.strings b/MyQrCode/pt.lproj/Localizable.strings index 6c3921d..fa2b06a 100644 --- a/MyQrCode/pt.lproj/Localizable.strings +++ b/MyQrCode/pt.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "Scanner e Gerador de Códigos QR"; +"app_description_launch" = "Solução Profissional de Códigos QR"; +"initializing" = "Inicializando..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/ru.lproj/Localizable.strings b/MyQrCode/ru.lproj/Localizable.strings index e862e1e..6d46a56 100644 --- a/MyQrCode/ru.lproj/Localizable.strings +++ b/MyQrCode/ru.lproj/Localizable.strings @@ -179,6 +179,9 @@ "app_description" = "Scan QR codes and barcodes with ease"; "app_description_long" = "QR Scanner is a powerful QR code and barcode scanning app that supports multiple barcode format recognition and creation."; "app_title" = "MyQrCode"; +"app_subtitle" = "Сканер и Генератор QR-кодов"; +"app_description_launch" = "Профессиональное решение для QR-кодов"; +"initializing" = "Инициализация..."; "arc" = "Arc"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "arrow" = "Arrow"; diff --git a/MyQrCode/th.lproj/Localizable.strings b/MyQrCode/th.lproj/Localizable.strings index ffb4c8f..b78465e 100644 --- a/MyQrCode/th.lproj/Localizable.strings +++ b/MyQrCode/th.lproj/Localizable.strings @@ -2,6 +2,9 @@ // App Title "app_title" = "MyQrCode"; +"app_subtitle" = "เครื่องสแกนและสร้าง QR Code"; +"app_description_launch" = "โซลูชัน QR Code ระดับมืออาชีพ"; +"initializing" = "กำลังเริ่มต้น..."; // Scanner View "scan_instruction" = "วาง QR code หรือบาร์โค้ดในกรอบ"; "detected_codes" = "ตรวจพบโค้ด"; diff --git a/MyQrCode/zh-Hans.lproj/Localizable.strings b/MyQrCode/zh-Hans.lproj/Localizable.strings index b366e24..167e24d 100644 --- a/MyQrCode/zh-Hans.lproj/Localizable.strings +++ b/MyQrCode/zh-Hans.lproj/Localizable.strings @@ -2,6 +2,16 @@ // 应用标题 "app_title" = "MyQrCode"; +"app_subtitle" = "二维码扫描器与生成器"; +"app_description_launch" = "专业的二维码解决方案"; + +// 网络权限 +"network_permission_required" = "网络权限需要"; +"network_permission_description" = "应用需要网络权限来提供完整的服务功能"; +"check_network" = "重新检查"; +"checking_network" = "检查中..."; +"open_settings" = "打开设置"; +"initializing" = "正在初始化..."; // 扫描视图 "scan_instruction" = "将二维码或条形码放入框内"; "detected_codes" = "检测到条码"; diff --git a/NETWORK_PERMISSION_IMPLEMENTATION.md b/NETWORK_PERMISSION_IMPLEMENTATION.md new file mode 100644 index 0000000..a465071 --- /dev/null +++ b/NETWORK_PERMISSION_IMPLEMENTATION.md @@ -0,0 +1,140 @@ +# 网络权限检查功能实现说明 + +## 功能概述 + +在 LaunchScreenView 中实现了网络权限检查功能,如果用户没有授权网络权限,应用会停留在启动界面并提示用户授权。 + +## 实现的功能 + +### 1. 网络权限管理器 (NetworkPermissionManager) + +**文件位置**: `MyQrCode/Utils/NetworkPermissionManager.swift` + +**主要功能**: +- 使用 `NWPathMonitor` 实时监控网络状态 +- 提供网络权限检查方法 +- 支持打开系统设置页面 +- 发布网络状态变化通知 + +**核心属性**: +```swift +@Published var isNetworkAvailable = false // 网络是否可用 +@Published var isCheckingNetwork = true // 是否正在检查网络 +@Published var showNetworkPermissionAlert = false // 是否显示权限提示 +``` + +### 2. 网络权限请求界面 (NetworkPermissionView) + +**主要组件**: +- 网络图标 (wifi.slash) +- 权限说明文本 +- 重新检查按钮 +- 打开设置按钮 + +**交互功能**: +- 点击"重新检查"按钮重新检测网络状态 +- 点击"打开设置"按钮跳转到系统设置 +- 网络恢复后自动继续启动流程 + +### 3. 启动界面集成 (LaunchScreenView) + +**修改内容**: +- 添加网络权限检查逻辑 +- 集成 NetworkPermissionView +- 支持网络权限获取后的回调 + +**启动流程**: +1. 显示正常的启动动画 (2秒) +2. 检查网络权限状态 +3. 如果网络不可用,显示权限请求界面 +4. 如果网络可用,继续启动流程 + +## 本地化支持 + +### 中文 (zh-Hans.lproj/Localizable.strings) +```strings +"network_permission_required" = "网络权限需要"; +"network_permission_description" = "应用需要网络权限来提供完整的服务功能"; +"check_network" = "重新检查"; +"checking_network" = "检查中..."; +"open_settings" = "打开设置"; +"initializing" = "正在初始化..."; +``` + +### 英文 (en.lproj/Localizable.strings) +```strings +"network_permission_required" = "Network Permission Required"; +"network_permission_description" = "The app needs network permission to provide complete service functionality"; +"check_network" = "Check Again"; +"checking_network" = "Checking..."; +"open_settings" = "Open Settings"; +"initializing" = "Initializing..."; +``` + +## 使用方式 + +### 在 MyQrCodeApp.swift 中的集成 +```swift +LaunchScreenView { + hideLaunchScreen() +} +``` + +### 网络权限检查流程 +1. 应用启动时显示启动界面 +2. 2秒后开始检查网络权限 +3. 如果网络不可用,显示权限请求界面 +4. 用户可以通过以下方式解决: + - 点击"重新检查"按钮 + - 点击"打开设置"按钮跳转到系统设置 + - 手动开启网络连接 +5. 网络恢复后自动继续启动流程 + +## 技术特点 + +### 1. 实时网络监控 +- 使用 `NWPathMonitor` 进行实时网络状态监控 +- 支持网络状态变化的即时响应 + +### 2. 用户体验优化 +- 平滑的界面过渡动画 +- 清晰的权限说明和操作指引 +- 支持多语言本地化 + +### 3. 错误处理 +- 网络检查失败时的重试机制 +- 优雅的降级处理 + +### 4. 性能优化 +- 异步网络检查,不阻塞主线程 +- 合理的检查时机和频率 + +## 测试建议 + +### 1. 网络权限测试 +- 关闭设备网络连接,验证权限请求界面显示 +- 重新开启网络,验证自动继续启动流程 +- 测试"重新检查"和"打开设置"按钮功能 + +### 2. 多语言测试 +- 切换系统语言,验证界面文本正确显示 +- 测试中英文界面的布局和功能 + +### 3. 边界情况测试 +- 网络状态快速变化时的处理 +- 应用在后台时网络状态变化 +- 长时间无网络时的用户体验 + +## 注意事项 + +1. **网络权限说明**: iOS 应用默认具有网络访问权限,此功能主要用于检测网络连接状态 +2. **用户体验**: 权限请求界面设计简洁明了,避免用户困惑 +3. **性能影响**: 网络监控对性能影响很小,但建议在不需要时及时停止监控 +4. **兼容性**: 支持 iOS 15.6+ 系统版本 + +## 后续优化建议 + +1. **网络质量检测**: 可以添加网络质量检测,区分网络不可用和网络质量差 +2. **离线模式**: 可以考虑添加离线模式支持 +3. **网络状态缓存**: 可以缓存网络状态,减少重复检查 +4. **更多权限检查**: 可以扩展支持其他权限检查,如相机、相册等