更新记录

1.1.1(2026-08-09)

修复已知错误

1.1.0(2026-08-09)

增加图片返回和参数配置

1.0.1(2026-08-08)

更新Info.plist

查看更多

平台兼容性

uni-app(5.0)

Vue2 Vue3 Chrome Safari app-vue app-vue插件版本 app-nvue Android iOS iOS插件版本 鸿蒙
- - - - 1.0.0 - - 18 1.0.0 -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - - -

uni-app x(5.0)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

miko-scanqrcode

UTS 插件 — SwiftUI 实现的二维码扫描器(iOS 18+),支持自定义 UI 配置与快照返回。


功能特性

  • 🚀 SwiftUI 原生实现 — iOS 18+,单 / 多二维码识别
  • 🎨 9 个可配置项 — 按钮显隐、动效开关、圆点颜色、延迟启动、提示音、震动等
  • 📷 快照双返回 — 同时返回 JPEG Base64(兼容)与本地文件绝对路径(更轻量)
  • 🖼 相册识别 — 从系统相册选图识别二维码
  • 🔦 手电筒 + 翻转摄像头 — 顶部 / 底部按钮栏
  • 多码选择 — 检测到多个二维码时显示彩色圆点,点击选择
  • 🔊 独立反馈开关 — 提示音与震动是两个独立开关,可单独控制

安装

本插件是 uni_modules 模块,直接放在项目的 uni_modules/miko-scanqrcode/ 目录即可。

uni_modules/
└── miko-scanqrcode/
    ├── package.json
    ├── readme.md
    └── utssdk/
        ├── interface.uts          # UTS 类型定义
        ├── unierror.uts           # 错误码定义
        └── app-ios/
            ├── ScannerView.swift      # SwiftUI 扫码主视图
            ├── ScannerController.swift # AVCaptureSession 控制器
            ├── ScanQRBridge.swift     # UTS ↔ Swift 桥接(含 DTO + 配置解析)
            └── index.uts              # iOS 端 UTS 调用入口

平台:目前仅 iOS(iOS 18+)。


快速开始

1. 最小化调用(使用全部默认配置)

import { scanQRCode } from "@/uni_modules/miko-scanqrcode";

scanQRCode({
  success: (res) => {
    console.log("二维码内容:", res.value);
    console.log("快照 Base64 长度:", res.imageBase64.length);
    console.log("快照本地路径:", res.imageURL);
  },
  fail: (err) => {
    console.log("失败:", err.code, err.message);
  },
  complete: () => {},
});

2. 自定义配置

import { scanQRCode } from "@/uni_modules/miko-scanqrcode";

scanQRCode({
  options: {
    // 9 个可选项,按需传(缺省时用默认值)
    showSwitchCamera: true,    // 显示翻转摄像头按钮
    showPhotoPicker: true,     // 显示从相册选择按钮
    showTorch: true,           // 显示手电筒按钮
    animationEnabled: true,    // 显示扫描线动效
    showQRCodeDot: true,       // 多码时显示彩色圆点
    dotColorHex: "#ff8800",    // 圆点颜色(HEX,可带 alpha)
    scanStartDelayMs: 800,     // 进入页面后延迟 800ms 再扫描
    soundEnabled: false,       // 关闭提示音
    vibrationEnabled: true,    // 开启震动
  },
  success: (res) => {
    // 处理结果...
  },
});

API 参考

scanQRCode(options: ScanQRCodeOptions): void

异步扫码方法,弹出全屏 SwiftUI 扫码页面,识别完成后自动关闭并通过回调返回结果。

ScanQRCodeOptions

字段 类型 必填 说明
options ScanQRCodeConfiguration UI 与行为配置,未传则用全部默认值
success (res: any) => void 识别成功回调,resMikoScanResultDTO
fail (err: any) => void 异常失败回调,errMikoScanErrorDTO
complete (res: any) => void 结束回调(success/fail 谁触发就传谁)

ScanQRCodeConfiguration(配置字典)

