├── 2018-03-02 15_03_28.gif ├── 2018-03-02 15_03_56.gif ├── LICENSE ├── README.md ├── ScanQRcodeDemo ├── ScanQRcode.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── wangshuanglong.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── wangshuanglong.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── ScanQRcode │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ ├── WSLCreateQRCodeController.h │ ├── WSLCreateQRCodeController.m │ ├── WSLNativeScanTool.h │ ├── WSLNativeScanTool.m │ ├── WSLScanView.h │ ├── WSLScanView.m │ ├── main.m │ ├── piao.png │ ├── scanFlashlight@2x.png │ ├── scanFlashlight@3x.png │ └── scanLine@2x.png ├── ScanQRcodeTests │ ├── Info.plist │ └── ScanQRcodeTests.m └── ScanQRcodeUITests │ ├── Info.plist │ └── ScanQRcodeUITests.m ├── WSLNativeScanTool.podspec └── WSLNativeScanTool ├── WSLNativeScanTool.h ├── WSLNativeScanTool.m ├── WSLScanView.h └── WSLScanView.m /2018-03-02 15_03_28.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/2018-03-02 15_03_28.gif -------------------------------------------------------------------------------- /2018-03-02 15_03_56.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/2018-03-02 15_03_56.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 王双龙 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WSLNativeScanTool 2 | 3 | 简书地址:https://www.jianshu.com/u/e15d1f644bea 4 | 5 | ![](https://img.shields.io/badge/license-MIT%20-green.svg) 6 | ![](https://img.shields.io/badge/pod-v1.0-brightgreen.svg) 7 | ![](https://img.shields.io/badge/platform-iOS-orange.svg) 8 | ![](https://img.shields.io/badge/support-iOS9.0-green.svg) 9 | ![](https://img.shields.io/badge/build-passing-green.svg) 10 | 11 | 12 | ![效果预览1.gif](http://upload-images.jianshu.io/upload_images/1708447-6f8d911290ccafb4.gif?imageMogr2/auto-orient/strip) ![效果预览2.gif](http://upload-images.jianshu.io/upload_images/1708447-70b61f5542fc07a1.gif?imageMogr2/auto-orient/strip) 13 | 14 | >功能描述:[WSLNativeScanTool](https://github.com/wslcmk/ScanQRcode.git)是在利用原生API的条件下封装的二维码扫描工具,支持二维码的扫描、识别图中二维码、生成自定义颜色和中心图标的二维码、监测环境亮度、打开闪光灯这些功能;[WSLScanView](https://github.com/wslcmk/ScanQRcode.git)是参照微信封装的一个扫一扫界面,支持线条颜色、大小、动画图片、矩形扫描框样式的自定义;这个示例本身就是仿照微信的扫一扫功能实现的。 15 | 16 | * 详细实现就不在此唠叨了,直接去看代码吧,注释详细是我的习惯😀→[WSLNativeScanTool](https://github.com/wslcmk/ScanQRcode.git) 17 | * 来看一下WSLNativeScanTool.h ,用法很明朗 18 | ``` 19 | @import UIKit; 20 | @import AVFoundation; 21 | 22 | #import 23 | 24 | /** 25 | 扫描完成的回调 26 | @param scanString 扫描出的字符串 27 | */ 28 | typedef void(^WSLScanFinishedBlock)( NSString * _Nullable scanString); 29 | 30 | /** 31 | 监听环境光感的回调 32 | @param brightness 亮度值 33 | */ 34 | typedef void(^WSLMonitorLightBlock)( float brightness); 35 | 36 | @interface WSLNativeScanTool : NSObject 37 | 38 | /** 39 | 扫描出结果后的回调 ,注意循环引用的问题 40 | */ 41 | @property (nonatomic, copy) WSLScanFinishedBlock _Nullable scanFinishedBlock; 42 | 43 | /** 44 | 监听环境光感的回调,如果 != nil 表示开启监测环境亮度功能 45 | */ 46 | @property (nonatomic, copy) WSLMonitorLightBlock _Nullable monitorLightBlock; 47 | 48 | /** 49 | 闪光灯的状态,不需要设置,仅供外边判断状态使用 50 | */ 51 | @property (nonatomic, assign) BOOL flashOpen; 52 | 53 | /** 54 | 初始化 扫描工具 55 | @param preview 展示输出流的视图 56 | @param scanFrame 扫描中心识别区域范围 57 | */ 58 | - (instancetype )initWithPreview:(UIView *)preview andScanFrame:(CGRect)scanFrame; 59 | 60 | /** 61 | 闪光灯开关 62 | */ 63 | - (void)openFlashSwitch:(BOOL)open; 64 | 65 | - (void)sessionStartRunning; 66 | 67 | - (void)sessionStopRunning; 68 | 69 | /** 70 | 识别图中二维码 71 | */ 72 | - (void)scanImageQRCode:(UIImage *_Nullable)imageCode; 73 | 74 | /** 75 | 生成自定义样式二维码 76 | 注意:有些颜色结合生成的二维码识别不了 77 | @param codeString 字符串 78 | @param size 大小 79 | @param backColor 背景色 80 | @param frontColor 前景色 81 | @param centerImage 中心图片 82 | @return image二维码 83 | */ 84 | + (UIImage *)createQRCodeImageWithString:(nonnull NSString *)codeString andSize:(CGSize)size andBackColor:(nullable UIColor *)backColor andFrontColor:(nullable UIColor *)frontColor andCenterImage:(nullable UIImage *)centerImage; 85 | 86 | ``` 87 | * 再来看一下WSLScanView.h,用法也明朗😁 88 | 89 | ``` 90 | // 91 | // WSLScanView.h 92 | // ScanQRcode 93 | // 94 | // Created by 王双龙 on 2018/2/28. 95 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea 96 | All rights reserved. 97 | // 98 | 99 | #import 100 | 101 | typedef void(^WSLMyQRCodeBlock)(void); 102 | 103 | typedef void(^WSLFlashSwitchBlock)(BOOL open); 104 | 105 | @interface WSLScanView : UIView 106 | 107 | /** 108 | 点击我的二维码的回调 109 | */ 110 | @property (nonatomic,copy) WSLMyQRCodeBlock myQRCodeBlock; 111 | 112 | /** 113 | 打开/关闭闪光灯的回调 114 | */ 115 | @property (nonatomic,copy) WSLFlashSwitchBlock flashSwitchBlock; 116 | 117 | #pragma mark - 扫码区域 118 | 119 | /** 120 | 扫码区域 默认为正方形,x = 60, y = 100 121 | */ 122 | @property (nonatomic,assign)CGRect scanRetangleRect; 123 | /** 124 | @brief 是否需要绘制扫码矩形框,默认YES 125 | */ 126 | @property (nonatomic, assign) BOOL isNeedShowRetangle; 127 | /** 128 | @brief 矩形框线条颜色 129 | */ 130 | @property (nonatomic, strong, nullable) UIColor *colorRetangleLine; 131 | 132 | #pragma mark - 矩形框(扫码区域)周围4个角 133 | 134 | //4个角的颜色 135 | @property (nonatomic, strong, nullable) UIColor* colorAngle; 136 | //扫码区域4个角的宽度和高度 默认都为20 137 | @property (nonatomic, assign) CGFloat photoframeAngleW; 138 | @property (nonatomic, assign) CGFloat photoframeAngleH; 139 | /** 140 | @brief 扫码区域4个角的线条宽度,默认6 141 | */ 142 | @property (nonatomic, assign) CGFloat photoframeLineW; 143 | 144 | #pragma mark --动画效果 145 | 146 | /** 147 | * 动画效果的图像 148 | */ 149 | @property (nonatomic,strong, nullable) UIImage * animationImage; 150 | /** 151 | 非识别区域颜色,默认 RGBA (0,0,0,0.5) 152 | */ 153 | @property (nonatomic, strong, nullable) UIColor * notRecoginitonArea; 154 | 155 | /** 156 | * 开始扫描动画 157 | */ 158 | - (void)startScanAnimation; 159 | /** 160 | * 结束扫描动画 161 | */ 162 | - (void)stopScanAnimation; 163 | 164 | /** 165 | 正在处理扫描到的结果 166 | */ 167 | - (void)handlingResultsOfScan; 168 | 169 | /** 170 | 完成扫描结果处理 171 | */ 172 | - (void)finishedHandle; 173 | 174 | /** 175 | 是否显示闪光灯开关 176 | @param show YES or NO 177 | */ 178 | - (void)showFlashSwitch:(BOOL)show; 179 | @end 180 | 181 | ``` 182 | 183 | * 初始化WSLNativeScanTool和WSLScanView 184 | 185 | ``` 186 | //输出流视图 187 | UIView *preview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)]; 188 | [self.view addSubview:preview]; 189 | 190 | __weak typeof(self) weakSelf = self; 191 | 192 | //构建扫描样式视图 193 | _scanView = [[WSLScanView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)]; 194 | _scanView.scanRetangleRect = CGRectMake(60, 120, (self.view.frame.size.width - 2 * 60), (self.view.frame.size.width - 2 * 60)); 195 | _scanView.colorAngle = [UIColor greenColor]; 196 | _scanView.photoframeAngleW = 20; 197 | _scanView.photoframeAngleH = 20; 198 | _scanView.photoframeLineW = 2; 199 | _scanView.isNeedShowRetangle = YES; 200 | _scanView.colorRetangleLine = [UIColor whiteColor]; 201 | _scanView.notRecoginitonArea = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 202 | _scanView.animationImage = [UIImage imageNamed:@"scanLine"]; 203 | _scanView.myQRCodeBlock = ^{ 204 | [WSLNativeScanTool createQRCodeImageWithString:@"https://www.jianshu.com/u/e15d1f644bea" andSize:CGSizeMake(250, 250) andBackColor:[UIColor whiteColor] andFrontColor:[UIColor orangeColor] andCenterImage:[UIImage imageNamed:@"piao"]]; 205 | createQRCodeController.qrString = @"https://www.jianshu.com/u/e15d1f644bea"; 206 | }; 207 | _scanView.flashSwitchBlock = ^(BOOL open) { 208 | [weakSelf.scanTool openFlashSwitch:open]; 209 | }; 210 | [self.view addSubview:_scanView]; 211 | 212 | //初始化扫描工具 213 | _scanTool = [[WSLNativeScanTool alloc] initWithPreview:preview andScanFrame:_scanView.scanRetangleRect]; 214 | _scanTool.scanFinishedBlock = ^(NSString *scanString) { 215 | NSLog(@"扫描结果 %@",scanString); 216 | [weakSelf.scanTool sessionStopRunning]; 217 | [weakSelf.scanTool openFlashSwitch:NO]; 218 | }; 219 | _scanTool.monitorLightBlock = ^(float brightness) { 220 | NSLog(@"环境光感 : %f",brightness); 221 | if (brightness < 0) { 222 | // 环境太暗,显示闪光灯开关按钮 223 | [weakSelf.scanView showFlashSwitch:YES]; 224 | }else if(brightness > 0){ 225 | // 环境亮度可以,且闪光灯处于关闭状态时,隐藏闪光灯开关 226 | if(!weakSelf.scanTool.flashOpen){ 227 | [weakSelf.scanView showFlashSwitch:NO]; 228 | } 229 | } 230 | }; 231 | [_scanTool sessionStartRunning]; 232 | [_scanView startScanAnimation]; 233 | 234 | ``` 235 | 236 | ![赞.gif](http://upload-images.jianshu.io/upload_images/1708447-ce06388c244874ce.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 237 | 238 | ## Welcome To Follow Me 239 | 240 | > 您的follow和start,是我前进的动力,Thanks♪(・ω・)ノ 241 | > * [简书](https://www.jianshu.com/u/e15d1f644bea) 242 | > * [微博](https://weibo.com/5732733120/profile?rightmod=1&wvr=6&mod=personinfo&is_all=1) 243 | > * [掘金](https://juejin.im/user/5c00d97b6fb9a049fb436288) 244 | > * [CSDN](https://blog.csdn.net/wsl2ls) 245 | > * QQ交流群:835303405 246 | 247 | > 欢迎扫描下方二维码关注——iOS开发进阶之路——微信公众号:iOS2679114653 本公众号是一个iOS开发者们的分享,交流,学习平台,会不定时的发送技术干货,源码,也欢迎大家积极踊跃投稿,(择优上头条) ^_^分享自己开发攻城的过程,心得,相互学习,共同进步,成为攻城狮中的翘楚! 248 | 249 | ![iOS开发进阶之路.jpg](http://upload-images.jianshu.io/upload_images/1708447-c2471528cadd7c86.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 250 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3121A6202047CF6600C739EB /* WSLCreateQRCodeController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3121A61F2047CF6600C739EB /* WSLCreateQRCodeController.m */; }; 11 | 3126E86C20467ADC0058E372 /* WSLScanView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3126E86B20467ADC0058E372 /* WSLScanView.m */; }; 12 | 3126E86E204682E70058E372 /* scanLine@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3126E86D204682E70058E372 /* scanLine@2x.png */; }; 13 | 31631AA22048E50F0063CE1C /* scanFlashlight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 31631AA02048E50F0063CE1C /* scanFlashlight@2x.png */; }; 14 | 31631AA32048E50F0063CE1C /* scanFlashlight@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 31631AA12048E50F0063CE1C /* scanFlashlight@3x.png */; }; 15 | 317DB4632018616C00D7308F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 317DB4622018616C00D7308F /* AppDelegate.m */; }; 16 | 317DB4662018616C00D7308F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 317DB4652018616C00D7308F /* ViewController.m */; }; 17 | 317DB4692018616C00D7308F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 317DB4672018616C00D7308F /* Main.storyboard */; }; 18 | 317DB46B2018616C00D7308F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 317DB46A2018616C00D7308F /* Assets.xcassets */; }; 19 | 317DB46E2018616C00D7308F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 317DB46C2018616C00D7308F /* LaunchScreen.storyboard */; }; 20 | 317DB4712018616C00D7308F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 317DB4702018616C00D7308F /* main.m */; }; 21 | 317DB47B2018616C00D7308F /* ScanQRcodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 317DB47A2018616C00D7308F /* ScanQRcodeTests.m */; }; 22 | 317DB4862018616C00D7308F /* ScanQRcodeUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 317DB4852018616C00D7308F /* ScanQRcodeUITests.m */; }; 23 | 319446522020577300002EC3 /* WSLNativeScanTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 319446512020577300002EC3 /* WSLNativeScanTool.m */; }; 24 | 785D27612283BA5D008A2F6C /* piao.png in Resources */ = {isa = PBXBuildFile; fileRef = 785D27602283BA5D008A2F6C /* piao.png */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 317DB4772018616C00D7308F /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 317DB4562018616C00D7308F /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 317DB45D2018616C00D7308F; 33 | remoteInfo = ScanQRcode; 34 | }; 35 | 317DB4822018616C00D7308F /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 317DB4562018616C00D7308F /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 317DB45D2018616C00D7308F; 40 | remoteInfo = ScanQRcode; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 3121A61E2047CF6600C739EB /* WSLCreateQRCodeController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WSLCreateQRCodeController.h; sourceTree = ""; }; 46 | 3121A61F2047CF6600C739EB /* WSLCreateQRCodeController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WSLCreateQRCodeController.m; sourceTree = ""; }; 47 | 3126E86A20467ADC0058E372 /* WSLScanView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WSLScanView.h; sourceTree = ""; }; 48 | 3126E86B20467ADC0058E372 /* WSLScanView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WSLScanView.m; sourceTree = ""; }; 49 | 3126E86D204682E70058E372 /* scanLine@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "scanLine@2x.png"; sourceTree = ""; }; 50 | 31631AA02048E50F0063CE1C /* scanFlashlight@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "scanFlashlight@2x.png"; sourceTree = ""; }; 51 | 31631AA12048E50F0063CE1C /* scanFlashlight@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "scanFlashlight@3x.png"; sourceTree = ""; }; 52 | 317DB45E2018616C00D7308F /* ScanQRcode.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScanQRcode.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 317DB4612018616C00D7308F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 54 | 317DB4622018616C00D7308F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 55 | 317DB4642018616C00D7308F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 56 | 317DB4652018616C00D7308F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 57 | 317DB4682018616C00D7308F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 58 | 317DB46A2018616C00D7308F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | 317DB46D2018616C00D7308F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 60 | 317DB46F2018616C00D7308F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 317DB4702018616C00D7308F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 62 | 317DB4762018616C00D7308F /* ScanQRcodeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScanQRcodeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 317DB47A2018616C00D7308F /* ScanQRcodeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRcodeTests.m; sourceTree = ""; }; 64 | 317DB47C2018616C00D7308F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 317DB4812018616C00D7308F /* ScanQRcodeUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ScanQRcodeUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 317DB4852018616C00D7308F /* ScanQRcodeUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScanQRcodeUITests.m; sourceTree = ""; }; 67 | 317DB4872018616C00D7308F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 319446502020577300002EC3 /* WSLNativeScanTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WSLNativeScanTool.h; sourceTree = ""; }; 69 | 319446512020577300002EC3 /* WSLNativeScanTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WSLNativeScanTool.m; sourceTree = ""; }; 70 | 785D27602283BA5D008A2F6C /* piao.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = piao.png; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 317DB45B2018616C00D7308F /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | ); 79 | runOnlyForDeploymentPostprocessing = 0; 80 | }; 81 | 317DB4732018616C00D7308F /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | 317DB47E2018616C00D7308F /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | ); 93 | runOnlyForDeploymentPostprocessing = 0; 94 | }; 95 | /* End PBXFrameworksBuildPhase section */ 96 | 97 | /* Begin PBXGroup section */ 98 | 317DB4552018616C00D7308F = { 99 | isa = PBXGroup; 100 | children = ( 101 | 317DB4602018616C00D7308F /* ScanQRcode */, 102 | 317DB4792018616C00D7308F /* ScanQRcodeTests */, 103 | 317DB4842018616C00D7308F /* ScanQRcodeUITests */, 104 | 317DB45F2018616C00D7308F /* Products */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | 317DB45F2018616C00D7308F /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 317DB45E2018616C00D7308F /* ScanQRcode.app */, 112 | 317DB4762018616C00D7308F /* ScanQRcodeTests.xctest */, 113 | 317DB4812018616C00D7308F /* ScanQRcodeUITests.xctest */, 114 | ); 115 | name = Products; 116 | sourceTree = ""; 117 | }; 118 | 317DB4602018616C00D7308F /* ScanQRcode */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 31631AA02048E50F0063CE1C /* scanFlashlight@2x.png */, 122 | 31631AA12048E50F0063CE1C /* scanFlashlight@3x.png */, 123 | 3126E86D204682E70058E372 /* scanLine@2x.png */, 124 | 785D27602283BA5D008A2F6C /* piao.png */, 125 | 317DB4612018616C00D7308F /* AppDelegate.h */, 126 | 317DB4622018616C00D7308F /* AppDelegate.m */, 127 | 319446502020577300002EC3 /* WSLNativeScanTool.h */, 128 | 319446512020577300002EC3 /* WSLNativeScanTool.m */, 129 | 3126E86A20467ADC0058E372 /* WSLScanView.h */, 130 | 3126E86B20467ADC0058E372 /* WSLScanView.m */, 131 | 317DB4642018616C00D7308F /* ViewController.h */, 132 | 317DB4652018616C00D7308F /* ViewController.m */, 133 | 3121A61E2047CF6600C739EB /* WSLCreateQRCodeController.h */, 134 | 3121A61F2047CF6600C739EB /* WSLCreateQRCodeController.m */, 135 | 317DB4672018616C00D7308F /* Main.storyboard */, 136 | 317DB46A2018616C00D7308F /* Assets.xcassets */, 137 | 317DB46C2018616C00D7308F /* LaunchScreen.storyboard */, 138 | 317DB46F2018616C00D7308F /* Info.plist */, 139 | 317DB4702018616C00D7308F /* main.m */, 140 | ); 141 | path = ScanQRcode; 142 | sourceTree = ""; 143 | }; 144 | 317DB4792018616C00D7308F /* ScanQRcodeTests */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 317DB47A2018616C00D7308F /* ScanQRcodeTests.m */, 148 | 317DB47C2018616C00D7308F /* Info.plist */, 149 | ); 150 | path = ScanQRcodeTests; 151 | sourceTree = ""; 152 | }; 153 | 317DB4842018616C00D7308F /* ScanQRcodeUITests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 317DB4852018616C00D7308F /* ScanQRcodeUITests.m */, 157 | 317DB4872018616C00D7308F /* Info.plist */, 158 | ); 159 | path = ScanQRcodeUITests; 160 | sourceTree = ""; 161 | }; 162 | /* End PBXGroup section */ 163 | 164 | /* Begin PBXNativeTarget section */ 165 | 317DB45D2018616C00D7308F /* ScanQRcode */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 317DB48A2018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcode" */; 168 | buildPhases = ( 169 | 317DB45A2018616C00D7308F /* Sources */, 170 | 317DB45B2018616C00D7308F /* Frameworks */, 171 | 317DB45C2018616C00D7308F /* Resources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | ); 177 | name = ScanQRcode; 178 | productName = ScanQRcode; 179 | productReference = 317DB45E2018616C00D7308F /* ScanQRcode.app */; 180 | productType = "com.apple.product-type.application"; 181 | }; 182 | 317DB4752018616C00D7308F /* ScanQRcodeTests */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 317DB48D2018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcodeTests" */; 185 | buildPhases = ( 186 | 317DB4722018616C00D7308F /* Sources */, 187 | 317DB4732018616C00D7308F /* Frameworks */, 188 | 317DB4742018616C00D7308F /* Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | 317DB4782018616C00D7308F /* PBXTargetDependency */, 194 | ); 195 | name = ScanQRcodeTests; 196 | productName = ScanQRcodeTests; 197 | productReference = 317DB4762018616C00D7308F /* ScanQRcodeTests.xctest */; 198 | productType = "com.apple.product-type.bundle.unit-test"; 199 | }; 200 | 317DB4802018616C00D7308F /* ScanQRcodeUITests */ = { 201 | isa = PBXNativeTarget; 202 | buildConfigurationList = 317DB4902018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcodeUITests" */; 203 | buildPhases = ( 204 | 317DB47D2018616C00D7308F /* Sources */, 205 | 317DB47E2018616C00D7308F /* Frameworks */, 206 | 317DB47F2018616C00D7308F /* Resources */, 207 | ); 208 | buildRules = ( 209 | ); 210 | dependencies = ( 211 | 317DB4832018616C00D7308F /* PBXTargetDependency */, 212 | ); 213 | name = ScanQRcodeUITests; 214 | productName = ScanQRcodeUITests; 215 | productReference = 317DB4812018616C00D7308F /* ScanQRcodeUITests.xctest */; 216 | productType = "com.apple.product-type.bundle.ui-testing"; 217 | }; 218 | /* End PBXNativeTarget section */ 219 | 220 | /* Begin PBXProject section */ 221 | 317DB4562018616C00D7308F /* Project object */ = { 222 | isa = PBXProject; 223 | attributes = { 224 | LastUpgradeCheck = 0920; 225 | ORGANIZATIONNAME = "王双龙"; 226 | TargetAttributes = { 227 | 317DB45D2018616C00D7308F = { 228 | CreatedOnToolsVersion = 9.2; 229 | ProvisioningStyle = Manual; 230 | }; 231 | 317DB4752018616C00D7308F = { 232 | CreatedOnToolsVersion = 9.2; 233 | ProvisioningStyle = Automatic; 234 | TestTargetID = 317DB45D2018616C00D7308F; 235 | }; 236 | 317DB4802018616C00D7308F = { 237 | CreatedOnToolsVersion = 9.2; 238 | ProvisioningStyle = Automatic; 239 | TestTargetID = 317DB45D2018616C00D7308F; 240 | }; 241 | }; 242 | }; 243 | buildConfigurationList = 317DB4592018616C00D7308F /* Build configuration list for PBXProject "ScanQRcode" */; 244 | compatibilityVersion = "Xcode 8.0"; 245 | developmentRegion = en; 246 | hasScannedForEncodings = 0; 247 | knownRegions = ( 248 | en, 249 | Base, 250 | ); 251 | mainGroup = 317DB4552018616C00D7308F; 252 | productRefGroup = 317DB45F2018616C00D7308F /* Products */; 253 | projectDirPath = ""; 254 | projectRoot = ""; 255 | targets = ( 256 | 317DB45D2018616C00D7308F /* ScanQRcode */, 257 | 317DB4752018616C00D7308F /* ScanQRcodeTests */, 258 | 317DB4802018616C00D7308F /* ScanQRcodeUITests */, 259 | ); 260 | }; 261 | /* End PBXProject section */ 262 | 263 | /* Begin PBXResourcesBuildPhase section */ 264 | 317DB45C2018616C00D7308F /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | 317DB46E2018616C00D7308F /* LaunchScreen.storyboard in Resources */, 269 | 3126E86E204682E70058E372 /* scanLine@2x.png in Resources */, 270 | 317DB46B2018616C00D7308F /* Assets.xcassets in Resources */, 271 | 785D27612283BA5D008A2F6C /* piao.png in Resources */, 272 | 31631AA22048E50F0063CE1C /* scanFlashlight@2x.png in Resources */, 273 | 31631AA32048E50F0063CE1C /* scanFlashlight@3x.png in Resources */, 274 | 317DB4692018616C00D7308F /* Main.storyboard in Resources */, 275 | ); 276 | runOnlyForDeploymentPostprocessing = 0; 277 | }; 278 | 317DB4742018616C00D7308F /* Resources */ = { 279 | isa = PBXResourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | 317DB47F2018616C00D7308F /* Resources */ = { 286 | isa = PBXResourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXResourcesBuildPhase section */ 293 | 294 | /* Begin PBXSourcesBuildPhase section */ 295 | 317DB45A2018616C00D7308F /* Sources */ = { 296 | isa = PBXSourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | 317DB4662018616C00D7308F /* ViewController.m in Sources */, 300 | 317DB4712018616C00D7308F /* main.m in Sources */, 301 | 319446522020577300002EC3 /* WSLNativeScanTool.m in Sources */, 302 | 3126E86C20467ADC0058E372 /* WSLScanView.m in Sources */, 303 | 3121A6202047CF6600C739EB /* WSLCreateQRCodeController.m in Sources */, 304 | 317DB4632018616C00D7308F /* AppDelegate.m in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | 317DB4722018616C00D7308F /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 317DB47B2018616C00D7308F /* ScanQRcodeTests.m in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | 317DB47D2018616C00D7308F /* Sources */ = { 317 | isa = PBXSourcesBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | 317DB4862018616C00D7308F /* ScanQRcodeUITests.m in Sources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXSourcesBuildPhase section */ 325 | 326 | /* Begin PBXTargetDependency section */ 327 | 317DB4782018616C00D7308F /* PBXTargetDependency */ = { 328 | isa = PBXTargetDependency; 329 | target = 317DB45D2018616C00D7308F /* ScanQRcode */; 330 | targetProxy = 317DB4772018616C00D7308F /* PBXContainerItemProxy */; 331 | }; 332 | 317DB4832018616C00D7308F /* PBXTargetDependency */ = { 333 | isa = PBXTargetDependency; 334 | target = 317DB45D2018616C00D7308F /* ScanQRcode */; 335 | targetProxy = 317DB4822018616C00D7308F /* PBXContainerItemProxy */; 336 | }; 337 | /* End PBXTargetDependency section */ 338 | 339 | /* Begin PBXVariantGroup section */ 340 | 317DB4672018616C00D7308F /* Main.storyboard */ = { 341 | isa = PBXVariantGroup; 342 | children = ( 343 | 317DB4682018616C00D7308F /* Base */, 344 | ); 345 | name = Main.storyboard; 346 | sourceTree = ""; 347 | }; 348 | 317DB46C2018616C00D7308F /* LaunchScreen.storyboard */ = { 349 | isa = PBXVariantGroup; 350 | children = ( 351 | 317DB46D2018616C00D7308F /* Base */, 352 | ); 353 | name = LaunchScreen.storyboard; 354 | sourceTree = ""; 355 | }; 356 | /* End PBXVariantGroup section */ 357 | 358 | /* Begin XCBuildConfiguration section */ 359 | 317DB4882018616C00D7308F /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ALWAYS_SEARCH_USER_PATHS = NO; 363 | CLANG_ANALYZER_NONNULL = YES; 364 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 374 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INFINITE_RECURSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 381 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 382 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 383 | CLANG_WARN_STRICT_PROTOTYPES = YES; 384 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 385 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 386 | CLANG_WARN_UNREACHABLE_CODE = YES; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | CODE_SIGN_IDENTITY = "iPhone Developer"; 389 | COPY_PHASE_STRIP = NO; 390 | DEBUG_INFORMATION_FORMAT = dwarf; 391 | ENABLE_STRICT_OBJC_MSGSEND = YES; 392 | ENABLE_TESTABILITY = YES; 393 | GCC_C_LANGUAGE_STANDARD = gnu11; 394 | GCC_DYNAMIC_NO_PIC = NO; 395 | GCC_NO_COMMON_BLOCKS = YES; 396 | GCC_OPTIMIZATION_LEVEL = 0; 397 | GCC_PREPROCESSOR_DEFINITIONS = ( 398 | "DEBUG=1", 399 | "$(inherited)", 400 | ); 401 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 402 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 403 | GCC_WARN_UNDECLARED_SELECTOR = YES; 404 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 405 | GCC_WARN_UNUSED_FUNCTION = YES; 406 | GCC_WARN_UNUSED_VARIABLE = YES; 407 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 408 | MTL_ENABLE_DEBUG_INFO = YES; 409 | ONLY_ACTIVE_ARCH = YES; 410 | SDKROOT = iphoneos; 411 | }; 412 | name = Debug; 413 | }; 414 | 317DB4892018616C00D7308F /* Release */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ALWAYS_SEARCH_USER_PATHS = NO; 418 | CLANG_ANALYZER_NONNULL = YES; 419 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 425 | CLANG_WARN_BOOL_CONVERSION = YES; 426 | CLANG_WARN_COMMA = YES; 427 | CLANG_WARN_CONSTANT_CONVERSION = YES; 428 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 429 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 430 | CLANG_WARN_EMPTY_BODY = YES; 431 | CLANG_WARN_ENUM_CONVERSION = YES; 432 | CLANG_WARN_INFINITE_RECURSION = YES; 433 | CLANG_WARN_INT_CONVERSION = YES; 434 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 435 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 437 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 438 | CLANG_WARN_STRICT_PROTOTYPES = YES; 439 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 440 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 441 | CLANG_WARN_UNREACHABLE_CODE = YES; 442 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 443 | CODE_SIGN_IDENTITY = "iPhone Developer"; 444 | COPY_PHASE_STRIP = NO; 445 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 446 | ENABLE_NS_ASSERTIONS = NO; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | GCC_C_LANGUAGE_STANDARD = gnu11; 449 | GCC_NO_COMMON_BLOCKS = YES; 450 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 451 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 452 | GCC_WARN_UNDECLARED_SELECTOR = YES; 453 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 454 | GCC_WARN_UNUSED_FUNCTION = YES; 455 | GCC_WARN_UNUSED_VARIABLE = YES; 456 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 457 | MTL_ENABLE_DEBUG_INFO = NO; 458 | SDKROOT = iphoneos; 459 | VALIDATE_PRODUCT = YES; 460 | }; 461 | name = Release; 462 | }; 463 | 317DB48B2018616C00D7308F /* Debug */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 467 | CODE_SIGN_IDENTITY = "iPhone Developer: qu xg (E3B8HDH8KA)"; 468 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: qu xg (E3B8HDH8KA)"; 469 | CODE_SIGN_STYLE = Manual; 470 | DEVELOPMENT_TEAM = 2N77G66QN3; 471 | INFOPLIST_FILE = ScanQRcode/Info.plist; 472 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 473 | PRODUCT_BUNDLE_IDENTIFIER = com.chinadaily.ChinaDaily; 474 | PRODUCT_NAME = "$(TARGET_NAME)"; 475 | PROVISIONING_PROFILE = "3022966d-393a-453d-9a18-cceb1425613d"; 476 | PROVISIONING_PROFILE_SPECIFIER = "News for iPhone"; 477 | TARGETED_DEVICE_FAMILY = "1,2"; 478 | }; 479 | name = Debug; 480 | }; 481 | 317DB48C2018616C00D7308F /* Release */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 485 | CODE_SIGN_IDENTITY = "iPhone Developer: qu xg (E3B8HDH8KA)"; 486 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: qu xg (E3B8HDH8KA)"; 487 | CODE_SIGN_STYLE = Manual; 488 | DEVELOPMENT_TEAM = 2N77G66QN3; 489 | INFOPLIST_FILE = ScanQRcode/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 491 | PRODUCT_BUNDLE_IDENTIFIER = com.chinadaily.ChinaDaily; 492 | PRODUCT_NAME = "$(TARGET_NAME)"; 493 | PROVISIONING_PROFILE = "3022966d-393a-453d-9a18-cceb1425613d"; 494 | PROVISIONING_PROFILE_SPECIFIER = "News for iPhone"; 495 | TARGETED_DEVICE_FAMILY = "1,2"; 496 | }; 497 | name = Release; 498 | }; 499 | 317DB48E2018616C00D7308F /* Debug */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | BUNDLE_LOADER = "$(TEST_HOST)"; 503 | CODE_SIGN_STYLE = Automatic; 504 | INFOPLIST_FILE = ScanQRcodeTests/Info.plist; 505 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 506 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.ScanQRcodeTests; 507 | PRODUCT_NAME = "$(TARGET_NAME)"; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScanQRcode.app/ScanQRcode"; 510 | }; 511 | name = Debug; 512 | }; 513 | 317DB48F2018616C00D7308F /* Release */ = { 514 | isa = XCBuildConfiguration; 515 | buildSettings = { 516 | BUNDLE_LOADER = "$(TEST_HOST)"; 517 | CODE_SIGN_STYLE = Automatic; 518 | INFOPLIST_FILE = ScanQRcodeTests/Info.plist; 519 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 520 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.ScanQRcodeTests; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | TARGETED_DEVICE_FAMILY = "1,2"; 523 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ScanQRcode.app/ScanQRcode"; 524 | }; 525 | name = Release; 526 | }; 527 | 317DB4912018616C00D7308F /* Debug */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | CODE_SIGN_STYLE = Automatic; 531 | INFOPLIST_FILE = ScanQRcodeUITests/Info.plist; 532 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 533 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.ScanQRcodeUITests; 534 | PRODUCT_NAME = "$(TARGET_NAME)"; 535 | TARGETED_DEVICE_FAMILY = "1,2"; 536 | TEST_TARGET_NAME = ScanQRcode; 537 | }; 538 | name = Debug; 539 | }; 540 | 317DB4922018616C00D7308F /* Release */ = { 541 | isa = XCBuildConfiguration; 542 | buildSettings = { 543 | CODE_SIGN_STYLE = Automatic; 544 | INFOPLIST_FILE = ScanQRcodeUITests/Info.plist; 545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 546 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.ScanQRcodeUITests; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | TARGETED_DEVICE_FAMILY = "1,2"; 549 | TEST_TARGET_NAME = ScanQRcode; 550 | }; 551 | name = Release; 552 | }; 553 | /* End XCBuildConfiguration section */ 554 | 555 | /* Begin XCConfigurationList section */ 556 | 317DB4592018616C00D7308F /* Build configuration list for PBXProject "ScanQRcode" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 317DB4882018616C00D7308F /* Debug */, 560 | 317DB4892018616C00D7308F /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | 317DB48A2018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcode" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 317DB48B2018616C00D7308F /* Debug */, 569 | 317DB48C2018616C00D7308F /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 317DB48D2018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcodeTests" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 317DB48E2018616C00D7308F /* Debug */, 578 | 317DB48F2018616C00D7308F /* Release */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | 317DB4902018616C00D7308F /* Build configuration list for PBXNativeTarget "ScanQRcodeUITests" */ = { 584 | isa = XCConfigurationList; 585 | buildConfigurations = ( 586 | 317DB4912018616C00D7308F /* Debug */, 587 | 317DB4922018616C00D7308F /* Release */, 588 | ); 589 | defaultConfigurationIsVisible = 0; 590 | defaultConfigurationName = Release; 591 | }; 592 | /* End XCConfigurationList section */ 593 | }; 594 | rootObject = 317DB4562018616C00D7308F /* Project object */; 595 | } 596 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/project.xcworkspace/xcuserdata/wangshuanglong.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/ScanQRcodeDemo/ScanQRcode.xcodeproj/project.xcworkspace/xcuserdata/wangshuanglong.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/xcuserdata/wangshuanglong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode.xcodeproj/xcuserdata/wangshuanglong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ScanQRcode.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ScanQRcode.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSCameraUsageDescription 24 | 访问相机将用于扫一扫、拍照等功能。 25 | NSPhotoLibraryUsageDescription 26 | 让开,我要访问你的相册咯 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "WSLScanView.h" 12 | #import "WSLNativeScanTool.h" 13 | #import "WSLCreateQRCodeController.h" 14 | 15 | #define iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) 16 | #define StatusBarAndNavigationBarHeight (iPhoneX ? 88.f : 64.f) 17 | 18 | @interface ViewController () 19 | 20 | @property (nonatomic, strong) WSLNativeScanTool * scanTool; 21 | @property (nonatomic, strong) WSLScanView * scanView; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | 30 | UIButton * photoBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 80, StatusBarAndNavigationBarHeight/2, 64, StatusBarAndNavigationBarHeight/2)]; 31 | [photoBtn setTitle:@"相册" forState:UIControlStateNormal]; 32 | [photoBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 33 | [photoBtn addTarget:self action:@selector(photoBtnClicked) forControlEvents:UIControlEventTouchUpInside]; 34 | self.navigationItem.title = @"二维码/条码"; 35 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:photoBtn]; 36 | 37 | //输出流视图 38 | UIView *preview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)]; 39 | [self.view addSubview:preview]; 40 | 41 | __weak typeof(self) weakSelf = self; 42 | 43 | //构建扫描样式视图 44 | _scanView = [[WSLScanView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 0)]; 45 | _scanView.scanRetangleRect = CGRectMake(60, 120, (self.view.frame.size.width - 2 * 60), (self.view.frame.size.width - 2 * 60)); 46 | _scanView.colorAngle = [UIColor greenColor]; 47 | _scanView.photoframeAngleW = 20; 48 | _scanView.photoframeAngleH = 20; 49 | _scanView.photoframeLineW = 2; 50 | _scanView.isNeedShowRetangle = YES; 51 | _scanView.colorRetangleLine = [UIColor whiteColor]; 52 | _scanView.notRecoginitonArea = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 53 | _scanView.animationImage = [UIImage imageNamed:@"scanLine"]; 54 | _scanView.myQRCodeBlock = ^{ 55 | // 获取指定的Storyboard,name填写Storyboard的文件名 56 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 57 | // 从Storyboard上按照identifier获取指定的界面(VC),identifier必须是唯一的 58 | WSLCreateQRCodeController *createQRCodeController = [storyboard instantiateViewControllerWithIdentifier:@"createQRCode"]; 59 | createQRCodeController.qrImage = [WSLNativeScanTool createQRCodeImageWithString:@"https://www.jianshu.com/u/e15d1f644bea" andSize:CGSizeMake(250, 250) andBackColor:[UIColor whiteColor] andFrontColor:[UIColor orangeColor] andCenterImage:[UIImage imageNamed:@"piao"]]; 60 | createQRCodeController.qrString = @"https://www.jianshu.com/u/e15d1f644bea"; 61 | [weakSelf.navigationController pushViewController:createQRCodeController animated:YES]; 62 | }; 63 | _scanView.flashSwitchBlock = ^(BOOL open) { 64 | [weakSelf.scanTool openFlashSwitch:open]; 65 | }; 66 | [self.view addSubview:_scanView]; 67 | 68 | //初始化扫描工具 69 | _scanTool = [[WSLNativeScanTool alloc] initWithPreview:preview andScanFrame:_scanView.scanRetangleRect]; 70 | _scanTool.scanFinishedBlock = ^(NSString *scanString) { 71 | NSLog(@"扫描结果 %@",scanString); 72 | [weakSelf.scanView handlingResultsOfScan]; 73 | // 获取指定的Storyboard,name填写Storyboard的文件名 74 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; 75 | // 从Storyboard上按照identifier获取指定的界面(VC),identifier必须是唯一的 76 | WSLCreateQRCodeController *createQRCodeController = [storyboard instantiateViewControllerWithIdentifier:@"createQRCode"]; 77 | createQRCodeController.qrImage = [WSLNativeScanTool createQRCodeImageWithString:scanString andSize:CGSizeMake(250, 250) andBackColor:[UIColor colorWithRed:arc4random()%255/256.0 green:arc4random()%255/256.0 blue:arc4random()%255/256.0 alpha:1.0] andFrontColor:[UIColor colorWithRed:arc4random()%255/256.0 green:arc4random()%255/256.0 blue:arc4random()%255/256.0 alpha:1.0] andCenterImage:[UIImage imageNamed:@"piao"]]; 78 | createQRCodeController.qrString = scanString; 79 | [weakSelf.navigationController pushViewController:createQRCodeController animated:YES]; 80 | [weakSelf.scanTool sessionStopRunning]; 81 | [weakSelf.scanTool openFlashSwitch:NO]; 82 | }; 83 | _scanTool.monitorLightBlock = ^(float brightness) { 84 | NSLog(@"环境光感 : %f",brightness); 85 | if (brightness < 0) { 86 | // 环境太暗,显示闪光灯开关按钮 87 | [weakSelf.scanView showFlashSwitch:YES]; 88 | }else if(brightness > 0){ 89 | // 环境亮度可以,且闪光灯处于关闭状态时,隐藏闪光灯开关 90 | if(!weakSelf.scanTool.flashOpen){ 91 | [weakSelf.scanView showFlashSwitch:NO]; 92 | } 93 | } 94 | }; 95 | 96 | [_scanTool sessionStartRunning]; 97 | [_scanView startScanAnimation]; 98 | 99 | } 100 | 101 | - (void)viewWillAppear:(BOOL)animated{ 102 | [super viewWillAppear:animated]; 103 | [_scanView startScanAnimation]; 104 | [_scanTool sessionStartRunning]; 105 | } 106 | - (void)viewWillDisappear:(BOOL)animated{ 107 | [super viewWillDisappear:animated]; 108 | [_scanView stopScanAnimation]; 109 | [_scanView finishedHandle]; 110 | [_scanView showFlashSwitch:NO]; 111 | [_scanTool sessionStopRunning]; 112 | } 113 | #pragma mark -- Events Handle 114 | - (void)photoBtnClicked{ 115 | if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]){ 116 | UIImagePickerController * _imagePickerController = [[UIImagePickerController alloc] init]; 117 | _imagePickerController.delegate = self; 118 | _imagePickerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; 119 | _imagePickerController.allowsEditing = YES; 120 | _imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 121 | [self presentViewController:_imagePickerController animated:YES completion:nil]; 122 | }else{ 123 | NSLog(@"不支持访问相册"); 124 | } 125 | } 126 | - (void)showAlertWithTitle:(NSString *)title message:(NSString *)message handler:(void (^) (UIAlertAction *action))handler{ 127 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert]; 128 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:handler]; 129 | [alert addAction:action]; 130 | [self presentViewController:alert animated:YES completion:nil]; 131 | } 132 | #pragma mark UIImagePickerControllerDelegate 133 | //该代理方法仅适用于只选取图片时 134 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary *)editingInfo { 135 | // NSLog(@"选择完毕----image:%@-----info:%@",image,editingInfo); 136 | [self dismissViewControllerAnimated:YES completion:nil]; 137 | [_scanTool scanImageQRCode:image]; 138 | } 139 | 140 | 141 | - (void)didReceiveMemoryWarning { 142 | [super didReceiveMemoryWarning]; 143 | // Dispose of any resources that can be recreated. 144 | } 145 | 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLCreateQRCodeController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSLCreateQRCodeController.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/3/1. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WSLCreateQRCodeController : UIViewController 12 | 13 | @property (nonatomic, strong) UIImage * qrImage; 14 | 15 | @property (nonatomic, copy) NSString * qrString; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLCreateQRCodeController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSLCreateQRCodeController.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/3/1. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import "WSLCreateQRCodeController.h" 10 | 11 | @interface WSLCreateQRCodeController () 12 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 13 | @property (weak, nonatomic) IBOutlet UILabel *label; 14 | 15 | @end 16 | 17 | @implementation WSLCreateQRCodeController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | 22 | self.navigationItem.title = @"生成二维码"; 23 | self.imageView.image = self.qrImage; 24 | self.label.text = [NSString stringWithFormat:@"扫描结果:%@ ",self.qrString]; 25 | 26 | UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapLabel)]; 27 | [self.label addGestureRecognizer:tap]; 28 | [self.imageView addGestureRecognizer:tap]; 29 | } 30 | 31 | - (void)tapLabel{ 32 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.qrString]]; 33 | } 34 | 35 | - (void)didReceiveMemoryWarning { 36 | [super didReceiveMemoryWarning]; 37 | } 38 | 39 | /* 40 | #pragma mark - Navigation 41 | 42 | // In a storyboard-based application, you will often want to do a little preparation before navigation 43 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 44 | // Get the new view controller using [segue destinationViewController]. 45 | // Pass the selected object to the new view controller. 46 | } 47 | */ 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLNativeScanTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSLNativeScanTool.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/30. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | @import AVFoundation; 11 | 12 | #import 13 | 14 | /** 15 | 扫描完成的回调 16 | @param scanString 扫描出的字符串 17 | */ 18 | typedef void(^WSLScanFinishedBlock)( NSString * _Nullable scanString); 19 | 20 | /** 21 | 监听环境光感的回调 22 | @param brightness 亮度值 23 | */ 24 | typedef void(^WSLMonitorLightBlock)( float brightness); 25 | 26 | @interface WSLNativeScanTool : NSObject 27 | 28 | /** 29 | 扫描出结果后的回调 ,注意循环引用的问题 30 | */ 31 | @property (nonatomic, copy) WSLScanFinishedBlock _Nullable scanFinishedBlock; 32 | 33 | /** 34 | 监听环境光感的回调,如果 != nil 表示开启监测环境亮度功能 35 | */ 36 | @property (nonatomic, copy) WSLMonitorLightBlock _Nullable monitorLightBlock; 37 | 38 | /** 39 | 闪光灯的状态,不需要设置,仅供外边判断状态使用 40 | */ 41 | @property (nonatomic, assign) BOOL flashOpen; 42 | 43 | /** 44 | 初始化 扫描工具 45 | @param preview 展示输出流的视图 46 | @param scanFrame 扫描中心识别区域范围 47 | */ 48 | - (instancetype )initWithPreview:(UIView *)preview andScanFrame:(CGRect)scanFrame; 49 | 50 | /** 51 | 闪光灯开关 52 | */ 53 | - (void)openFlashSwitch:(BOOL)open; 54 | 55 | - (void)sessionStartRunning; 56 | 57 | - (void)sessionStopRunning; 58 | 59 | /** 60 | 识别图中二维码 61 | */ 62 | - (void)scanImageQRCode:(UIImage *_Nullable)imageCode; 63 | 64 | /** 65 | 生成自定义样式二维码 66 | 注意:有些颜色结合生成的二维码识别不了 67 | @param codeString 字符串 68 | @param size 大小 69 | @param backColor 背景色 70 | @param frontColor 前景色 71 | @param centerImage 中心图片 72 | @return image二维码 73 | */ 74 | + (UIImage *)createQRCodeImageWithString:(nonnull NSString *)codeString andSize:(CGSize)size andBackColor:(nullable UIColor *)backColor andFrontColor:(nullable UIColor *)frontColor andCenterImage:(nullable UIImage *)centerImage; 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLNativeScanTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSLNativeScanTool.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/30. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import "WSLNativeScanTool.h" 10 | 11 | @interface WSLNativeScanTool() 12 | 13 | @property (nonatomic, strong) AVCaptureSession * session; 14 | 15 | /** 16 | 扫描中心识别区域范围 17 | */ 18 | @property (nonatomic, assign) CGRect scanFrame; 19 | 20 | /** 21 | 展示输出流的视图——即照相机镜头下的内容 22 | */ 23 | @property (nonatomic, strong) UIView *preview; 24 | 25 | @end 26 | 27 | @implementation WSLNativeScanTool 28 | 29 | - (instancetype)initWithPreview:(UIView *)preview andScanFrame:(CGRect)scanFrame{ 30 | 31 | if (self == [super init]) { 32 | self.preview = preview; 33 | self.scanFrame = scanFrame; 34 | [self configuredScanTool]; 35 | } 36 | return self; 37 | } 38 | 39 | #pragma mark -- Help Methods 40 | 41 | //初始化采集配置信息 42 | - (void)configuredScanTool{ 43 | 44 | AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; 45 | layer.videoGravity = AVLayerVideoGravityResizeAspectFill; 46 | layer.frame = self.preview.layer.bounds; 47 | [self.preview.layer insertSublayer:layer atIndex:0]; 48 | 49 | } 50 | 51 | #pragma mark -- Event Handel 52 | 53 | - (void)openFlashSwitch:(BOOL)open{ 54 | if (self.flashOpen == open) { 55 | return; 56 | } 57 | self.flashOpen = open; 58 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 59 | 60 | if ([device hasTorch] && [device hasFlash]){ 61 | 62 | [device lockForConfiguration:nil]; 63 | if (self.flashOpen){ 64 | device.torchMode = AVCaptureTorchModeOn; 65 | device.flashMode = AVCaptureFlashModeOn; 66 | } 67 | else{ 68 | device.torchMode = AVCaptureTorchModeOff; 69 | device.flashMode = AVCaptureFlashModeOff; 70 | } 71 | 72 | [device unlockForConfiguration]; 73 | } 74 | 75 | } 76 | 77 | - (void)sessionStartRunning{ 78 | [_session startRunning]; 79 | } 80 | 81 | - (void)sessionStopRunning{ 82 | [_session stopRunning]; 83 | } 84 | 85 | 86 | - (void)scanImageQRCode:(UIImage *)imageCode{ 87 | 88 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode 89 | context:nil 90 | options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}]; 91 | NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:imageCode.CGImage]]; 92 | if (features.count >= 1){ 93 | CIQRCodeFeature *feature = [features firstObject]; 94 | if(self.scanFinishedBlock != nil){ 95 | self.scanFinishedBlock(feature.messageString); 96 | } 97 | }else{ 98 | NSLog(@"无法识别图中二维码"); 99 | } 100 | } 101 | 102 | + (UIImage *)createQRCodeImageWithString:(nonnull NSString *)codeString andSize:(CGSize)size andBackColor:(nullable UIColor *)backColor andFrontColor:(nullable UIColor *)frontColor andCenterImage:(nullable UIImage *)centerImage{ 103 | 104 | NSData *stringData = [codeString dataUsingEncoding:NSUTF8StringEncoding]; 105 | 106 | CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; 107 | // NSLog(@"%@",qrFilter.inputKeys); 108 | [qrFilter setValue:stringData forKey:@"inputMessage"]; 109 | [qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"]; 110 | 111 | CIImage *qrImage = qrFilter.outputImage; 112 | //放大并绘制二维码 (上面生成的二维码很小,需要放大) 113 | CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent]; 114 | UIGraphicsBeginImageContext(size); 115 | CGContextRef context = UIGraphicsGetCurrentContext(); 116 | CGContextSetInterpolationQuality(context, kCGInterpolationNone); 117 | //翻转一下图片 不然生成的QRCode就是上下颠倒的 118 | CGContextScaleCTM(context, 1.0, -1.0); 119 | CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage); 120 | UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext(); 121 | UIGraphicsEndImageContext(); 122 | 123 | CGImageRelease(cgImage); 124 | 125 | //绘制颜色 126 | CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor" 127 | keysAndValues: 128 | @"inputImage",[CIImage imageWithCGImage:codeImage.CGImage], 129 | @"inputColor0",[CIColor colorWithCGColor:frontColor == nil ? [UIColor clearColor].CGColor: frontColor.CGColor], 130 | @"inputColor1",[CIColor colorWithCGColor: backColor == nil ? [UIColor blackColor].CGColor : backColor.CGColor], 131 | nil]; 132 | 133 | UIImage * colorCodeImage = [UIImage imageWithCIImage:colorFilter.outputImage]; 134 | 135 | //中心添加图片 136 | if (centerImage != nil) { 137 | 138 | UIGraphicsBeginImageContext(colorCodeImage.size); 139 | 140 | [colorCodeImage drawInRect:CGRectMake(0, 0, colorCodeImage.size.width, colorCodeImage.size.height)]; 141 | 142 | UIImage *image = centerImage; 143 | 144 | CGFloat imageW = 50; 145 | CGFloat imageX = (colorCodeImage.size.width - imageW) * 0.5; 146 | CGFloat imgaeY = (colorCodeImage.size.height - imageW) * 0.5; 147 | 148 | [image drawInRect:CGRectMake(imageX, imgaeY, imageW, imageW)]; 149 | 150 | UIImage *centerImageCode = UIGraphicsGetImageFromCurrentImageContext(); 151 | UIGraphicsEndImageContext(); 152 | return centerImageCode; 153 | } 154 | return colorCodeImage; 155 | } 156 | 157 | #pragma mark -- AVCaptureMetadataOutputObjectsDelegate 158 | //扫描完成后执行 159 | -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{ 160 | 161 | if (metadataObjects.count > 0){ 162 | AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject]; 163 | // 扫描完成后的字符 164 | // NSLog(@"扫描出 %@",metadataObject.stringValue); 165 | if(self.scanFinishedBlock != nil){ 166 | self.scanFinishedBlock(metadataObject.stringValue); 167 | } 168 | } 169 | } 170 | #pragma mark- AVCaptureVideoDataOutputSampleBufferDelegate的方法 171 | //扫描过程中执行,主要用来判断环境的黑暗程度 172 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{ 173 | 174 | if (self.monitorLightBlock == nil) { 175 | return; 176 | } 177 | 178 | CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate); 179 | NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict]; 180 | CFRelease(metadataDict); 181 | NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy]; 182 | float brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue]; 183 | 184 | // NSLog(@"环境光感 : %f",brightnessValue); 185 | 186 | // 根据brightnessValue的值来判断是否需要打开和关闭闪光灯 187 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 188 | BOOL result = [device hasTorch];// 判断设备是否有闪光灯 189 | if ((brightnessValue < 0) && result) { 190 | // 环境太暗,可以打开闪光灯了 191 | }else if((brightnessValue > 0) && result){ 192 | // 环境亮度可以 193 | } 194 | if (self.monitorLightBlock != nil) { 195 | self.monitorLightBlock(brightnessValue); 196 | } 197 | 198 | } 199 | 200 | #pragma mark - Getter 201 | - (AVCaptureSession *)session{ 202 | 203 | if (_session == nil){ 204 | //获取摄像设备 205 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 206 | //创建输入流 207 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; 208 | if (!input){ 209 | return nil; 210 | } 211 | 212 | //创建二维码扫描输出流 213 | AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; 214 | //设置代理 在主线程里刷新 215 | [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; 216 | //设置采集扫描区域的比例 默认全屏是(0,0,1,1) 217 | //rectOfInterest 填写的是一个比例,输出流视图preview.frame为 x , y, w, h, 要设置的矩形快的scanFrame 为 x1, y1, w1, h1. 那么rectOfInterest 应该设置为 CGRectMake(y1/y, x1/x, h1/h, w1/w)。 218 | CGFloat x = CGRectGetMinX(self.scanFrame)/ CGRectGetWidth(self.preview.frame); 219 | CGFloat y = CGRectGetMinY(self.scanFrame)/ CGRectGetHeight(self.preview.frame); 220 | CGFloat width = CGRectGetWidth(self.scanFrame)/ CGRectGetWidth(self.preview.frame); 221 | CGFloat height = CGRectGetHeight(self.scanFrame)/ CGRectGetHeight(self.preview.frame); 222 | output.rectOfInterest = CGRectMake(y, x, height, width); 223 | 224 | // 创建环境光感输出流 225 | AVCaptureVideoDataOutput *lightOutput = [[AVCaptureVideoDataOutput alloc] init]; 226 | [lightOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; 227 | 228 | _session = [[AVCaptureSession alloc] init]; 229 | //高质量采集率 230 | [_session setSessionPreset:AVCaptureSessionPresetHigh]; 231 | [_session addInput:input]; 232 | [_session addOutput:output]; 233 | [_session addOutput:lightOutput]; 234 | 235 | //设置扫码支持的编码格式(这里设置条形码和二维码兼容) 236 | output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, 237 | AVMetadataObjectTypeEAN13Code, 238 | AVMetadataObjectTypeEAN8Code, 239 | AVMetadataObjectTypeCode128Code]; 240 | } 241 | 242 | return _session; 243 | } 244 | 245 | 246 | @end 247 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLScanView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSLScanView.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/2/28. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^WSLMyQRCodeBlock)(void); 12 | 13 | typedef void(^WSLFlashSwitchBlock)(BOOL open); 14 | 15 | @interface WSLScanView : UIView 16 | 17 | /** 18 | 点击我的二维码的回调 19 | */ 20 | @property (nonatomic,copy) WSLMyQRCodeBlock myQRCodeBlock; 21 | 22 | /** 23 | 打开/关闭闪光灯的回调 24 | */ 25 | @property (nonatomic,copy) WSLFlashSwitchBlock flashSwitchBlock; 26 | 27 | #pragma mark - 扫码区域 28 | 29 | /** 30 | 扫码区域 默认为正方形,x = 60, y = 100 31 | */ 32 | @property (nonatomic,assign)CGRect scanRetangleRect; 33 | /** 34 | @brief 是否需要绘制扫码矩形框,默认YES 35 | */ 36 | @property (nonatomic, assign) BOOL isNeedShowRetangle; 37 | /** 38 | @brief 矩形框线条颜色 39 | */ 40 | @property (nonatomic, strong, nullable) UIColor *colorRetangleLine; 41 | 42 | #pragma mark - 矩形框(扫码区域)周围4个角 43 | 44 | //4个角的颜色 45 | @property (nonatomic, strong, nullable) UIColor* colorAngle; 46 | //扫码区域4个角的宽度和高度 默认都为20 47 | @property (nonatomic, assign) CGFloat photoframeAngleW; 48 | @property (nonatomic, assign) CGFloat photoframeAngleH; 49 | /** 50 | @brief 扫码区域4个角的线条宽度,默认6 51 | */ 52 | @property (nonatomic, assign) CGFloat photoframeLineW; 53 | 54 | #pragma mark --动画效果 55 | 56 | /** 57 | * 动画效果的图像 58 | */ 59 | @property (nonatomic,strong, nullable) UIImage * animationImage; 60 | /** 61 | 非识别区域颜色,默认 RGBA (0,0,0,0.5) 62 | */ 63 | @property (nonatomic, strong, nullable) UIColor * notRecoginitonArea; 64 | 65 | /** 66 | * 开始扫描动画 67 | */ 68 | - (void)startScanAnimation; 69 | /** 70 | * 结束扫描动画 71 | */ 72 | - (void)stopScanAnimation; 73 | 74 | /** 75 | 正在处理扫描到的结果 76 | */ 77 | - (void)handlingResultsOfScan; 78 | 79 | /** 80 | 完成扫描结果处理 81 | */ 82 | - (void)finishedHandle; 83 | 84 | 85 | /** 86 | 是否显示闪光灯开关 87 | @param show YES or NO 88 | */ 89 | - (void)showFlashSwitch:(BOOL)show; 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/WSLScanView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSLScanView.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/2/28. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import "WSLScanView.h" 10 | 11 | 12 | @interface WSLScanView () 13 | 14 | //动画线条 15 | @property (nonatomic,strong) UIImageView * scanLine; 16 | //网络状态提示语 17 | @property (nonatomic,strong) UILabel * netLabel; 18 | 19 | /** 20 | 菊花等待 21 | */ 22 | @property(nonatomic,strong) UIActivityIndicatorView * activityView; 23 | /** 24 | 扫描结果处理中 25 | */ 26 | @property(nonatomic,strong) UIView * handlingView; 27 | /** 28 | 是否正在动画 29 | */ 30 | @property(nonatomic,assign) BOOL isAnimating; 31 | 32 | //手电筒开关 33 | @property(nonatomic,strong) UIButton * flashBtn; 34 | /** 35 | 闪光灯开关的状态 36 | */ 37 | @property (nonatomic, assign) BOOL flashOpen; 38 | 39 | @end 40 | 41 | @implementation WSLScanView 42 | 43 | -(instancetype)initWithFrame:(CGRect)frame{ 44 | 45 | if (self = [super initWithFrame:frame]) { 46 | self.backgroundColor = [UIColor clearColor]; 47 | _isNeedShowRetangle = YES; 48 | _photoframeLineW = 6; 49 | _notRecoginitonArea = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 50 | _scanRetangleRect = CGRectMake(60, 100, frame.size.width - 2 * 60, frame.size.width - 2 * 60); 51 | _photoframeAngleW = 20; 52 | _photoframeAngleH = 20; 53 | _colorAngle = [UIColor greenColor]; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)drawRect:(CGRect)rect{ 59 | 60 | [super drawRect:rect]; 61 | 62 | [self drawScanRect]; 63 | [self addSubview:self.scanLine]; 64 | 65 | // app退到后台 66 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterBackground) name:UIApplicationWillResignActiveNotification object:nil]; 67 | // app进入前台 68 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterPlayGround) name:UIApplicationDidBecomeActiveNotification object:nil]; 69 | 70 | UILabel * descLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height + 10, rect.size.width, 20)]; 71 | descLabel.font = [UIFont systemFontOfSize:12]; 72 | descLabel.textAlignment = NSTextAlignmentCenter; 73 | descLabel.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1.0]; 74 | descLabel.text = @"将二维码/条码放入框内,即可自动扫描"; 75 | [self addSubview:descLabel]; 76 | 77 | UIButton * myCode = [[UIButton alloc] initWithFrame:CGRectMake(0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height + 10 + 20 + 10, rect.size.width, 20)]; 78 | myCode.titleLabel.font = [UIFont systemFontOfSize:15]; 79 | [myCode setTitle:@"我的二维码" forState:UIControlStateNormal]; 80 | [myCode setTitleColor:[UIColor greenColor] forState:UIControlStateNormal]; 81 | [myCode addTarget:self action:@selector(myCodeClicked) forControlEvents:UIControlEventTouchUpInside]; 82 | [self addSubview:myCode]; 83 | 84 | } 85 | 86 | //绘制扫描区域 87 | - (void)drawScanRect{ 88 | 89 | CGSize sizeRetangle = self.scanRetangleRect.size; 90 | //扫码区域Y轴最小坐标 91 | CGFloat YMinRetangle = self.scanRetangleRect.origin.y; 92 | CGFloat YMaxRetangle = YMinRetangle + sizeRetangle.height; 93 | CGFloat XRetangleLeft = self.scanRetangleRect.origin.x; 94 | CGFloat XRetangleRight = self.frame.size.width - XRetangleLeft; 95 | 96 | CGContextRef context = UIGraphicsGetCurrentContext(); 97 | 98 | //非扫码区域半透明 99 | { 100 | //设置非识别区域颜色 101 | const CGFloat *components = CGColorGetComponents(self.notRecoginitonArea.CGColor); 102 | 103 | CGFloat red_notRecoginitonArea = components[0]; 104 | CGFloat green_notRecoginitonArea = components[1]; 105 | CGFloat blue_notRecoginitonArea = components[2]; 106 | CGFloat alpa_notRecoginitonArea = components[3]; 107 | 108 | CGContextSetRGBFillColor(context, red_notRecoginitonArea, green_notRecoginitonArea, 109 | blue_notRecoginitonArea, alpa_notRecoginitonArea); 110 | 111 | //填充矩形 112 | 113 | //扫码区域上面填充 114 | CGRect rect = CGRectMake(0, 0, self.frame.size.width, YMinRetangle); 115 | CGContextFillRect(context, rect); 116 | 117 | //扫码区域左边填充 118 | rect = CGRectMake(0, YMinRetangle, XRetangleLeft,sizeRetangle.height); 119 | CGContextFillRect(context, rect); 120 | 121 | //扫码区域右边填充 122 | rect = CGRectMake(XRetangleRight, YMinRetangle, XRetangleLeft,sizeRetangle.height); 123 | CGContextFillRect(context, rect); 124 | 125 | //扫码区域下面填充 126 | rect = CGRectMake(0, YMaxRetangle, self.frame.size.width,self.frame.size.height - YMaxRetangle); 127 | CGContextFillRect(context, rect); 128 | //执行绘画 129 | CGContextStrokePath(context); 130 | } 131 | 132 | if (self.isNeedShowRetangle){ 133 | //中间画矩形(正方形) 134 | CGContextSetStrokeColorWithColor(context, self.colorRetangleLine.CGColor); 135 | CGContextSetLineWidth(context, 1); 136 | 137 | CGContextAddRect(context, CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height)); 138 | CGContextStrokePath(context); 139 | } 140 | 141 | // _scanRetangleRect = CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height); 142 | 143 | //画矩形框4格外围相框角 144 | 145 | //相框角的宽度和高度 146 | int wAngle = self.photoframeAngleW; 147 | int hAngle = self.photoframeAngleH; 148 | //4个角的 线的宽度 149 | CGFloat linewidthAngle = self.photoframeLineW;// 经验参数:6和4 150 | 151 | //画扫码矩形以及周边半透明黑色坐标参数 152 | CGFloat diffAngle = 0.0f; 153 | //diffAngle = -linewidthAngle/3; //框外面4个角,与框有缝隙 154 | //diffAngle = linewidthAngle/3; //框4个角 在线上加4个角效果 155 | //diffAngle = 0;//与矩形框重合 156 | diffAngle = -linewidthAngle/3; 157 | 158 | CGContextSetStrokeColorWithColor(context, self.colorAngle.CGColor); 159 | CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); 160 | CGContextSetLineWidth(context, linewidthAngle); 161 | 162 | //顶点位置 163 | CGFloat leftX = XRetangleLeft - diffAngle; 164 | CGFloat topY = YMinRetangle - diffAngle; 165 | CGFloat rightX = XRetangleRight + diffAngle; 166 | CGFloat bottomY = YMaxRetangle + diffAngle; 167 | 168 | //左上角水平线 169 | CGContextMoveToPoint(context, leftX-linewidthAngle/2, topY); 170 | CGContextAddLineToPoint(context, leftX + wAngle, topY); 171 | //左上角垂直线 172 | CGContextMoveToPoint(context, leftX, topY-linewidthAngle/2); 173 | CGContextAddLineToPoint(context, leftX, topY+hAngle); 174 | 175 | //左下角水平线 176 | CGContextMoveToPoint(context, leftX-linewidthAngle/2, bottomY); 177 | CGContextAddLineToPoint(context, leftX + wAngle, bottomY); 178 | //左下角垂直线 179 | CGContextMoveToPoint(context, leftX, bottomY+linewidthAngle/2); 180 | CGContextAddLineToPoint(context, leftX, bottomY - hAngle); 181 | 182 | //右上角水平线 183 | CGContextMoveToPoint(context, rightX+linewidthAngle/2, topY); 184 | CGContextAddLineToPoint(context, rightX - wAngle, topY); 185 | //右上角垂直线 186 | CGContextMoveToPoint(context, rightX, topY-linewidthAngle/2); 187 | CGContextAddLineToPoint(context, rightX, topY + hAngle); 188 | 189 | //右下角水平线 190 | CGContextMoveToPoint(context, rightX+linewidthAngle/2, bottomY); 191 | CGContextAddLineToPoint(context, rightX - wAngle, bottomY); 192 | //右下角垂直线 193 | CGContextMoveToPoint(context, rightX, bottomY+linewidthAngle/2); 194 | CGContextAddLineToPoint(context, rightX, bottomY - hAngle); 195 | 196 | CGContextStrokePath(context); 197 | } 198 | 199 | #pragma mark -- Events Handle 200 | 201 | - (void)appWillEnterBackground{ 202 | [self stopScanAnimation]; 203 | } 204 | 205 | - (void)appWillEnterPlayGround{ 206 | [self startScanAnimation]; 207 | } 208 | 209 | - (void)myCodeClicked{ 210 | if(self.myQRCodeBlock != nil){ 211 | self.myQRCodeBlock(); 212 | } 213 | } 214 | 215 | - (void)startScan{ 216 | 217 | if (_isAnimating == NO) { 218 | return; 219 | } 220 | 221 | __weak __typeof(self) weakSelf = self; 222 | [UIView animateWithDuration:3.0 animations:^{ 223 | weakSelf.scanLine.frame = CGRectMake(weakSelf.scanRetangleRect.origin.x, weakSelf.scanRetangleRect.origin.y + weakSelf.scanRetangleRect.size.height - 2, weakSelf.scanRetangleRect.size.width, 2); 224 | } completion:^(BOOL finished){ 225 | if (finished == YES) { 226 | weakSelf.scanLine.frame = CGRectMake(weakSelf.scanRetangleRect.origin.x, weakSelf.scanRetangleRect.origin.y, weakSelf.scanRetangleRect.size.width, 2); 227 | [weakSelf performSelector:@selector(startScan) withObject:nil afterDelay:0.03]; 228 | } 229 | }]; 230 | 231 | } 232 | 233 | - (void)startScanAnimation{ 234 | if(_isAnimating){ 235 | return; 236 | } 237 | _isAnimating = YES; 238 | [self startScan]; 239 | } 240 | 241 | - (void)stopScanAnimation{ 242 | self.scanLine.frame = CGRectMake(self.scanRetangleRect.origin.x, self.scanRetangleRect.origin.y, self.scanRetangleRect.size.width, 2); 243 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startScan) object:nil]; 244 | _isAnimating = NO; 245 | [self.scanLine.layer removeAllAnimations]; 246 | } 247 | 248 | - (void)handlingResultsOfScan{ 249 | self.handlingView.center = CGPointMake(self.frame.size.width/2.0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height/2); 250 | [self addSubview:self.handlingView]; 251 | [self.activityView startAnimating]; 252 | } 253 | 254 | - (void)finishedHandle{ 255 | [self.activityView stopAnimating]; 256 | [self.activityView removeFromSuperview]; 257 | self.activityView = nil; 258 | [self.handlingView removeFromSuperview]; 259 | self.handlingView = nil; 260 | } 261 | 262 | - (void)flashBtnClicked:(UIButton *)flashBtn{ 263 | if (self.flashSwitchBlock != nil) { 264 | self.flashOpen = !self.flashOpen; 265 | self.flashSwitchBlock(self.flashOpen); 266 | } 267 | } 268 | 269 | - (void)showFlashSwitch:(BOOL)show{ 270 | if (show == YES) { 271 | self.flashBtn.hidden = NO; 272 | [self addSubview:self.flashBtn]; 273 | }else{ 274 | self.flashBtn.hidden = YES; 275 | [self.flashBtn removeFromSuperview]; 276 | } 277 | } 278 | 279 | #pragma mark -- Getter 280 | 281 | - (UIImageView *)scanLine{ 282 | if (_scanLine == nil) { 283 | _scanLine = [[UIImageView alloc] initWithFrame:CGRectMake(self.scanRetangleRect.origin.x, self.scanRetangleRect.origin.y, self.scanRetangleRect.size.width, 4)]; 284 | _scanLine.image = self.animationImage; 285 | _scanLine.contentMode = UIViewContentModeScaleToFill; 286 | } 287 | return _scanLine; 288 | } 289 | // 加载中指示器 290 | - (UIActivityIndicatorView *)activityView{ 291 | if (_activityView == nil) { 292 | _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; 293 | [_activityView startAnimating]; 294 | _activityView.frame = CGRectMake(0, 0, self.scanRetangleRect.size.width, 40); 295 | } 296 | return _activityView; 297 | } 298 | //正在处理视图 299 | - (UIView *)handlingView{ 300 | 301 | if (_handlingView == nil) { 302 | _handlingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.scanRetangleRect.size.width, 40 + 40)]; 303 | [_handlingView addSubview:self.activityView]; 304 | 305 | UILabel * handleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 40, _handlingView.frame.size.width, 40)]; 306 | handleLabel.font = [UIFont systemFontOfSize:12]; 307 | handleLabel.textAlignment = NSTextAlignmentCenter; 308 | handleLabel.textColor = [UIColor whiteColor]; 309 | handleLabel.text = @"正在处理..."; 310 | [_handlingView addSubview:handleLabel]; 311 | } 312 | return _handlingView; 313 | } 314 | 315 | - (UIButton *)flashBtn{ 316 | if (_flashBtn == nil) { 317 | _flashBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; 318 | [_flashBtn setImage:[UIImage imageNamed:@"scanFlashlight"] forState:UIControlStateNormal]; 319 | [_flashBtn addTarget:self action:@selector(flashBtnClicked:) forControlEvents:UIControlEventTouchUpInside]; 320 | _flashBtn.center = CGPointMake(self.frame.size.width/2.0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height - 40); 321 | } 322 | return _flashBtn; 323 | } 324 | 325 | 326 | - (void)dealloc{ 327 | [self stopScanAnimation]; 328 | [self finishedHandle]; 329 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 330 | } 331 | 332 | @end 333 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/piao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/ScanQRcodeDemo/ScanQRcode/piao.png -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/scanFlashlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/ScanQRcodeDemo/ScanQRcode/scanFlashlight@2x.png -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/scanFlashlight@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/ScanQRcodeDemo/ScanQRcode/scanFlashlight@3x.png -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcode/scanLine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/ScanQRcode/e64737f5b00a763500d3ed8cbfe8e42214194d6d/ScanQRcodeDemo/ScanQRcode/scanLine@2x.png -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcodeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcodeTests/ScanQRcodeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ScanQRcodeTests.m 3 | // ScanQRcodeTests 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ScanQRcodeTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ScanQRcodeTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcodeUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ScanQRcodeDemo/ScanQRcodeUITests/ScanQRcodeUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ScanQRcodeUITests.m 3 | // ScanQRcodeUITests 4 | // 5 | // Created by 王双龙 on 2018/1/24. 6 | // Copyright © 2018年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ScanQRcodeUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ScanQRcodeUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /WSLNativeScanTool.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint WSLNativeScanTool.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "WSLNativeScanTool" 19 | spec.version = "1.0" 20 | spec.summary = "iOS 封装原生二维码扫描和生成" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描工具,支持二维码的扫描、识别图中二维码、生成自定义颜色和中心图标的二维码、监测环境亮度、打开闪光灯这些功能;WSLScanView是参照微信封装的一个扫一扫界面,支持线条颜色、大小、动画图片、矩形扫描框样式的自定义;这个示例本身就是仿照微信的扫一扫功能实现的。 29 | DESC 30 | 31 | spec.homepage = "https://github.com/wsl2ls/ScanQRcode" 32 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See https://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | spec.license = "MIT" 43 | # spec.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | spec.author = { "wsl" => "15324956576@163.com" } 57 | # Or just: spec.author = "wsl" 58 | # spec.authors = { "wsl" => "15324956576@163.com" } 59 | spec.social_media_url = "https://www.jianshu.com/u/e15d1f644bea" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # spec.platform = :ios 68 | spec.platform = :ios, "8.0" 69 | 70 | # When using multiple platforms 71 | # spec.ios.deployment_target = "5.0" 72 | # spec.osx.deployment_target = "10.7" 73 | # spec.watchos.deployment_target = "2.0" 74 | # spec.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | spec.source = { :git => "https://github.com/wsl2ls/ScanQRcode.git", :tag => "v#{spec.version}" } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | spec.source_files = "WSLNativeScanTool", "WSLNativeScanTool/**/*.{h,m}" 95 | spec.exclude_files = "Classes/Exclude" 96 | 97 | # spec.public_header_files = "Classes/**/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # spec.resource = "icon.png" 109 | # spec.resources = "Resources/*.png" 110 | 111 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # spec.framework = "SomeFramework" 121 | # spec.frameworks = "SomeFramework", "AnotherFramework" 122 | 123 | # spec.library = "iconv" 124 | # spec.libraries = "iconv", "xml2" 125 | 126 | spec.requires_arc = true 127 | 128 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 129 | # spec.dependency "JSONKit", "~> 1.4" 130 | 131 | end 132 | -------------------------------------------------------------------------------- /WSLNativeScanTool/WSLNativeScanTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSLNativeScanTool.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/30. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | @import AVFoundation; 11 | 12 | #import 13 | 14 | /** 15 | 扫描完成的回调 16 | @param scanString 扫描出的字符串 17 | */ 18 | typedef void(^WSLScanFinishedBlock)( NSString * _Nullable scanString); 19 | 20 | /** 21 | 监听环境光感的回调 22 | @param brightness 亮度值 23 | */ 24 | typedef void(^WSLMonitorLightBlock)( float brightness); 25 | 26 | @interface WSLNativeScanTool : NSObject 27 | 28 | /** 29 | 扫描出结果后的回调 ,注意循环引用的问题 30 | */ 31 | @property (nonatomic, copy) WSLScanFinishedBlock _Nullable scanFinishedBlock; 32 | 33 | /** 34 | 监听环境光感的回调,如果 != nil 表示开启监测环境亮度功能 35 | */ 36 | @property (nonatomic, copy) WSLMonitorLightBlock _Nullable monitorLightBlock; 37 | 38 | /** 39 | 闪光灯的状态,不需要设置,仅供外边判断状态使用 40 | */ 41 | @property (nonatomic, assign) BOOL flashOpen; 42 | 43 | /** 44 | 初始化 扫描工具 45 | @param preview 展示输出流的视图 46 | @param scanFrame 扫描中心识别区域范围 47 | */ 48 | - (instancetype )initWithPreview:(UIView *)preview andScanFrame:(CGRect)scanFrame; 49 | 50 | /** 51 | 闪光灯开关 52 | */ 53 | - (void)openFlashSwitch:(BOOL)open; 54 | 55 | - (void)sessionStartRunning; 56 | 57 | - (void)sessionStopRunning; 58 | 59 | /** 60 | 识别图中二维码 61 | */ 62 | - (void)scanImageQRCode:(UIImage *_Nullable)imageCode; 63 | 64 | /** 65 | 生成自定义样式二维码 66 | 注意:有些颜色结合生成的二维码识别不了 67 | @param codeString 字符串 68 | @param size 大小 69 | @param backColor 背景色 70 | @param frontColor 前景色 71 | @param centerImage 中心图片 72 | @return image二维码 73 | */ 74 | + (UIImage *)createQRCodeImageWithString:(nonnull NSString *)codeString andSize:(CGSize)size andBackColor:(nullable UIColor *)backColor andFrontColor:(nullable UIColor *)frontColor andCenterImage:(nullable UIImage *)centerImage; 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /WSLNativeScanTool/WSLNativeScanTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSLNativeScanTool.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/1/30. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import "WSLNativeScanTool.h" 10 | 11 | @interface WSLNativeScanTool() 12 | 13 | @property (nonatomic, strong) AVCaptureSession * session; 14 | 15 | /** 16 | 扫描中心识别区域范围 17 | */ 18 | @property (nonatomic, assign) CGRect scanFrame; 19 | 20 | /** 21 | 展示输出流的视图——即照相机镜头下的内容 22 | */ 23 | @property (nonatomic, strong) UIView *preview; 24 | 25 | @end 26 | 27 | @implementation WSLNativeScanTool 28 | 29 | - (instancetype)initWithPreview:(UIView *)preview andScanFrame:(CGRect)scanFrame{ 30 | 31 | if (self == [super init]) { 32 | self.preview = preview; 33 | self.scanFrame = scanFrame; 34 | [self configuredScanTool]; 35 | } 36 | return self; 37 | } 38 | 39 | #pragma mark -- Help Methods 40 | 41 | //初始化采集配置信息 42 | - (void)configuredScanTool{ 43 | 44 | AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; 45 | layer.videoGravity = AVLayerVideoGravityResizeAspectFill; 46 | layer.frame = self.preview.layer.bounds; 47 | [self.preview.layer insertSublayer:layer atIndex:0]; 48 | 49 | } 50 | 51 | #pragma mark -- Event Handel 52 | 53 | - (void)openFlashSwitch:(BOOL)open{ 54 | if (self.flashOpen == open) { 55 | return; 56 | } 57 | self.flashOpen = open; 58 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 59 | 60 | if ([device hasTorch] && [device hasFlash]){ 61 | 62 | [device lockForConfiguration:nil]; 63 | if (self.flashOpen){ 64 | device.torchMode = AVCaptureTorchModeOn; 65 | device.flashMode = AVCaptureFlashModeOn; 66 | } 67 | else{ 68 | device.torchMode = AVCaptureTorchModeOff; 69 | device.flashMode = AVCaptureFlashModeOff; 70 | } 71 | 72 | [device unlockForConfiguration]; 73 | } 74 | 75 | } 76 | 77 | - (void)sessionStartRunning{ 78 | [_session startRunning]; 79 | } 80 | 81 | - (void)sessionStopRunning{ 82 | [_session stopRunning]; 83 | } 84 | 85 | 86 | - (void)scanImageQRCode:(UIImage *)imageCode{ 87 | 88 | CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode 89 | context:nil 90 | options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}]; 91 | NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:imageCode.CGImage]]; 92 | if (features.count >= 1){ 93 | CIQRCodeFeature *feature = [features firstObject]; 94 | if(self.scanFinishedBlock != nil){ 95 | self.scanFinishedBlock(feature.messageString); 96 | } 97 | }else{ 98 | NSLog(@"无法识别图中二维码"); 99 | } 100 | } 101 | 102 | + (UIImage *)createQRCodeImageWithString:(nonnull NSString *)codeString andSize:(CGSize)size andBackColor:(nullable UIColor *)backColor andFrontColor:(nullable UIColor *)frontColor andCenterImage:(nullable UIImage *)centerImage{ 103 | 104 | NSData *stringData = [codeString dataUsingEncoding:NSUTF8StringEncoding]; 105 | 106 | CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; 107 | // NSLog(@"%@",qrFilter.inputKeys); 108 | [qrFilter setValue:stringData forKey:@"inputMessage"]; 109 | [qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"]; 110 | 111 | CIImage *qrImage = qrFilter.outputImage; 112 | //放大并绘制二维码 (上面生成的二维码很小,需要放大) 113 | CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent]; 114 | UIGraphicsBeginImageContext(size); 115 | CGContextRef context = UIGraphicsGetCurrentContext(); 116 | CGContextSetInterpolationQuality(context, kCGInterpolationNone); 117 | //翻转一下图片 不然生成的QRCode就是上下颠倒的 118 | CGContextScaleCTM(context, 1.0, -1.0); 119 | CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage); 120 | UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext(); 121 | UIGraphicsEndImageContext(); 122 | 123 | CGImageRelease(cgImage); 124 | 125 | //绘制颜色 126 | CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor" 127 | keysAndValues: 128 | @"inputImage",[CIImage imageWithCGImage:codeImage.CGImage], 129 | @"inputColor0",[CIColor colorWithCGColor:frontColor == nil ? [UIColor clearColor].CGColor: frontColor.CGColor], 130 | @"inputColor1",[CIColor colorWithCGColor: backColor == nil ? [UIColor blackColor].CGColor : backColor.CGColor], 131 | nil]; 132 | 133 | UIImage * colorCodeImage = [UIImage imageWithCIImage:colorFilter.outputImage]; 134 | 135 | //中心添加图片 136 | if (centerImage != nil) { 137 | 138 | UIGraphicsBeginImageContext(colorCodeImage.size); 139 | 140 | [colorCodeImage drawInRect:CGRectMake(0, 0, colorCodeImage.size.width, colorCodeImage.size.height)]; 141 | 142 | UIImage *image = centerImage; 143 | 144 | CGFloat imageW = 50; 145 | CGFloat imageX = (colorCodeImage.size.width - imageW) * 0.5; 146 | CGFloat imgaeY = (colorCodeImage.size.height - imageW) * 0.5; 147 | 148 | [image drawInRect:CGRectMake(imageX, imgaeY, imageW, imageW)]; 149 | 150 | UIImage *centerImageCode = UIGraphicsGetImageFromCurrentImageContext(); 151 | UIGraphicsEndImageContext(); 152 | return centerImageCode; 153 | } 154 | return colorCodeImage; 155 | } 156 | 157 | #pragma mark -- AVCaptureMetadataOutputObjectsDelegate 158 | //扫描完成后执行 159 | -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{ 160 | 161 | if (metadataObjects.count > 0){ 162 | AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject]; 163 | // 扫描完成后的字符 164 | // NSLog(@"扫描出 %@",metadataObject.stringValue); 165 | if(self.scanFinishedBlock != nil){ 166 | self.scanFinishedBlock(metadataObject.stringValue); 167 | } 168 | } 169 | } 170 | #pragma mark- AVCaptureVideoDataOutputSampleBufferDelegate的方法 171 | //扫描过程中执行,主要用来判断环境的黑暗程度 172 | - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection{ 173 | 174 | if (self.monitorLightBlock == nil) { 175 | return; 176 | } 177 | 178 | CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate); 179 | NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict]; 180 | CFRelease(metadataDict); 181 | NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy]; 182 | float brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue]; 183 | 184 | // NSLog(@"环境光感 : %f",brightnessValue); 185 | 186 | // 根据brightnessValue的值来判断是否需要打开和关闭闪光灯 187 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 188 | BOOL result = [device hasTorch];// 判断设备是否有闪光灯 189 | if ((brightnessValue < 0) && result) { 190 | // 环境太暗,可以打开闪光灯了 191 | }else if((brightnessValue > 0) && result){ 192 | // 环境亮度可以 193 | } 194 | if (self.monitorLightBlock != nil) { 195 | self.monitorLightBlock(brightnessValue); 196 | } 197 | 198 | } 199 | 200 | #pragma mark - Getter 201 | - (AVCaptureSession *)session{ 202 | 203 | if (_session == nil){ 204 | //获取摄像设备 205 | AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 206 | //创建输入流 207 | AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; 208 | if (!input){ 209 | return nil; 210 | } 211 | 212 | //创建二维码扫描输出流 213 | AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; 214 | //设置代理 在主线程里刷新 215 | [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; 216 | //设置采集扫描区域的比例 默认全屏是(0,0,1,1) 217 | //rectOfInterest 填写的是一个比例,输出流视图preview.frame为 x , y, w, h, 要设置的矩形快的scanFrame 为 x1, y1, w1, h1. 那么rectOfInterest 应该设置为 CGRectMake(y1/y, x1/x, h1/h, w1/w)。 218 | CGFloat x = CGRectGetMinX(self.scanFrame)/ CGRectGetWidth(self.preview.frame); 219 | CGFloat y = CGRectGetMinY(self.scanFrame)/ CGRectGetHeight(self.preview.frame); 220 | CGFloat width = CGRectGetWidth(self.scanFrame)/ CGRectGetWidth(self.preview.frame); 221 | CGFloat height = CGRectGetHeight(self.scanFrame)/ CGRectGetHeight(self.preview.frame); 222 | output.rectOfInterest = CGRectMake(y, x, height, width); 223 | 224 | // 创建环境光感输出流 225 | AVCaptureVideoDataOutput *lightOutput = [[AVCaptureVideoDataOutput alloc] init]; 226 | [lightOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; 227 | 228 | _session = [[AVCaptureSession alloc] init]; 229 | //高质量采集率 230 | [_session setSessionPreset:AVCaptureSessionPresetHigh]; 231 | [_session addInput:input]; 232 | [_session addOutput:output]; 233 | [_session addOutput:lightOutput]; 234 | 235 | //设置扫码支持的编码格式(这里设置条形码和二维码兼容) 236 | output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, 237 | AVMetadataObjectTypeEAN13Code, 238 | AVMetadataObjectTypeEAN8Code, 239 | AVMetadataObjectTypeCode128Code]; 240 | } 241 | 242 | return _session; 243 | } 244 | 245 | 246 | @end 247 | -------------------------------------------------------------------------------- /WSLNativeScanTool/WSLScanView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WSLScanView.h 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/2/28. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^WSLMyQRCodeBlock)(void); 12 | 13 | typedef void(^WSLFlashSwitchBlock)(BOOL open); 14 | 15 | @interface WSLScanView : UIView 16 | 17 | /** 18 | 点击我的二维码的回调 19 | */ 20 | @property (nonatomic,copy) WSLMyQRCodeBlock myQRCodeBlock; 21 | 22 | /** 23 | 打开/关闭闪光灯的回调 24 | */ 25 | @property (nonatomic,copy) WSLFlashSwitchBlock flashSwitchBlock; 26 | 27 | #pragma mark - 扫码区域 28 | 29 | /** 30 | 扫码区域 默认为正方形,x = 60, y = 100 31 | */ 32 | @property (nonatomic,assign)CGRect scanRetangleRect; 33 | /** 34 | @brief 是否需要绘制扫码矩形框,默认YES 35 | */ 36 | @property (nonatomic, assign) BOOL isNeedShowRetangle; 37 | /** 38 | @brief 矩形框线条颜色 39 | */ 40 | @property (nonatomic, strong, nullable) UIColor *colorRetangleLine; 41 | 42 | #pragma mark - 矩形框(扫码区域)周围4个角 43 | 44 | //4个角的颜色 45 | @property (nonatomic, strong, nullable) UIColor* colorAngle; 46 | //扫码区域4个角的宽度和高度 默认都为20 47 | @property (nonatomic, assign) CGFloat photoframeAngleW; 48 | @property (nonatomic, assign) CGFloat photoframeAngleH; 49 | /** 50 | @brief 扫码区域4个角的线条宽度,默认6 51 | */ 52 | @property (nonatomic, assign) CGFloat photoframeLineW; 53 | 54 | #pragma mark --动画效果 55 | 56 | /** 57 | * 动画效果的图像 58 | */ 59 | @property (nonatomic,strong, nullable) UIImage * animationImage; 60 | /** 61 | 非识别区域颜色,默认 RGBA (0,0,0,0.5) 62 | */ 63 | @property (nonatomic, strong, nullable) UIColor * notRecoginitonArea; 64 | 65 | /** 66 | * 开始扫描动画 67 | */ 68 | - (void)startScanAnimation; 69 | /** 70 | * 结束扫描动画 71 | */ 72 | - (void)stopScanAnimation; 73 | 74 | /** 75 | 正在处理扫描到的结果 76 | */ 77 | - (void)handlingResultsOfScan; 78 | 79 | /** 80 | 完成扫描结果处理 81 | */ 82 | - (void)finishedHandle; 83 | 84 | 85 | /** 86 | 是否显示闪光灯开关 87 | @param show YES or NO 88 | */ 89 | - (void)showFlashSwitch:(BOOL)show; 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /WSLNativeScanTool/WSLScanView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WSLScanView.m 3 | // ScanQRcode 4 | // 5 | // Created by 王双龙 on 2018/2/28. 6 | // Copyright © 2018年 https://www.jianshu.com/u/e15d1f644bea All rights reserved. 7 | // 8 | 9 | #import "WSLScanView.h" 10 | 11 | 12 | @interface WSLScanView () 13 | 14 | //动画线条 15 | @property (nonatomic,strong) UIImageView * scanLine; 16 | //网络状态提示语 17 | @property (nonatomic,strong) UILabel * netLabel; 18 | 19 | /** 20 | 菊花等待 21 | */ 22 | @property(nonatomic,strong) UIActivityIndicatorView * activityView; 23 | /** 24 | 扫描结果处理中 25 | */ 26 | @property(nonatomic,strong) UIView * handlingView; 27 | /** 28 | 是否正在动画 29 | */ 30 | @property(nonatomic,assign) BOOL isAnimating; 31 | 32 | //手电筒开关 33 | @property(nonatomic,strong) UIButton * flashBtn; 34 | /** 35 | 闪光灯开关的状态 36 | */ 37 | @property (nonatomic, assign) BOOL flashOpen; 38 | 39 | @end 40 | 41 | @implementation WSLScanView 42 | 43 | -(instancetype)initWithFrame:(CGRect)frame{ 44 | 45 | if (self = [super initWithFrame:frame]) { 46 | self.backgroundColor = [UIColor clearColor]; 47 | _isNeedShowRetangle = YES; 48 | _photoframeLineW = 6; 49 | _notRecoginitonArea = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; 50 | _scanRetangleRect = CGRectMake(60, 100, frame.size.width - 2 * 60, frame.size.width - 2 * 60); 51 | _photoframeAngleW = 20; 52 | _photoframeAngleH = 20; 53 | _colorAngle = [UIColor greenColor]; 54 | } 55 | return self; 56 | } 57 | 58 | - (void)drawRect:(CGRect)rect{ 59 | 60 | [super drawRect:rect]; 61 | 62 | [self drawScanRect]; 63 | [self addSubview:self.scanLine]; 64 | 65 | // app退到后台 66 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterBackground) name:UIApplicationWillResignActiveNotification object:nil]; 67 | // app进入前台 68 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterPlayGround) name:UIApplicationDidBecomeActiveNotification object:nil]; 69 | 70 | UILabel * descLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height + 10, rect.size.width, 20)]; 71 | descLabel.font = [UIFont systemFontOfSize:12]; 72 | descLabel.textAlignment = NSTextAlignmentCenter; 73 | descLabel.textColor = [UIColor colorWithRed:153/255.0 green:153/255.0 blue:153/255.0 alpha:1.0]; 74 | descLabel.text = @"将二维码/条码放入框内,即可自动扫描"; 75 | [self addSubview:descLabel]; 76 | 77 | UIButton * myCode = [[UIButton alloc] initWithFrame:CGRectMake(0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height + 10 + 20 + 10, rect.size.width, 20)]; 78 | myCode.titleLabel.font = [UIFont systemFontOfSize:15]; 79 | [myCode setTitle:@"我的二维码" forState:UIControlStateNormal]; 80 | [myCode setTitleColor:[UIColor greenColor] forState:UIControlStateNormal]; 81 | [myCode addTarget:self action:@selector(myCodeClicked) forControlEvents:UIControlEventTouchUpInside]; 82 | [self addSubview:myCode]; 83 | 84 | } 85 | 86 | //绘制扫描区域 87 | - (void)drawScanRect{ 88 | 89 | CGSize sizeRetangle = self.scanRetangleRect.size; 90 | //扫码区域Y轴最小坐标 91 | CGFloat YMinRetangle = self.scanRetangleRect.origin.y; 92 | CGFloat YMaxRetangle = YMinRetangle + sizeRetangle.height; 93 | CGFloat XRetangleLeft = self.scanRetangleRect.origin.x; 94 | CGFloat XRetangleRight = self.frame.size.width - XRetangleLeft; 95 | 96 | CGContextRef context = UIGraphicsGetCurrentContext(); 97 | 98 | //非扫码区域半透明 99 | { 100 | //设置非识别区域颜色 101 | const CGFloat *components = CGColorGetComponents(self.notRecoginitonArea.CGColor); 102 | 103 | CGFloat red_notRecoginitonArea = components[0]; 104 | CGFloat green_notRecoginitonArea = components[1]; 105 | CGFloat blue_notRecoginitonArea = components[2]; 106 | CGFloat alpa_notRecoginitonArea = components[3]; 107 | 108 | CGContextSetRGBFillColor(context, red_notRecoginitonArea, green_notRecoginitonArea, 109 | blue_notRecoginitonArea, alpa_notRecoginitonArea); 110 | 111 | //填充矩形 112 | 113 | //扫码区域上面填充 114 | CGRect rect = CGRectMake(0, 0, self.frame.size.width, YMinRetangle); 115 | CGContextFillRect(context, rect); 116 | 117 | //扫码区域左边填充 118 | rect = CGRectMake(0, YMinRetangle, XRetangleLeft,sizeRetangle.height); 119 | CGContextFillRect(context, rect); 120 | 121 | //扫码区域右边填充 122 | rect = CGRectMake(XRetangleRight, YMinRetangle, XRetangleLeft,sizeRetangle.height); 123 | CGContextFillRect(context, rect); 124 | 125 | //扫码区域下面填充 126 | rect = CGRectMake(0, YMaxRetangle, self.frame.size.width,self.frame.size.height - YMaxRetangle); 127 | CGContextFillRect(context, rect); 128 | //执行绘画 129 | CGContextStrokePath(context); 130 | } 131 | 132 | if (self.isNeedShowRetangle){ 133 | //中间画矩形(正方形) 134 | CGContextSetStrokeColorWithColor(context, self.colorRetangleLine.CGColor); 135 | CGContextSetLineWidth(context, 1); 136 | 137 | CGContextAddRect(context, CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height)); 138 | CGContextStrokePath(context); 139 | } 140 | 141 | // _scanRetangleRect = CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height); 142 | 143 | //画矩形框4格外围相框角 144 | 145 | //相框角的宽度和高度 146 | int wAngle = self.photoframeAngleW; 147 | int hAngle = self.photoframeAngleH; 148 | //4个角的 线的宽度 149 | CGFloat linewidthAngle = self.photoframeLineW;// 经验参数:6和4 150 | 151 | //画扫码矩形以及周边半透明黑色坐标参数 152 | CGFloat diffAngle = 0.0f; 153 | //diffAngle = -linewidthAngle/3; //框外面4个角,与框有缝隙 154 | //diffAngle = linewidthAngle/3; //框4个角 在线上加4个角效果 155 | //diffAngle = 0;//与矩形框重合 156 | diffAngle = -linewidthAngle/3; 157 | 158 | CGContextSetStrokeColorWithColor(context, self.colorAngle.CGColor); 159 | CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0); 160 | CGContextSetLineWidth(context, linewidthAngle); 161 | 162 | //顶点位置 163 | CGFloat leftX = XRetangleLeft - diffAngle; 164 | CGFloat topY = YMinRetangle - diffAngle; 165 | CGFloat rightX = XRetangleRight + diffAngle; 166 | CGFloat bottomY = YMaxRetangle + diffAngle; 167 | 168 | //左上角水平线 169 | CGContextMoveToPoint(context, leftX-linewidthAngle/2, topY); 170 | CGContextAddLineToPoint(context, leftX + wAngle, topY); 171 | //左上角垂直线 172 | CGContextMoveToPoint(context, leftX, topY-linewidthAngle/2); 173 | CGContextAddLineToPoint(context, leftX, topY+hAngle); 174 | 175 | //左下角水平线 176 | CGContextMoveToPoint(context, leftX-linewidthAngle/2, bottomY); 177 | CGContextAddLineToPoint(context, leftX + wAngle, bottomY); 178 | //左下角垂直线 179 | CGContextMoveToPoint(context, leftX, bottomY+linewidthAngle/2); 180 | CGContextAddLineToPoint(context, leftX, bottomY - hAngle); 181 | 182 | //右上角水平线 183 | CGContextMoveToPoint(context, rightX+linewidthAngle/2, topY); 184 | CGContextAddLineToPoint(context, rightX - wAngle, topY); 185 | //右上角垂直线 186 | CGContextMoveToPoint(context, rightX, topY-linewidthAngle/2); 187 | CGContextAddLineToPoint(context, rightX, topY + hAngle); 188 | 189 | //右下角水平线 190 | CGContextMoveToPoint(context, rightX+linewidthAngle/2, bottomY); 191 | CGContextAddLineToPoint(context, rightX - wAngle, bottomY); 192 | //右下角垂直线 193 | CGContextMoveToPoint(context, rightX, bottomY+linewidthAngle/2); 194 | CGContextAddLineToPoint(context, rightX, bottomY - hAngle); 195 | 196 | CGContextStrokePath(context); 197 | } 198 | 199 | #pragma mark -- Events Handle 200 | 201 | - (void)appWillEnterBackground{ 202 | [self stopScanAnimation]; 203 | } 204 | 205 | - (void)appWillEnterPlayGround{ 206 | [self startScanAnimation]; 207 | } 208 | 209 | - (void)myCodeClicked{ 210 | if(self.myQRCodeBlock != nil){ 211 | self.myQRCodeBlock(); 212 | } 213 | } 214 | 215 | - (void)startScan{ 216 | 217 | if (_isAnimating == NO) { 218 | return; 219 | } 220 | 221 | __weak __typeof(self) weakSelf = self; 222 | [UIView animateWithDuration:3.0 animations:^{ 223 | weakSelf.scanLine.frame = CGRectMake(weakSelf.scanRetangleRect.origin.x, weakSelf.scanRetangleRect.origin.y + weakSelf.scanRetangleRect.size.height - 2, weakSelf.scanRetangleRect.size.width, 2); 224 | } completion:^(BOOL finished){ 225 | if (finished == YES) { 226 | weakSelf.scanLine.frame = CGRectMake(weakSelf.scanRetangleRect.origin.x, weakSelf.scanRetangleRect.origin.y, weakSelf.scanRetangleRect.size.width, 2); 227 | [weakSelf performSelector:@selector(startScan) withObject:nil afterDelay:0.03]; 228 | } 229 | }]; 230 | 231 | } 232 | 233 | - (void)startScanAnimation{ 234 | if(_isAnimating){ 235 | return; 236 | } 237 | _isAnimating = YES; 238 | [self startScan]; 239 | } 240 | 241 | - (void)stopScanAnimation{ 242 | self.scanLine.frame = CGRectMake(self.scanRetangleRect.origin.x, self.scanRetangleRect.origin.y, self.scanRetangleRect.size.width, 2); 243 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startScan) object:nil]; 244 | _isAnimating = NO; 245 | [self.scanLine.layer removeAllAnimations]; 246 | } 247 | 248 | - (void)handlingResultsOfScan{ 249 | self.handlingView.center = CGPointMake(self.frame.size.width/2.0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height/2); 250 | [self addSubview:self.handlingView]; 251 | [self.activityView startAnimating]; 252 | } 253 | 254 | - (void)finishedHandle{ 255 | [self.activityView stopAnimating]; 256 | [self.activityView removeFromSuperview]; 257 | self.activityView = nil; 258 | [self.handlingView removeFromSuperview]; 259 | self.handlingView = nil; 260 | } 261 | 262 | - (void)flashBtnClicked:(UIButton *)flashBtn{ 263 | if (self.flashSwitchBlock != nil) { 264 | self.flashOpen = !self.flashOpen; 265 | self.flashSwitchBlock(self.flashOpen); 266 | } 267 | } 268 | 269 | - (void)showFlashSwitch:(BOOL)show{ 270 | if (show == YES) { 271 | self.flashBtn.hidden = NO; 272 | [self addSubview:self.flashBtn]; 273 | }else{ 274 | self.flashBtn.hidden = YES; 275 | [self.flashBtn removeFromSuperview]; 276 | } 277 | } 278 | 279 | #pragma mark -- Getter 280 | 281 | - (UIImageView *)scanLine{ 282 | if (_scanLine == nil) { 283 | _scanLine = [[UIImageView alloc] initWithFrame:CGRectMake(self.scanRetangleRect.origin.x, self.scanRetangleRect.origin.y, self.scanRetangleRect.size.width, 4)]; 284 | _scanLine.image = self.animationImage; 285 | _scanLine.contentMode = UIViewContentModeScaleToFill; 286 | } 287 | return _scanLine; 288 | } 289 | // 加载中指示器 290 | - (UIActivityIndicatorView *)activityView{ 291 | if (_activityView == nil) { 292 | _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; 293 | [_activityView startAnimating]; 294 | _activityView.frame = CGRectMake(0, 0, self.scanRetangleRect.size.width, 40); 295 | } 296 | return _activityView; 297 | } 298 | //正在处理视图 299 | - (UIView *)handlingView{ 300 | 301 | if (_handlingView == nil) { 302 | _handlingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.scanRetangleRect.size.width, 40 + 40)]; 303 | [_handlingView addSubview:self.activityView]; 304 | 305 | UILabel * handleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 40, _handlingView.frame.size.width, 40)]; 306 | handleLabel.font = [UIFont systemFontOfSize:12]; 307 | handleLabel.textAlignment = NSTextAlignmentCenter; 308 | handleLabel.textColor = [UIColor whiteColor]; 309 | handleLabel.text = @"正在处理..."; 310 | [_handlingView addSubview:handleLabel]; 311 | } 312 | return _handlingView; 313 | } 314 | 315 | - (UIButton *)flashBtn{ 316 | if (_flashBtn == nil) { 317 | _flashBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 40, 40)]; 318 | [_flashBtn setImage:[UIImage imageNamed:@"scanFlashlight"] forState:UIControlStateNormal]; 319 | [_flashBtn addTarget:self action:@selector(flashBtnClicked:) forControlEvents:UIControlEventTouchUpInside]; 320 | _flashBtn.center = CGPointMake(self.frame.size.width/2.0, self.scanRetangleRect.origin.y + self.scanRetangleRect.size.height - 40); 321 | } 322 | return _flashBtn; 323 | } 324 | 325 | 326 | - (void)dealloc{ 327 | [self stopScanAnimation]; 328 | [self finishedHandle]; 329 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 330 | } 331 | 332 | @end 333 | --------------------------------------------------------------------------------