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.

main
v504 2 months ago
parent cba3fc81ad
commit 2e6fcc7798

@ -33,9 +33,11 @@ struct MyQrCodeApp: App {
.opacity(showLaunchScreen ? 0 : 1) .opacity(showLaunchScreen ? 0 : 1)
if showLaunchScreen { if showLaunchScreen {
LaunchScreenView() LaunchScreenView {
.transition(.opacity) hideLaunchScreen()
.zIndex(1) }
.transition(.opacity)
.zIndex(1)
} }
} }
.onAppear { .onAppear {

@ -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()
}
}
}
}

@ -5,6 +5,10 @@ struct LaunchScreenView: View {
@State private var iconScale: CGFloat = 0.8 @State private var iconScale: CGFloat = 0.8
@State private var textOpacity: Double = 0.0 @State private var textOpacity: Double = 0.0
@State private var backgroundOpacity: 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 { var body: some View {
ZStack { ZStack {
@ -21,6 +25,38 @@ struct LaunchScreenView: View {
.ignoresSafeArea() .ignoresSafeArea()
.opacity(backgroundOpacity) .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 ForEach(0..<20) { index in
Circle() Circle()
@ -109,18 +145,18 @@ struct LaunchScreenView: View {
// //
VStack(spacing: 15) { VStack(spacing: 15) {
Text("MyQrCode") Text("app_title".localized)
.font(.system(size: 42, weight: .bold, design: .rounded)) .font(.system(size: 42, weight: .bold, design: .rounded))
.foregroundColor(.white) .foregroundColor(.white)
.shadow(color: .black.opacity(0.4), radius: 3, x: 0, y: 2) .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)) .font(.system(size: 20, weight: .medium))
.foregroundColor(.white.opacity(0.9)) .foregroundColor(.white.opacity(0.9))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 1) .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)) .font(.system(size: 16, weight: .regular))
.foregroundColor(.white.opacity(0.7)) .foregroundColor(.white.opacity(0.7))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
@ -154,7 +190,7 @@ struct LaunchScreenView: View {
) )
// //
Text("Initializing...") Text("initializing".localized)
.font(.system(size: 16, weight: .medium)) .font(.system(size: 16, weight: .medium))
.foregroundColor(.white.opacity(0.8)) .foregroundColor(.white.opacity(0.8))
.opacity(textOpacity) .opacity(textOpacity)
@ -182,8 +218,17 @@ struct LaunchScreenView: View {
} }
.padding(.horizontal, 40) .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 { #Preview {
LaunchScreenView() LaunchScreenView {
print("Launch completed")
}
} }

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "QR-Code Scanner & Generator";
"app_description_launch" = "Professionelle QR-Code Lösung";
"initializing" = "Initialisierung...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -2,6 +2,16 @@
// App Title // App Title
"app_title" = "MyQrCode"; "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 View
"scanner_title" = "Barcode Scanner"; "scanner_title" = "Barcode Scanner";
"scan_instruction" = "Place QR code or barcode in the frame"; "scan_instruction" = "Place QR code or barcode in the frame";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_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"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "Scanner et Générateur de QR Code";
"app_description_launch" = "Solution Professionnelle de QR Code";
"initializing" = "Initialisation...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "Scanner e Generatore di Codici QR";
"app_description_launch" = "Soluzione Professionale per Codici QR";
"initializing" = "Inizializzazione...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "QRコードスキャナージェネレーター";
"app_description_launch" = "プロフェッショナルQRコードソリューション";
"initializing" = "初期化中...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "QR 코드 스캐너 및 생성기";
"app_description_launch" = "전문 QR 코드 솔루션";
"initializing" = "초기화 중...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_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"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -179,6 +179,9 @@
"app_description" = "Scan QR codes and barcodes with ease"; "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_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_title" = "MyQrCode";
"app_subtitle" = "Сканер и Генератор QR-кодов";
"app_description_launch" = "Профессиональное решение для QR-кодов";
"initializing" = "Инициализация...";
"arc" = "Arc"; "arc" = "Arc";
"architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file"; "architecture_mismatch_detected" = "🔄 Architecture mismatch detected, deleting existing database file";
"arrow" = "Arrow"; "arrow" = "Arrow";

@ -2,6 +2,9 @@
// App Title // App Title
"app_title" = "MyQrCode"; "app_title" = "MyQrCode";
"app_subtitle" = "เครื่องสแกนและสร้าง QR Code";
"app_description_launch" = "โซลูชัน QR Code ระดับมืออาชีพ";
"initializing" = "กำลังเริ่มต้น...";
// Scanner View // Scanner View
"scan_instruction" = "วาง QR code หรือบาร์โค้ดในกรอบ"; "scan_instruction" = "วาง QR code หรือบาร์โค้ดในกรอบ";
"detected_codes" = "ตรวจพบโค้ด"; "detected_codes" = "ตรวจพบโค้ด";

@ -2,6 +2,16 @@
// 应用标题 // 应用标题
"app_title" = "MyQrCode"; "app_title" = "MyQrCode";
"app_subtitle" = "二维码扫描器与生成器";
"app_description_launch" = "专业的二维码解决方案";
// 网络权限
"network_permission_required" = "网络权限需要";
"network_permission_description" = "应用需要网络权限来提供完整的服务功能";
"check_network" = "重新检查";
"checking_network" = "检查中...";
"open_settings" = "打开设置";
"initializing" = "正在初始化...";
// 扫描视图 // 扫描视图
"scan_instruction" = "将二维码或条形码放入框内"; "scan_instruction" = "将二维码或条形码放入框内";
"detected_codes" = "检测到条码"; "detected_codes" = "检测到条码";

@ -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. **更多权限检查**: 可以扩展支持其他权限检查,如相机、相册等
Loading…
Cancel
Save