最近在做的一个项目里面包含了扫码逻辑,因为之前公司的一些项目用的是旧的第三方框架,准确性和速度都与原生存在一定的差距,界面可变性局限大,维护成本高,不能忍😤,所以自己用原生 API 重写了一个扫码模块,Github 具体代码可点击此处。官方的扫码 API 是在 iOS8 上推出的,如今系统迭代到了 iOS10,iOS8 以下系统的市场占有率已经可以忽略,并且 APP 适配现在也是从 iOS8 开始。
实现方法
这里使用 ViewController 进行实现,之前考虑过用 View,因为生命周期的那里坑比较多,为了方便管理生命周期所以采用了 ViewController 进行实现。
工程设置
使用 JBScanViewController,项目工程 info.plist 需要添加相机、相册权限 (适配 iOS 10) 和将系统 StatusBar 的优先级调低,否则在相册选择图片进行裁切时会有 20PX 的下沉。
原生 API 的调用
头文件需要用到 AVFoundation 框架和 MobileCoreServices,相机的操作依赖 AVFoundation,MobileCoreServices 则是在约束相册筛选类型时使用。
ViewController 需要遵循AVCaptureMetadataOutputObjectsDelegate
、UINavigationControllerDelegate
、UIImagePickerControllerDelegate
三个代理,AVCaptureMetadataOutputObjectsDelegate 是接受相机输入流,其它两个则是调用系统相册。
首先需要声明中间 SessionAVCaptureSession * session; //输入输出的中间桥梁
进行初始化摄像设备和扫码行为
1 | //获取摄像设备 |
扫码范围这里要计算好,特别需要注意的是此处坐标与正常 CGRect 不同,此处是(Y,X,HEIGHT,WIDTH)
并且全部按比例 (0-1) 设定,设定了扫码范围后系统只会判断该范围内的数据,这样能显著仅提高效率和成功率,JBScanViewController 设定了一些宏参数以灵活改变范围大小和偏移量可根据需求进行修改
1 |
执行完 startRunning 之后就开始进行扫描了,但是这里会涉及到生命周期的一些问题,如果将这一段代码在ViewDidLoad
方法里面进行初始化则会有 0.5-1 秒的延时,因为加载相机会消耗大量的时间,所以界面会等到相机加载完之后才 Push,为了不产生卡顿感,这里做法是将初始化方法放到ViewDidAppear
方法中进行,先将页面 Push,再初始化相机,这样会非常流畅,但是先 push 过来回有黑屏的界面,因为相机还在启动过程中,所以加上一层 UI 引导用户进行等待。
1 | //启动提示和loading菊花 |
得到扫描结果之后会调用以下两个回调
1 |
|
这里得到的结果会返回到我封装的两个方法中,每得到一次结果会同时调用这两个方法,可在里面进行数据操作
1 |
|
UI 界面与动画
扫描框蒙版 UI 和动画
因为考虑到可集成性,JBScanViewController 使用纯代码进行 UI 构建,主要使用贝塞尔曲线在 Layer 层进行动画。首先需要在相机层上方进行一层蒙版处理
1 | //扫描框 |
将蒙版通过贝塞尔矩形路径镂空,通过控制蒙版 Layer 的 Mask 属性加上CABasicAnimation
的 Path 动画使蒙版出现生成动画
1 | /** 加载扫描框生成动画 */ |
扫描框四角 UI 和动画
这时蒙版生成的动画已经完成,我们需要的还有边框与扫描线的 UI 和动画,将扫描框拆分为四个角进行绘制,每个角的绘制方向按顺时针绘制,并且 X=Y = 边框边长乘以边长与 X 的比例,所以当我们更改比例至 0.5 时就能实现全边框包裹。
扫描框初始值(原点值)
1 | //扫描框 |
扫描框目标值
1 | /** |
扫描线 UI 和动画
最后加上扫描线的 UI 和动画就能完成了,扫描线需要用到一个矩形 Layer 层加上渐变色,然后将其 Mask 属性添加椭圆 Path 遮罩,最后生成扫描线边缘半透明的效果,因为渐变层 Frame 属性不能添加动画,所以将动画添加到 Position 属性,通过改变起始点和终止点来完成从无到有的动画。扫描动画同理改变其 Position 的高度值,但是要特别注意removedOnCompletion
属性,否则按 home 键退出时会停止扫描动画。
1 | /** |
其它方法
提供了相册读取方法和闪光灯开启 / 关闭方法,可自行调用
1 |
|
JBScanViewController 大部分代码实现的功能是 UI 和动画,对整体的时长和美观度进行了考量,同时兼顾性能和界面流畅度,也抽离出常用属性以适应不同的项目需求。具体使用可下载 Demo 进行了解。