# mas-code-scan iOS 端支持方案 > 文档状态:方案设计,尚未开始 iOS 原生实现。 > > 目标是在不改变现有业务调用方式的前提下,为 `mas-code-scan` 增加 iOS App 支持,并与 Android 端尽量保持相同的功能、参数语义、回调顺序和错误码。 --- ## 1. 背景与目标 当前 `mas-code-scan` 是 Android UTS API 插件,Android 原生层基于 CameraX + Google ML Kit 实现。对外 API 已沉淀在 `utssdk/interface.uts`,业务页面通过 `openCodeScan` 调用,因此具备扩展 iOS 实现的良好基础。 ### 1.1 当前状态 | 项目 | 当前情况 | | --- | --- | | 对外 API | `openCodeScan(options)` | | 公共类型 | `CodeScanOptions`、`CodeScanSuccess`、`CodeScanFail`、`ScanRegion`、`CodeScanStyle` | | Android 实现 | `utssdk/app-android/`,CameraX + Google ML Kit | | iOS 实现 | 暂无 `utssdk/app-ios/` 目录 | | 业务调用 | `pages/scan/prototype.vue` 只需调用统一 API | | 当前支持平台 | Android App(uni-app Vue2/Vue3) | ### 1.2 建设目标 1. **API 不变**:iOS 与 Android 均使用 `openCodeScan`,业务页面不增加平台分支。 2. **回调一致**:统一 `success`、`fail`、`complete` 的数据结构和触发时机。 3. **优先原生能力**:使用 iOS 系统框架,尽量不新增第三方识别库。 4. **功能分阶段交付**:先完成可真机使用的最小链路,再逐步补齐 Android 已有能力。 5. **平台差异显式处理**:对系统不支持或实现成本较高的能力,明确降级或延后,不以静默不生效代替。 ### 1.3 非目标 本期方案不包含: - 鸿蒙、H5、小程序、nvue 支持; - 修改 Android 识别链路; - 将扫码页改造为 Vue 组件; - 为尚未经过真机验证的 iOS 能力提前修改 README 平台声明。 --- ## 2. 总体架构 ### 2.1 目录规划 在保持现有 Android 目录不变的基础上新增 iOS 实现: ```text frontend/scanapp/uni_modules/mas-code-scan/ ├── package.json ├── readme.md ├── changelog.md └── utssdk/ ├── interface.uts # Android / iOS 共用公开类型 ├── unierror.uts # Android / iOS 共用错误对象 ├── app-android/ # 现有 Android 实现 │ ├── index.uts │ ├── CodeCaptureActivity.kt │ ├── MultiCodePickView.kt │ └── ScanFrameDecorView.kt └── app-ios/ # 新增 iOS 实现 ├── index.uts # UTS 桥接与回调编排 ├── CodeCaptureViewController.swift ├── CodeScanner.swift ├── ScanOverlayView.swift ├── MultiCodePickView.swift ├── ScanStyleParser.swift └── Info.plist # 或按 UTS iOS 配置规范声明权限 ``` > `app-ios` 的最终配置文件和 Swift 文件参与编译方式,必须以当前 HBuilderX 版本的 UTS iOS 官方规范为准;开始编码前需要用官方最小示例验证目录结构。 ### 2.2 分层职责 ```text 业务页面 ↓ openCodeScan(options) UTS 平台入口(app-android/index.uts 或 app-ios/index.uts) ↓ 参数转换 / 生命周期 / 回调派发 原生扫码控制器 ↓ 相机、识别、相册、交互 原生绘制层 ↓ 扫描框、扫描线、多码点选、呼吸动画 ``` - `interface.uts`:作为跨端 API 契约,不应写入 Android 或 iOS 私有字段。 - `app-ios/index.uts`:负责将 `CodeScanOptions` 转为原生配置,处理单次与连续扫码回调。 - `CodeCaptureViewController`:负责页面展示、权限、相机生命周期、工具栏和结果模式。 - `CodeScanner`:负责相机帧分析、Vision 识别、格式过滤、区域过滤和缩放策略。 - `ScanOverlayView`:负责 `scanStyle` 与 `scanRegion` 的视觉绘制。 - `MultiCodePickView`:负责多码定格、点选和绿色呼吸波纹。 --- ## 3. iOS 技术选型 | 能力 | 推荐技术 | 选择理由 | | --- | --- | --- | | 相机预览与帧采集 | `AVFoundation` | 系统原生、稳定、无需新增依赖 | | 条码识别 | `Vision` 的 `VNDetectBarcodesRequest` | 覆盖常见二维码与条形码,识别结果可获取码制和边界框 | | 相册选图 | `PHPickerViewController` | iOS 14+ 可按用户选择读取图片,通常无需完整相册权限 | | 低版本相册兼容 | `UIImagePickerController` | 仅在确认最低系统版本低于 iOS 14 时补充 | | 扫描线动画 | `CAShapeLayer` + `CABasicAnimation` | 使用 Core Animation,避免定时器逐帧重绘 | | 多码呼吸动画 | `CAShapeLayer` + `CABasicAnimation` | 与 Android 绿点呼吸波纹实现保持视觉一致 | | 手势缩放 | `UIPinchGestureRecognizer` | 对应 Android 双指捏合 | | 震动反馈 | `UIImpactFeedbackGenerator` | 适配 iOS 系统触感反馈 | ### 3.1 不引入第三方扫码库 首版不建议接入 ZXing、ML Kit iOS 等第三方库,原因: - 系统 `Vision` 已能覆盖主要码制; - 避免 CocoaPods/SPM 与 UTS iOS 构建的额外集成风险; - 减少安装包和维护成本; - 便于优先验证 UTS + Swift 桥接与相机链路。 若后续测试确认某些业务关键码制在 `Vision` 上识别率不足,再单独评估第三方引擎,不在首版预置依赖。 --- ## 4. 公共 API 与行为约束 ### 4.1 保持现有 API 现有 `CodeScanOptions` 不新增必填字段,iOS 端识别并实现以下参数: | 参数 | iOS 首版目标 | 说明 | | --- | --- | --- | | `formats` | 支持 | 映射到 Vision 支持的 symbology;不支持值忽略 | | `vibrate` | 支持 | 识别成功后触发轻触感反馈 | | `fromAlbum` | 支持 | 打开扫码页后直接进入相册选择 | | `scanRegion` | 支持 | 控制实时识别区域与扫描框;相册识图不受限制 | | `scanStyle` | 支持 | 控制遮罩、边框、扫描线及动画 | | `resultMode` | 支持 | `immediate`、`confirm`、`pause` | | `continuous` | 支持 | 多次 `success`,关闭时 `complete` | | `showPauseButton` | 支持 | 仅连续扫码时展示暂停/继续控制 | | `torch: 'off'` | 支持 | 默认关闭 | | `torch: 'on'` | 支持 | 设备支持时尝试开启 | | `smartZoom` | 第二阶段 | 首版可读取但不自动变焦,后续补齐 | | `torch: 'auto'` | 第二阶段 | 需要基于视频帧亮度策略验证 | ### 4.2 成功数据 iOS 识别结果必须统一转换为: ```ts { content: string, format: string, fromAlbum: boolean } ``` 格式名称与 Android 对齐,至少包括: ```text QR_CODE EAN_13 EAN_8 UPC_A UPC_E CODE_128 CODE_39 CODE_93 ITF CODABAR PDF417 AZTEC DATA_MATRIX ``` 当 Vision 不支持某个 Android 已支持的格式时: 1. `formats` 中的该值不参与 Vision 请求; 2. 不因单个非法或不支持值使整个调用失败; 3. 若过滤后没有任何 iOS 支持的格式,可按全格式或返回 `9021004`,最终策略需在编码前固定,并同步文档; 4. 推荐与 Android 保持一致:过滤为空时退回全格式识别。 ### 4.3 回调一致性 | 场景 | `success` | `fail` | `complete` | | --- | --- | --- | --- | | 单次扫码成功 | 调用一次 | 不调用 | 紧随成功调用一次,参数为成功结果 | | 单次取消/关闭 | 不调用 | `9021003` | 紧随失败调用一次 | | 相机权限拒绝 | 不调用 | `9021002` | 紧随失败调用一次 | | 无有效扫码结果的异常结束 | 不调用 | `9021004` | 紧随失败调用一次 | | 连续扫码单次命中 | 每次命中调用 | 不调用 | 页面未关闭前不调用 | | 连续扫码关闭且已有命中 | 不再调用 | 不调用 | `complete({ closed: true, hitCount })` | | 连续扫码关闭且无命中 | 不调用 | `9021003` | 紧随失败调用一次 | 所有业务回调必须在主线程派发,控制器关闭、权限回调和异步识别完成之间要有会话状态保护,确保不会重复回调。 ### 4.4 错误码复用 继续使用 `unierror.uts` 中的错误码: | 错误码 | 语义 | iOS 对应场景 | | --- | --- | --- | | `9021001` | 当前无法获取页面 Activity | 无法获取当前 `UIViewController` 或不能展示扫码页 | | `9021002` | 相机权限被拒绝 | `.denied` / `.restricted` 或用户拒绝授权 | | `9021003` | 用户取消扫码 | 点击关闭、系统返回或取消图片选择 | | `9021004` | 未获取到有效的扫码结果 | 扫码处理异常结束、相册图片无有效条码后由调用方选择结束 | --- ## 5. 核心功能设计 ### 5.1 相机权限与页面生命周期 流程: ```text openCodeScan ↓ 获取当前 UIViewController ↓ 检查 AVCaptureDevice 相机权限 ├─ 已授权 → 初始化扫码控制器与会话 ├─ 未决定 → 请求授权 → 成功后初始化 └─ 拒绝/受限 → fail(9021002) → complete ``` 控制器生命周期要求: - 页面展示时创建并启动 `AVCaptureSession`; - 页面进入后台时暂停会话; - 页面恢复前台时,未完成且未暂停时恢复会话; - 页面关闭、成功返回或发生不可恢复错误时停止会话; - 释放 `AVCaptureVideoDataOutput` 委托、图片引用、动画图层和 UTS 回调引用; - 防止异步 Vision 回调在页面关闭后再提交结果。 ### 5.2 实时扫码链路 ```text AVCaptureVideoDataOutput ↓ CMSampleBuffer Vision VNDetectBarcodesRequest ↓ VNBarcodeObservation 列表 formats 过滤 ↓ scanRegion 区域过滤 ↓ 单码 / 多码 / 连续扫码 / 结果模式处理 ``` 性能策略: - 使用串行 `DispatchQueue` 处理 Vision 请求,避免同一时间处理多帧; - 在上一帧识别尚未结束时丢弃新帧; - 多码定格、确认弹窗、暂停状态、页面关闭后停止提交识别请求; - 连续扫码需按 `content + format` 做短时间去重,建议冷却窗口 `800ms ~ 1200ms`; - 不在 Vision 回调线程直接更新 UIKit,统一回主线程更新界面。 ### 5.3 formats 映射 新增 iOS 私有映射表: ```text QR_CODE → VNBarcodeSymbologyQR EAN_13 → VNBarcodeSymbologyEAN13 EAN_8 → VNBarcodeSymbologyEAN8 UPC_A → VNBarcodeSymbologyUPCE(需以实际 Vision 结果确认映射) UPC_E → VNBarcodeSymbologyUPCE CODE_128 → VNBarcodeSymbologyCode128 CODE_39 → VNBarcodeSymbologyCode39 CODE_93 → VNBarcodeSymbologyCode93 ITF → VNBarcodeSymbologyI2of5 CODABAR → VNBarcodeSymbologyCodabar PDF417 → VNBarcodeSymbologyPDF417 AZTEC → VNBarcodeSymbologyAztec DATA_MATRIX → VNBarcodeSymbologyDataMatrix ``` > `VNBarcodeSymbology` 的实际可用范围与命名随 iOS SDK 变化。开发时必须以目标 Xcode / iOS SDK 编译结果为准,不能假定每个 Android 格式都一一对应。 ### 5.4 scanRegion 坐标转换与过滤 `scanRegion` 是业务 API 中最容易出现跨平台差异的部分,iOS 必须确保“画面上显示的扫描框”与“实际允许识别的区域”一致。 处理步骤: 1. 根据 `width`、`height`、`centerX`、`centerY` 计算预览层的扫描框 `CGRect`; 2. 使用 `AVCaptureVideoPreviewLayer` 将 Vision 的归一化 `boundingBox` 转换为 UIKit 坐标; 3. 判断条码边界框中心点是否在扫描框中; 4. 仅保留区域内条码; 5. `fullScreen: true` 时不绘制扫描框且不进行区域过滤; 6. 相册识图跳过 `scanRegion` 过滤,保持 Android 行为。 验证重点: - Vision 坐标原点与 UIKit 坐标原点不同; - `.resizeAspectFill` 会产生裁切偏移; - 前置镜像、横竖屏和刘海安全区可能影响坐标; - 多码点选标记必须与真实条码中心重合。 ### 5.5 scanStyle 绘制 `ScanOverlayView` 使用 `CAShapeLayer` 与 `CAGradientLayer` 绘制,配置语义与 Android 一致: | 参数 | iOS 实现 | | --- | --- | | `showMask` | 显示/隐藏扫描框外遮罩层 | | `showBeam` | 显示/隐藏扫描线图层 | | `borderColor` | `CGColor` 边框颜色 | | `maskColor` | 解析 `#RRGGBB`、`#AARRGGBB`、`rgba()` 后填充遮罩 | | `beamColor` | 扫描线颜色 | | `beamDuration` | 扫描线 `CABasicAnimation.duration`,限制 `100 ~ 60000ms` | | `cornerStyle: 'corner'` | 绘制四角边框 | | `cornerStyle: 'full'` | 绘制完整矩形边框 | 默认值必须沿用当前接口默认行为: ```ts { showMask: true, showBeam: true, borderColor: '#19be6b', maskColor: 'rgba(0, 0, 0, 0.45)', beamColor: '#12e880', beamDuration: 1800, cornerStyle: 'corner' } ``` 颜色解析建议单独实现 `ScanStyleParser`: - 支持 `#RRGGBB`; - 支持 `#AARRGGBB`; - 支持 `rgba(r,g,b,a)`; - 解析失败时回退默认色; - 所有回退规则与 Android `readScanStyle()` 保持一致。 ### 5.6 单码、多码和点选 #### 单码 单个有效条码按 `resultMode` 处理: - `immediate`:停止识别并立即返回; - `confirm`:暂停识别,弹出确认框; - `pause`:暂停识别,显示继续扫描和确认返回操作。 #### 多码 同一帧出现两个及以上有效、有内容且有边界框的条码时: 1. 停止实时识别; 2. 抓取当前预览帧; 3. 显示定格画面和半透明遮罩; 4. 把各码的边界框映射到屏幕; 5. 在中心位置绘制点选标记; 6. 显示“已识别到多个码,请选择一个”提示; 7. 用户点击最近标记后提交对应条码结果。 `MultiCodePickView` 的标记视觉与 Android 对齐: ```text 绿色中心圆点 + 白色圆形底 + 绿色半透明外圈 + 周期性呼吸扩散动画 ``` 建议使用 `CAKeyframeAnimation` 或 `CABasicAnimation` 同步变化外圈的 `transform.scale` 与 `opacity`,周期暂定 `1500ms`。 点击策略: - 以点位中心为基准选择最近候选; - 设置最小可点击半径,建议约 `28pt`; - 无命中点时继续等待选择,不直接取消; - 用户主动关闭时按取消流程处理。 ### 5.7 相册识图 优先采用 `PHPickerViewController`: ```text fromAlbum: true 或点击相册按钮 ↓ 系统图片选择器 ↓ 获取 UIImage / CGImage ↓ Vision VNDetectBarcodesRequest ↓ 单码直接返回;多码进入点选 ``` 规则: - 相册识图结果的 `fromAlbum` 固定为 `true`; - 相册图片不受 `scanRegion` 限制; - 用户取消图片选择:单次扫码按 `9021003` 处理; - 图片中未识别到条码时,提示“未识别到有效条码”,并返回扫码页继续操作; - 多码相册图片使用图片本身作为定格背景并进入点选。 ### 5.8 连续扫码与暂停 `continuous: true` 时: - 命中一次条码调用一次 `success`; - 扫码页不关闭; - 同一内容需通过短时冷却避免高频重复提交; - `showPauseButton: true` 时展示暂停/继续按钮; - 用户关闭时,若已有命中,则仅调用: ```ts complete({ closed: true, hitCount }) ``` - 用户关闭时,若没有命中,则按 `fail(9021003)` 后 `complete`; - 多码点选确认后,在连续模式下应投递一次成功结果、清除定格层、恢复相机识别,而不是关闭控制器。 ### 5.9 手电筒、缩放与震动 #### 手电筒 - 使用 `AVCaptureDevice.hasTorch` 判断设备能力; - 使用 `lockForConfiguration()` + `torchMode` 变更状态; - `off`:默认关闭; - `on`:启动后尝试开启; - 无手电筒设备隐藏或禁用页面按钮; - 页面结束时关闭手电筒。 #### 手势缩放 - 使用 `UIPinchGestureRecognizer`; - 映射至 `videoZoomFactor`; - 限制在 `1.0 ... activeFormat.videoMaxZoomFactor` 的安全范围; - 变焦期间禁止智能变焦抢占。 #### 智能变焦 作为第二阶段能力: - 根据条码边界框占画面比例计算建议倍率; - 设定最大尝试次数、最大倍率和平滑过渡; - 检测到多个码时先恢复 `1.0x`,避免多码点选区域被放大裁切; - 与手势缩放、连续扫码、定格状态互斥。 #### 震动 - `vibrate: true` 时使用 `UIImpactFeedbackGenerator(style: .light)`; - 连续扫码每次实际成功投递时触发; - 系统可能因“减少动态效果”、静音模式或设备策略弱化触感反馈,业务层不应依赖其作为唯一成功提示。 --- ## 6. UTS 桥接设计 ### 6.1 app-ios/index.uts 职责 `app-ios/index.uts` 负责: 1. 获取可展示扫码页的当前 `UIViewController`; 2. 校验当前是否已有扫码会话,避免重复打开; 3. 归一化 `CodeScanOptions` 默认值; 4. 创建并展示 `CodeCaptureViewController`; 5. 接收原生事件并转换为 `CodeScanSuccess` / `CodeScanFailImpl`; 6. 保证回调顺序和只执行一次的终止语义; 7. 连续扫码时维护 `hitCount`; 8. 控制器退出后解除事件与对象引用。 ### 6.2 建议的原生事件模型 控制器向桥接层输出有限事件,不直接感知 UTS 回调: ```text onResult(content, format, fromAlbum) onCancel() onPermissionDenied() onFatalError() onClosed(hitCount, hadHits) ``` 桥接层统一决定: - 单次成功:`success` → `complete`; - 单次失败:`fail` → `complete`; - 连续扫码:每次 `onResult` → `success`,关闭时再 `complete`; - 已结束会话的后续事件全部丢弃。 ### 6.3 会话状态 建议 iOS 桥接层维护: ```text idle → presenting → scanning → paused / picking → finishing → finished ``` 关键约束: - `finished` 后不允许再次派发回调; - 权限请求、Vision 回调、相册回调都需检查当前状态; - 控制器 dismissal 完成后再释放强引用; - 业务层再次调用时必须创建新会话,不复用旧回调。 --- ## 7. 权限与配置 ### 7.1 必需权限 iOS 必须声明: ```xml NSCameraUsageDescription 需要使用相机扫描二维码和条形码 ``` 若采用 `PHPickerViewController` 且最低系统版本为 iOS 14,一般不需要完整相册访问权限。 若最终需要 `UIImagePickerController` 或直接读取照片库,则补充: ```xml NSPhotoLibraryUsageDescription 需要访问相册以识别图片中的二维码和条形码 ``` ### 7.2 最低系统版本建议 建议首版目标: ```text iOS 14+ ``` 理由: - 可以使用 `PHPickerViewController`; - Vision 条码能力更成熟; - 降低图片权限和兼容逻辑复杂度。 若产品必须支持 iOS 13,需要增加相册选择兼容分支,并重新评估 Vision 可识别格式集合。 ### 7.3 打包与调试 iOS 原生 UTS 代码不能通过 Web 预览验证,必须使用: - 包含插件的 iOS 自定义调试基座;或 - iOS 云打包 / 本地原生打包产物; - 真机设备与 Xcode 控制台。 模拟器不应作为相机扫码主验收环境;可用于页面布局、权限分支和相册图片识别的部分验证。 --- ## 8. 分阶段实施计划 ### 阶段 0:UTS iOS 可编译性验证 目标:确认当前 HBuilderX 与项目配置可编译 `app-ios` 原生实现。 任务: - 创建最小 `app-ios/index.uts`; - 验证获取当前 iOS 控制器与展示原生页面的官方方式; - 验证 Swift 文件参与 UTS iOS 编译的目录和配置; - 在 iPhone 真机执行一个最小“打开原生页并返回”示例; - 固化项目实际约定后再开始完整扫码实现。 验收:iOS 自定义基座可编译并成功打开、关闭原生测试页面。 ### 阶段 1:最小可用扫码 范围: - 相机权限; - 后置相机预览; - Vision 单码实时识别; - `success`、`fail`、`complete`; - 用户关闭; - 常见二维码与条形码格式。 验收:iPhone 真机可扫描 QR Code 和 Code 128,并按既定顺序回调。 ### 阶段 2:跨端核心功能对齐 范围: - `formats`; - `scanRegion`; - `scanStyle`; - 相册识图; - 多码定格点选; - 绿色呼吸波纹; - `resultMode`; - `vibrate`。 验收:Android 与 iOS 对同一配置的显示、区域过滤和数据结构一致。 ### 阶段 3:会话与相机增强 范围: - `continuous`; - `showPauseButton`; - `torch: 'off' | 'on'`; - 手势缩放; - 连续扫码去重与关闭结算。 验收:连续扫码有命中和无命中关闭时的回调行为与 Android 一致。 ### 阶段 4:高级策略 范围: - `smartZoom`; - `torch: 'auto'`; - 复杂机型上的性能优化; - 必要时评估第三方识别引擎。 验收:在目标机型上无明显误触发、频繁跳变、发热或回调重复问题。 --- ## 9. 验收矩阵 ### 9.1 功能验收 | 场景 | 预期 | | --- | --- | | 单个二维码 | 返回 `QR_CODE`、`fromAlbum: false` | | 单个 Code 128 | 返回 `CODE_128` | | formats 过滤 | 仅接受指定码制 | | 全屏识别 | 不显示扫描框,整画面可识别 | | 自定义区域 | 框外条码不返回,框内条码正常返回 | | 相册单码 | 返回 `fromAlbum: true` | | 相册多码 | 显示定格图片与多个可点击标记 | | 实时多码 | 显示定格画面、提示语和绿色呼吸点 | | confirm 模式 | 确认后返回,取消后继续识别 | | pause 模式 | 可继续扫描或确认结果 | | 连续扫码 | 多次 success,关闭后返回正确 hitCount | | 连续扫码无命中关闭 | fail `9021003` 后 complete | | 相机权限拒绝 | fail `9021002` 后 complete | | 无手电筒设备 | 不崩溃,按钮隐藏或禁用 | ### 9.2 机型与系统验收 至少覆盖: - iPhone 小屏机型; - 刘海屏机型; - 灵动岛机型; - 无手电筒或手电筒不可用场景; - iOS 14、16、17 及项目实际要求的最新系统; - 深色模式; - 权限首次授权、拒绝、设置页重新授权; - 前后台切换、来电或控制中心打断后恢复; - 快速打开、关闭、再次打开扫码页。 ### 9.3 性能与稳定性验收 - 连续识别 5 分钟不出现明显内存持续增长; - 多次进入、退出扫码页后相机可再次正常打开; - 多码定格帧不会造成明显卡顿或黑屏; - 页面关闭后不再收到 `success`; - 不发生重复 `complete` 或 success/fail 同时触发; - 扫描框与实际识别区域、点选标记位置对齐。 --- ## 10. 风险与应对 | 风险 | 影响 | 应对措施 | | --- | --- | --- | | UTS iOS 原生目录/Swift 桥接规则与预期不同 | 无法编译或调用 | 阶段 0 使用官方最小示例先验证,不直接大规模开发 | | Vision 支持的码制与 Android 不完全一致 | formats 行为差异 | 建立格式能力矩阵,过滤不支持项并在 README 明示 | | Vision 到预览层坐标转换错误 | scanRegion、点选位置偏移 | 单独编写坐标转换测试清单,覆盖 Aspect Fill、旋转和安全区 | | 定格帧转换开销高 | 多码时卡顿、内存上涨 | 限制图像分辨率,及时释放 `CGImage` / `UIImage`,仅保留一帧 | | 异步回调竞争 | 重复回调、页面关闭后回调 | 使用会话状态机和主线程串行化终止事件 | | Torch / zoom 在机型上差异大 | 功能不稳定 | 将 `auto` 与 smartZoom 放到后续阶段,先实现基础能力 | | iOS 权限文案或 Info.plist 缺失 | 审核或运行时崩溃 | 将权限配置纳入构建前检查和真机回归 | --- ## 11. 代码与文档更新清单 ### 11.1 开发完成后需要修改 | 文件 | 修改内容 | | --- | --- | | `utssdk/app-ios/*` | 新增 iOS UTS 桥接和 Swift 原生实现 | | `package.json` | `displayName`、描述、关键字、`platforms.client.uni-app.app.ios` 改为支持 | | `readme.md` | 支持平台改为 Android / iOS;补充 iOS 权限、最低版本和格式差异说明 | | `changelog.md` | 记录 iOS 首次支持及功能范围 | | `pages/scan/prototype.vue` | 将 Android 专属提示改为 Android / iOS App 提示 | ### 11.2 开发完成前不得修改 以下内容必须等 iOS 自定义基座成功编译且完成真机验收后再调整: - README 的“支持 iOS”声明; - `package.json` 中的 `ios: '√'`; - 业务页面中“Android 专属”的运行提示; - changelog 中的正式 iOS 发布记录。 --- ## 12. 推荐实施顺序 1. 确认项目的 HBuilderX 版本、Xcode 版本和最低 iOS 版本; 2. 用官方 UTS iOS 最小原生页面验证 `app-ios` 目录与 Swift 编译方式; 3. 实现相机权限 + 单码 QR 实时识别闭环; 4. 固化 `success` / `fail` / `complete` 的 iOS 回调行为; 5. 加入 formats、scanRegion 和 scanStyle; 6. 加入相册识图和多码定格点选; 7. 加入连续扫码、暂停、手电筒和手势缩放; 8. 最后实现 smartZoom、auto torch 等设备敏感能力; 9. 真机回归通过后更新插件元信息、README、changelog 和演示页文案。 --- ## 13. 结论 推荐采用 **AVFoundation + Vision + PHPickerViewController** 的原生 iOS 实现方案,并在 `utssdk/app-ios/` 中增加独立平台入口。现有 `interface.uts`、`unierror.uts` 和 `openCodeScan` 调用方式继续作为跨端契约,Android 代码无需重构。 实施上应先完成 iOS UTS 编译与原生页面呈现验证,再逐步建立单码扫码、多码点选、相册识图和连续扫码能力。这样能将最大的构建与坐标转换风险前置,同时避免在未验证前修改对外平台支持声明。