# 手电筒功能添加说明

## 🎯 功能概述

为 `ScannerView` 添加了手电筒控制功能，用户可以通过工具栏按钮打开/关闭手电筒，提升在低光环境下的扫描体验。

## 🔧 主要修改内容

### 1. **ScannerViewModel.swift 的修改**

#### 新增状态变量
```swift
@Published var isTorchOn = false  // 手电筒开关状态
private var videoDevice: AVCaptureDevice?  // 视频设备引用
```

#### 保存视频设备引用
```swift
private func setupCaptureSession() {
    // ... 现有代码 ...
    
    guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else {
        showAlert = true
        return
    }
    
    // 保存视频设备引用，用于手电筒控制
    videoDevice = videoCaptureDevice
    
    // ... 现有代码 ...
}
```

#### 手电筒控制方法
```swift
// MARK: - 手电筒控制

/// 检查设备是否支持手电筒
var isTorchAvailable: Bool {
    guard let device = videoDevice else { return false }
    return device.hasTorch && device.isTorchAvailable
}

/// 切换手电筒状态
func toggleTorch() {
    guard let device = videoDevice else {
        logWarning("❌ 没有可用的视频设备", className: "ScannerViewModel")
        return
    }
    
    guard device.hasTorch && device.isTorchAvailable else {
        logWarning("❌ 设备不支持手电筒", className: "ScannerViewModel")
        return
    }
    
    do {
        try device.lockForConfiguration()
        
        if isTorchOn {
            // 关闭手电筒
            try device.setTorchModeOn(level: 0.0)
            isTorchOn = false
            logInfo("🔦 手电筒已关闭", className: "ScannerViewModel")
        } else {
            // 打开手电筒
            try device.setTorchModeOn(level: 1.0)
            isTorchOn = true
            logInfo("🔦 手电筒已打开", className: "ScannerViewModel")
        }
        
        device.unlockForConfiguration()
        
    } catch {
        logError("❌ 手电筒控制失败: \(error.localizedDescription)", className: "ScannerViewModel")
        device.unlockForConfiguration()
    }
}

/// 关闭手电筒
func turnOffTorch() {
    guard let device = videoDevice else { return }
    
    do {
        try device.lockForConfiguration()
        try device.setTorchModeOn(level: 0.0)
        isTorchOn = false
        device.unlockForConfiguration()
        logInfo("🔦 手电筒已关闭", className: "ScannerViewModel")
    } catch {
        logError("❌ 关闭手电筒失败: \(error.localizedDescription)", className: "ScannerViewModel")
        device.unlockForConfiguration()
    }
}
```

### 2. **ScannerView.swift 的修改**

#### 工具栏布局更新
```swift
.toolbar {
    ToolbarItem(placement: .navigationBarLeading) {
        // 手电筒按钮 - 只在相机权限已授权时显示
        if scannerViewModel.cameraAuthorizationStatus == .authorized && scannerViewModel.isTorchAvailable {
            Button(action: {
                logInfo("🔦 用户点击手电筒按钮", className: "ScannerView")
                
                // 添加触觉反馈
                let impactFeedback = UIImpactFeedbackGenerator(style: .medium)
                impactFeedback.impactOccurred()
                
                scannerViewModel.toggleTorch()
            }) {
                Image(systemName: scannerViewModel.isTorchOn ? "bolt.fill" : "bolt")
                    .font(.system(size: 18, weight: .semibold))
                    .foregroundColor(scannerViewModel.isTorchOn ? .yellow : .blue)
            }
        }
    }
    
    ToolbarItem(placement: .navigationBarTrailing) {
        // 重新扫描按钮 - 只在预览暂停状态时显示
        if showPreviewPause {
            // ... 现有代码 ...
        }
    }
}
```

#### 生命周期管理
```swift
.onDisappear {
    scannerViewModel.stopScanning()
    // 确保退出时关闭手电筒
    if scannerViewModel.isTorchOn {
        scannerViewModel.turnOffTorch()
    }
}
```

## 🚀 功能特性

### 1. **智能显示**
- 手电筒按钮只在设备支持手电筒时显示
- 只在相机权限已授权时显示
- 避免在不支持的设备上显示无效按钮

### 2. **状态指示**
- 使用闪电图标表示手电筒状态：
  - `bolt`: 手电筒关闭状态（蓝色）
  - `bolt.fill`: 手电筒开启状态（黄色）