字段 类型 默认值 说明
showSwitchCamera boolean true 顶部右上:翻转前后摄像头按钮
showPhotoPicker boolean true 底部右:从系统相册选择图片识别
showTorch boolean true 底部中央:手电筒开关
animationEnabled boolean true 中央绿色扫描线上下循环动效
showQRCodeDot boolean true 检测到多个二维码时显示彩色圆点供点击选择
dotColorHex string "#00ff00" 圆点颜色(HEX,支持 #RRGGBB#RRGGBBAA# 可省略)
scanStartDelayMs number 0 进入页面后延迟启动扫描的毫秒数(0 = 立即)
soundEnabled boolean false 识别成功播放系统提示音(AudioServicesPlaySystemSound(1057)
vibrationEnabled boolean true 识别成功震动反馈(UIImpactFeedbackGenerator.medium

所有字段均为可选。Swift 端会以 ScannerConfiguration.default 兜底缺失字段,类型错误也会回退到默认值。


返回结果 MikoScanResultDTO

识别成功时传给 success / complete 的对象字段:

字段 类型 说明
value string 二维码内容(解码后的原始字符串)
imageBase64 string 冻结画面快照的 JPEG Base64(失败时为空字符串 ""
imageURL string 快照写入 tmp 目录后的绝对文件 URL(如 file:///var/.../scanqrcode_xxx.jpg),可直接赋给 <image :src="..."> 显示,省内存
width number 快照尺寸宽度(pt)
height number 快照尺寸高度(pt)
scale number 屏幕 scale(用于 1:1 像素换算)

两种显示快照的方式

// 方式 1:用 imageURL(推荐,文件已经在 tmp 里,<image> 直接读本地,加载快、内存小)
uni.previewImage({ urls: [res.imageURL] });

// 方式 2:用 imageBase64(兼容旧用法,SwiftUI Text(base64) 解析稍重)
const src = "data:image/jpeg;base64," + res.imageBase64;

imageURL 指向 NSTemporaryDirectory() 下,每次扫码会生成独立 UUID 文件名。若需长期保留,可读取后自行转存到持久目录。


错误返回 MikoScanErrorDTO

字段 类型 说明
code number 错误码(见下表)
message string 用户可读的错误描述
errStr string? 原始异常字符串(可选)

错误码

错误码 含义
9010001 无法获取当前页面(顶层 ViewController 不存在)
9010002 用户未授权相机权限(暂未使用,预留)
9010003 启动摄像头失败(暂未使用,预留)
9010004 用户主动取消(注意:当前 ScannerView 未提供 onCancel 回调,用户点返回时直接 dismiss,不会触发 fail 回调

完整示例

<template>
  <view>
    <button @click="onScan">扫码</button>
    <image v-if="result" :src="result.imageURL" mode="widthFix" />
  </view>
</template>

<script lang="uts">
import { scanQRCode } from "@/uni_modules/miko-scanqrcode";

export default {
  data() {
    return {
      result: null as any,
    };
  },
  methods: {
    onScan() {
      // 关闭提示音 / 手电筒,方便静默识别
      scanQRCode({
        options: {
          showTorch: false,
          soundEnabled: false,
          scanStartDelayMs: 600,
        },
        success: (res: any) => {
          this.result = res;
          console.log("识别到:", res.value);
        },
        fail: (err: any) => {
          uni.showToast({ title: err.message, icon: "none" });
        },
      });
    },
  },
};
</script>

实现说明

三层架构

┌─────────────────────────────────────────────────────────────┐
│ Vue / nvue 调用层(pages/index/index.vue)                  │
│   - 拿到 ScanQRCodeConfiguration 字典、success/fail/complete│
└───────────────────────────┬─────────────────────────────────┘
                            │ UTS (interface.uts + index.uts)
┌───────────────────────────▼─────────────────────────────────┐
│ UTS 桥接层(app-ios/index.uts)                              │
│   - 仅做透传:options 字典原样转 Swift,DTO 对象原样回传     │
│   - 不做字段拆解(UTS 的 any 在 Swift 是 Any,无法做下标)   │
└───────────────────────────┬─────────────────────────────────┘
                            │ @objc 暴露
┌───────────────────────────▼─────────────────────────────────┐
│ Swift 桥类层(ScanQRBridge.swift)                           │
│   - 解析 options → ScannerConfiguration                      │
│   - ScanResult → MikoScanResultDTO                           │
│   - UIHostingController 弹出 ScannerView                     │
└───────────────────────────┬─────────────────────────────────┘
                            │
┌───────────────────────────▼─────────────────────────────────┐
│ SwiftUI 视图层(ScannerView.swift + ScannerController.swift)│
│   - AVCaptureSession + 元数据识别                            │
│   - 多码稳定期、单码确认延迟                                 │
│   - 冻结画面快照写入 tmp                                     │
│   - SwiftUI 动画 / 手电筒 / 摄像头切换                       │
└─────────────────────────────────────────────────────────────┘

关键设计决策

  1. DTO 中转:UTS 的 any 在 Swift 端编译为 Any,既不下标也不能 cast 成自定义类型,因此 Swift 端必须用 @objc(XXXDTO) 把字段拍平到 NSObject,UTS 才能透传。
  2. 配置可缺省:UTS 侧不强制传 options,Swift 端 ScannerConfigurationParser 对每个 key 做"读取-校验-默认值兜底",类型错误也安全降级。
  3. 快照双返回imageBase64 兼容旧 API 调用,imageURL 让 Vue 端可以用本地文件 URL 直接渲染(性能更好、内存占用更小)。
  4. 快照写入 tmp:每次扫码生成独立 UUID 文件名,避免冲突;调用方若需长期保留,自行 readFile 转存。
  5. 节流 + 稳定期:识别逻辑做 400ms 节流 + 300ms 稳定期 + 500ms 单码确认,避免每帧抖动与误触发。

兼容性

  • iOS 18.0+
  • HBuilderX 3.6.8+
  • uni-app x 5.0+

许可

隐私、权限声明

1. 本插件需要申请的系统权限列表:

相机、相册权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。