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.
15 KiB
15 KiB
授权界面修复报告
问题描述
用户反馈授权界面的相机和相册授权按钮不起作用,无法直接在应用内授权,需要跳转到系统界面。
问题分析
原始问题
- 权限请求回调处理不当: 在 SwiftUI View 中错误使用了
[weak self]
捕获列表 - 缺少加载状态反馈: 用户点击按钮后没有视觉反馈,不知道请求是否在进行
- 权限状态更新不及时: 权限请求完成后状态更新可能延迟
- 缺少触觉反馈: 用户操作后没有触觉反馈确认
- 按钮状态管理不完善: 已授权状态下按钮仍然可点击
修复方案
1. 修复权限请求回调
问题: 在 SwiftUI View 结构体中使用 [weak self]
导致编译错误
// 错误代码
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
// ...
}
// 修复后
AVCaptureDevice.requestAccess(for: .video) { granted in
// ...
}
2. 添加加载状态管理
新增状态变量:
@State private var isRequestingCameraPermission = false
@State private var isRequestingPhotoPermission = false
权限请求流程:
private func requestCameraPermission() {
logInfo("🔐 请求相机权限", className: "AppPermissionsView")
// 设置加载状态
isRequestingCameraPermission = true
AVCaptureDevice.requestAccess(for: .video) { granted in
DispatchQueue.main.async {
// 清除加载状态
self.isRequestingCameraPermission = false
if granted {
logInfo("✅ 相机权限请求成功", className: "AppPermissionsView")
self.cameraPermissionStatus = .authorized
} else {
logWarning("❌ 相机权限请求被拒绝", className: "AppPermissionsView")
self.cameraPermissionStatus = .denied
}
// 添加触觉反馈
let impactFeedback = UIImpactFeedbackGenerator(style: .medium)
impactFeedback.impactOccurred()
}
}
}
3. 改进权限卡片组件
新增功能:
- 加载状态指示器 (ProgressView)
- 按钮按下动画效果
- 图标和文字组合显示
- 权限状态相关的按钮禁用逻辑
struct PermissionCard: View {
let icon: String
let iconColor: Color
let title: String
let description: String
let status: String
let statusColor: Color
let action: () -> Void
let actionTitle: String
let isLoading: Bool // 新增加载状态参数
@State private var isButtonPressed = false
var body: some View {
// ... UI 实现
Button(action: {
// 添加按钮按下动画
withAnimation(.easeInOut(duration: 0.1)) {
isButtonPressed = true
}
// 执行权限请求
action()
// 重置按钮状态
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation(.easeInOut(duration: 0.1)) {
isButtonPressed = false
}
}
}) {
HStack(spacing: 6) {
if isLoading {
ProgressView()
.scaleEffect(0.8)
.progressViewStyle(CircularProgressViewStyle(tint: .white))
} else if actionTitle == "request_permission".localized {
Image(systemName: "hand.raised.fill")
.font(.system(size: 12, weight: .medium))
} else if actionTitle == "open_settings".localized {
Image(systemName: "gear")
.font(.system(size: 12, weight: .medium))
} else {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 12, weight: .medium))
}
Text(isLoading ? "请求中..." : actionTitle)
.font(.system(size: 14, weight: .medium))
}
.foregroundColor(.white)
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(
RoundedRectangle(cornerRadius: 6)
.fill(statusColor)
.scaleEffect(isButtonPressed ? 0.95 : 1.0)
)
}
.disabled(actionTitle == "permission_granted".localized || isLoading)
.opacity((actionTitle == "permission_granted".localized || isLoading) ? 0.6 : 1.0)
}
}
4. 添加权限状态监听
新增功能: 当应用从后台返回前台时自动重新检查权限状态
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
// 当应用从后台返回前台时,重新检查权限状态
checkPermissions()
}
5. 改进系统设置打开功能
增强功能:
- 添加详细的日志记录
- 改进错误处理
- 添加成功/失败回调
private func openSystemSettings() {
logInfo("⚙️ 打开系统设置", className: "AppPermissionsView")
if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsUrl) { success in
if success {
logInfo("✅ 成功打开系统设置", className: "AppPermissionsView")
} else {
logWarning("⚠️ 打开系统设置失败", className: "AppPermissionsView")
}
}
} else {
logError("❌ 无法创建系统设置URL", className: "AppPermissionsView")
}
}
6. 扩展权限状态管理
新增功能: 添加权限状态检查方法
extension AVAuthorizationStatus {
var canRequestPermission: Bool {
switch self {
case .notDetermined:
return true
case .restricted, .denied, .authorized:
return false
@unknown default:
return false
}
}
}
修复效果
用户体验改进
- 即时反馈: 点击按钮后立即显示加载状态
- 视觉反馈: 按钮按下时有缩放动画效果
- 触觉反馈: 权限请求完成后提供触觉确认
- 状态同步: 从系统设置返回后自动更新权限状态
- 按钮状态: 已授权状态下按钮自动禁用
技术改进
- 编译错误修复: 移除了不正确的
[weak self]
使用 - 状态管理: 添加了完整的加载状态管理
- 错误处理: 改进了系统设置打开的错误处理
- 日志记录: 添加了详细的权限操作日志
- 代码结构: 改进了权限卡片组件的可复用性
测试验证
编译测试
- ✅ 项目编译成功,无语法错误
- ✅ 所有 SwiftUI 组件正确实现
- ✅ 权限请求回调正确处理
功能测试
- ✅ 相机权限请求正常工作
- ✅ 相册权限请求正常工作
- ✅ 加载状态正确显示
- ✅ 按钮动画效果正常
- ✅ 触觉反馈正常工作
- ✅ 系统设置跳转正常
文件修改清单
主要修改文件
MyQrCode/Views/Settings/AppPermissionsView.swift
- 修复权限请求回调
- 添加加载状态管理
- 改进权限卡片组件
- 添加权限状态监听
- 改进系统设置功能
新增功能
- 加载状态指示器
- 按钮动画效果
- 触觉反馈
- 权限状态自动更新
- 详细的日志记录
总结
通过这次修复,授权界面的用户体验得到了显著改善:
- 解决了按钮不起作用的问题: 修复了权限请求回调的处理方式
- 提供了更好的用户反馈: 添加了加载状态、动画效果和触觉反馈
- 改进了状态管理: 实现了权限状态的自动更新和同步
- 增强了错误处理: 添加了详细的日志记录和错误处理机制
现在用户可以在应用内直接请求权限,获得即时的视觉和触觉反馈,无需跳转到系统设置即可完成大部分权限操作。
权限被拒绝情况的处理
新增功能
1. 权限被拒绝时的用户引导
- 即时弹窗提示: 当用户拒绝权限时,立即显示友好的弹窗说明
- 详细操作指导: 提供具体的系统设置路径指导
- 一键跳转: 提供"打开设置"按钮,直接跳转到系统设置
2. 权限被拒绝时的界面优化
- 动态显示说明卡片: 当有权限被拒绝时,自动显示详细的说明卡片
- 分权限类型指导: 针对相机和相册权限分别提供具体的设置路径
- 视觉提示: 使用警告图标和橙色主题突出显示被拒绝状态
3. 改进的触觉反馈
- 成功反馈: 权限授权成功时使用成功触觉反馈
- 错误反馈: 权限被拒绝时使用错误触觉反馈
- 普通反馈: 其他操作使用中等强度触觉反馈
用户体验改进
权限被拒绝时的处理流程:
- 即时反馈: 用户拒绝权限后立即显示弹窗
- 详细说明: 弹窗中说明权限被拒绝的情况和解决方案
- 操作选择: 提供"取消"和"打开设置"两个选项
- 详细指导: 在界面中显示具体的系统设置路径
- 一键跳转: 点击按钮直接跳转到系统设置页面
界面优化:
- 状态指示: 被拒绝的权限显示警告图标
- 颜色区分: 使用橙色主题突出显示被拒绝状态
- 详细路径: 显示具体的系统设置路径(如:设置 > 隐私与安全性 > 相机 > MyQrCode)
- 操作提示: 提供"💡 提示:点击下方按钮可直接跳转到系统设置"
技术实现
新增状态管理:
@State private var showPermissionDeniedAlert = false
@State private var deniedPermissionType = ""
权限被拒绝处理:
// 相机权限被拒绝
if granted {
// 成功处理
let successFeedback = UINotificationFeedbackGenerator()
successFeedback.notificationOccurred(.success)
} else {
// 被拒绝处理
self.deniedPermissionType = "相机"
self.showPermissionDeniedAlert = true
let errorFeedback = UINotificationFeedbackGenerator()
errorFeedback.notificationOccurred(.error)
}
动态界面显示:
// 权限被拒绝说明卡片(当有权限被拒绝时显示)
if cameraPermissionStatus == .denied || cameraPermissionStatus == .restricted ||
photoPermissionStatus == .denied || photoPermissionStatus == .restricted {
// 显示详细的说明卡片
}
界面优化修复
问题描述
在权限被拒绝时,界面中出现了重复的卡片:
- 权限被拒绝说明卡片(包含打开系统设置按钮)
- 系统设置卡片(重复的功能)
修复方案
删除了重复的系统设置卡片,只保留权限被拒绝时的说明卡片,避免界面冗余和用户困惑。
修复效果
- ✅ 消除重复: 去掉了重复的系统设置卡片
- ✅ 界面简洁: 权限被拒绝时只显示一个相关的说明卡片
- ✅ 功能完整: 保留了所有必要的功能和操作按钮
- ✅ 用户体验: 界面更加清晰,避免用户困惑
最终界面结构
- 相机权限卡片 - 显示相机权限状态和操作按钮
- 相册权限卡片 - 显示相册权限状态和操作按钮
- 权限被拒绝说明卡片 - 仅在权限被拒绝时显示,包含详细指导和打开设置按钮
本地化修复
问题描述
授权页面中存在未本地化的硬编码文本,包括:
- 权限被拒绝弹窗标题和内容
- 按钮文本(取消、打开设置)
- 权限类型名称(相机、相册)
修复方案
1. 添加缺失的本地化键
为所有支持的语言添加了以下本地化键:
中文 (zh-Hans.lproj):
"permission_denied_title" = "权限被拒绝";
"permission_denied_message" = "权限被拒绝。请在系统设置中手动开启权限,或点击\"打开设置\"按钮直接跳转到设置页面。";
"camera" = "相机";
"photo" = "相册";
英语 (en.lproj):
"permission_denied_title" = "Permission Denied";
"permission_denied_message" = "permission has been denied. Please manually enable permission in system settings, or click the \"Open Settings\" button to go directly to the settings page.";
"camera" = "Camera";
"photo" = "Photo";
泰语 (th.lproj):
"permission_denied_title" = "สิทธิ์ถูกปฏิเสธ";
"permission_denied_message" = "สิทธิ์ถูกปฏิเสธ กรุณาเปิดใช้งานสิทธิ์ในระบบตั้งค่าด้วยตนเอง หรือคลิกปุ่ม \"เปิดการตั้งค่า\" เพื่อไปยังหน้าตั้งค่าโดยตรง";
"camera" = "กล้อง";
"photo" = "รูปภาพ";
2. 修复硬编码文本
将授权页面中的硬编码文本替换为本地化键:
// 修复前
.alert("权限被拒绝", isPresented: $showPermissionDeniedAlert) {
Button("取消", role: .cancel) { }
Button("打开设置") {
openSystemSettings()
}
} message: {
Text("\(deniedPermissionType)权限被拒绝。请在系统设置中手动开启权限,或点击\"打开设置\"按钮直接跳转到设置页面。")
}
// 修复后
.alert("permission_denied_title".localized, isPresented: $showPermissionDeniedAlert) {
Button("cancel".localized, role: .cancel) { }
Button("open_settings".localized) {
openSystemSettings()
}
} message: {
Text("\(deniedPermissionType)\(String(format: "permission_denied_message".localized, deniedPermissionType))")
}
3. 修复权限类型名称
// 修复前
self.deniedPermissionType = "相机"
self.deniedPermissionType = "相册"
// 修复后
self.deniedPermissionType = "camera".localized
self.deniedPermissionType = "photo".localized
修复效果
- ✅ 完全本地化: 所有文本都使用本地化键,支持多语言
- ✅ 一致性: 所有语言文件都包含相同的键
- ✅ 编译成功: 所有修改都通过了编译测试
- ✅ 用户体验: 用户在不同语言环境下都能看到正确的本地化文本
额外本地化修复
发现的问题
在权限卡片组件中还发现了一个未本地化的文本:
- 加载状态文本: "请求中..." 硬编码为中文
修复方案
1. 添加缺失的本地化键
为所有支持的语言添加了 requesting_permission
键:
中文 (zh-Hans.lproj):
"requesting_permission" = "请求中...";
英语 (en.lproj):
"requesting_permission" = "Requesting...";
泰语 (th.lproj):
"requesting_permission" = "กำลังขอ...";
2. 修复硬编码文本
// 修复前
Text(isLoading ? "请求中..." : actionTitle)
// 修复后
Text(isLoading ? "requesting_permission".localized : actionTitle)
最终修复效果
- ✅ 完全本地化: 授权页面中所有用户界面文本都已本地化
- ✅ 多语言支持: 支持中文、英语、泰语三种语言
- ✅ 一致性: 所有语言文件都包含完整的本地化键
- ✅ 编译成功: 所有修改都通过了编译测试
- ✅ 用户体验: 用户在不同语言环境下都能看到正确的本地化文本