- 颜色变化提供直观的状态反馈

### 3. **安全控制**
- 设备配置锁定/解锁确保安全
- 错误处理和日志记录
- 应用退出时自动关闭手电筒

### 4. **用户体验**
- 触觉反馈增强交互体验
- 按钮位置合理（导航栏左侧）
- 图标大小和样式符合系统设计

## 📱 界面布局

### 工具栏布局
```
[🔦 手电筒] ← 扫描器 → [🔄 重新扫描]
```

- **左侧**: 手电筒按钮（始终显示，如果支持）
- **中间**: 导航标题
- **右侧**: 重新扫描按钮（仅在预览暂停时显示）

### 按钮状态
- **关闭状态**: 蓝色闪电图标 + 关闭填充
- **开启状态**: 黄色闪电图标 + 开启填充

## 🧪 测试要点

### 1. **功能测试**
- ✅ 手电筒按钮在支持的设备上正确显示
- ✅ 点击按钮正确切换手电筒状态
- ✅ 手电筒实际开启/关闭
- ✅ 状态指示正确更新

### 2. **兼容性测试**
- ✅ 在不支持手电筒的设备上不显示按钮
- ✅ 在相机权限未授权时不显示按钮
- ✅ 在不同设备上正常工作

### 3. **安全性测试**
- ✅ 应用退出时手电筒自动关闭
- ✅ 设备配置正确锁定/解锁
- ✅ 错误情况下的安全处理

### 4. **用户体验测试**
- ✅ 触觉反馈正常工作
- ✅ 按钮响应及时
- ✅ 图标和颜色变化清晰

## 🔍 技术实现细节

### 1. **AVFoundation 集成**
- 使用 `AVCaptureDevice` 控制手电筒
- 设备配置锁定确保安全操作
- 错误处理和资源管理

### 2. **状态管理**
- `@Published var isTorchOn` 提供响应式状态
- 状态变化自动更新 UI
- 生命周期管理确保状态一致性

### 3. **权限检查**
- 检查设备硬件支持
- 检查相机权限状态
- 条件渲染避免无效操作

### 4. **错误处理**
- 完整的错误捕获和日志记录
- 设备配置解锁确保资源释放
- 用户友好的错误提示

## 🚨 注意事项

### 1. **设备兼容性**
- 不是所有设备都支持手电筒
- 需要检查 `hasTorch` 和 `isTorchAvailable` 属性
- 在模拟器上可能无法测试手电筒功能

### 2. **权限管理**
- 手电筒功能需要相机权限
- 权限变化时按钮状态需要更新
- 权限被拒绝时的处理

### 3. **电池管理**
- 手电筒会消耗较多电量
- 建议在不需要时及时关闭
- 应用退出时自动关闭

### 4. **用户体验**
- 手电筒开启时提供视觉反馈
- 考虑添加亮度调节功能
- 在低光环境下的使用提示

## 🐛 Bug修复

### 问题描述
初始实现中使用 `setTorchModeOn(level: 0.0)` 来关闭手电筒，这会导致崩溃：
```
Terminating app due to uncaught exception 'NSInvalidArgumentException', 
reason: '*** -[AVCaptureDevice setTorchModeOnWithLevel:error:] The passed torchLevel 0.000000 is invalid'
```

### 修复方案
```swift
// ❌ 错误的关闭方式
try device.setTorchModeOn(level: 0.0)

// ✅ 正确的关闭方式
device.torchMode = .off
```

### 修复后的代码
```swift
if isTorchOn {
    // 关闭手电筒
    device.torchMode = .off
    isTorchOn = false
    logInfo("🔦 手电筒已关闭", className: "ScannerViewModel")
} else {
    // 打开手电筒
    try device.setTorchModeOn(level: 1.0)
    isTorchOn = true
    logInfo("🔦 手电筒已打开", className: "ScannerViewModel")
}
```

## 📊 功能总结

通过这次修改，我们成功添加了手电筒控制功能：

1. **功能完整**: 支持手电筒的开启、关闭和状态切换
2. **界面友好**: 直观的图标和颜色状态指示
3. **安全可靠**: 完整的错误处理和资源管理
4. **用户体验**: 触觉反馈和智能显示逻辑
5. **稳定性**: 修复了关闭手电筒时的崩溃bug

手电筒功能将显著提升用户在低光环境下的扫描体验，特别是在夜间或光线不足的室内环境中。🎉 