├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── SCTouchDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── xiaoguang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── xiaoguang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── SCTouchDemo.xcscheme │ └── xcschememanagement.plist ├── SCTouchDemo.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── xiaoguang.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── SCTouchDemo ├── AppDelegate.h ├── AppDelegate.m ├── Category │ ├── CALayer+shake.h │ ├── CALayer+shake.m │ ├── GCDDelay.h │ ├── GCDDelay.m │ ├── UIView+Ext.h │ └── UIView+Ext.m ├── GestureTouchID │ ├── SCLoginVerifyView.h │ ├── SCLoginVerifyView.m │ ├── fingerprint.png │ └── head_Img.JPG ├── GestureView │ ├── MYZCircleView.h │ ├── MYZCircleView.m │ ├── MYZGestureShapeView.h │ ├── MYZGestureShapeView.m │ ├── MYZGestureView.h │ └── MYZGestureView.m ├── Helper │ ├── SCSecureHelper.h │ └── SCSecureHelper.m ├── Resources │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── scangreen.imageset │ │ │ ├── Contents.json │ │ │ ├── scangreen@2x.png │ │ │ └── scangreen@3x.png │ │ └── searcharrow.imageset │ │ │ ├── Contents.json │ │ │ ├── searcharrow@2x.png │ │ │ └── searcharrow@3x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── SCHeader.h │ ├── SCTouchDemo.pch │ └── main.m ├── ViewController.h ├── ViewController.m ├── ViewControllers │ ├── SCGestureController.h │ ├── SCGestureController.m │ ├── SCGestureController.xib │ ├── SCGestureSetController.h │ ├── SCGestureSetController.m │ ├── SCTouchIDController.h │ ├── SCTouchIDController.m │ └── SCTouchIDController.xib └── Views │ ├── SCMainCell.h │ ├── SCMainCell.m │ ├── SCMainCell.xib │ ├── SCNormalCell.h │ ├── SCNormalCell.m │ ├── SCNormalCell.xib │ ├── SCSwitchCell.h │ ├── SCSwitchCell.m │ └── SCSwitchCell.xib ├── SCTouchDemoTests ├── Info.plist └── SCTouchDemoTests.m ├── SCTouchDemoUITests ├── Info.plist └── SCTouchDemoUITests.m └── images └── develop.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Xcode ### 4 | build 5 | *.xcodeproj/* 6 | !*.xcodeproj/project.pbxproj 7 | !*.xcworkspace/contents.xcworkspacedata 8 | 9 | 10 | ### Objective-C ### 11 | # OS X 12 | .DS_Store 13 | 14 | # Xcode 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | profile 27 | *.moved-aside 28 | DerivedData 29 | *.hmap 30 | *.ipa 31 | 32 | 33 | 34 | # CocoaPods 35 | Pods 36 | 37 | 38 | Pods 39 | #Podfile.lock 40 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | 3 | platform :ios, '8.0' 4 | #use_frameworks! 5 | 6 | target 'SCTouchDemo' do 7 | pod 'SVProgressHUD' 8 | end 9 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SVProgressHUD (2.0.4) 3 | 4 | DEPENDENCIES: 5 | - SVProgressHUD 6 | 7 | SPEC CHECKSUMS: 8 | SVProgressHUD: bfde85ed6c573ea802b9cb37894ed9b629034f1b 9 | 10 | PODFILE CHECKSUM: 7479e9cdb66e2bbf5bcfb38f75297b777e069a14 11 | 12 | COCOAPODS: 1.1.1 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 主要内容 2 | 手势密码解锁和指纹TouchID解锁的demo,逐步修改中... 3 | 使用了pod管理,下载后,pod install一下,就可以运行了; 4 | ### 注意 5 | 设置TouchID,需要在真机模式下设置,模拟器无法设置; 6 | 保存密码解锁类型和状态用的是NSUserDefaults存储,如果设置好解锁类型或状态,立即重新运行会出现保存失败的bug, 7 | 经查相关文章,这是Xcode的bug,调试的时候断开Xcode链接,则无此bug; 8 | ###效果如图 9 | 10 | ![image](https://github.com/XGPASS/XGTouchDemo/blob/master/images/develop.gif) 11 | 12 | #### 现在略微来讲解一下TouchID的使用 13 | ## 1.系统所使用TouchID的SDK 14 | 添加引入```LocalAuthentication.framework``` 15 | framework主要的内容是这个几个类 16 | ``` 17 | - LAContext.h 18 | - LAError.h 19 | - LAPublicDefines.h 20 | - LocalAuthentication.h 21 | ``` 22 | 使用的时候直接 23 | ``` 24 | #import 25 | ``` 26 | 好了,废话少说,下面来讲主要使用的2个类; 27 | ## LAError.h 28 | 错误类型的枚举类,其实是把```LAPublicDefines```里的kLAError宏放入到了这枚举类中,统一了一下,具体代码注释写的很清晰,在这我加点中文翻译 29 | ```objective-c 30 | 31 | typedef NS_ENUM(NSInteger, LAError) 32 | { 33 | LAErrorAuthenticationFailed, // 验证信息出错,就是说你指纹不对 34 | LAErrorUserCancel // 用户取消了验证 35 | LAErrorUserFallback // 用户点击了手动输入密码的按钮,所以被取消了 36 | LAErrorSystemCancel // 被系统取消,就是说你现在进入别的应用了,不在刚刚那个页面,所以没法验证 37 | LAErrorPasscodeNotSet // 用户没有设置TouchID 38 | LAErrorTouchIDNotAvailable // 用户设备不支持TouchID 39 | LAErrorTouchIDNotEnrolled // 用户没有设置手指指纹 40 | LAErrorTouchIDLockout // 用户错误次数太多,现在被锁住了 41 | LAErrorAppCancel // 在验证中被其他app中断 42 | LAErrorInvalidContext // 请求验证出错 43 | } NS_ENUM_AVAILABLE(10_10, 8_0); 44 | 45 | ``` 46 | 47 | ## LAContext.h 48 | 想要在自己的项目中使用TouchID,就要用到```LAContext.h```这个类, 49 | 最上面的一个枚举 50 | ```objective-c 51 | typedef NS_ENUM(NSInteger, LAPolicy) 52 | { 53 | LAPolicyDeviceOwnerAuthenticationWithBiometrics NS_ENUM_AVAILABLE(NA, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0) = kLAPolicyDeviceOwnerAuthenticationWithBiometrics, 54 | LAPolicyDeviceOwnerAuthentication NS_ENUM_AVAILABLE(10_11, 9_0) = kLAPolicyDeviceOwnerAuthentication 55 | 56 | } NS_ENUM_AVAILABLE(10_10, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0); 57 | ``` 58 | ``` 59 | 第一个枚举LAPolicyDeviceOwnerAuthenticationWithBiometrics就是说,用的是手指指纹去验证的;NS_ENUM_AVAILABLE(NA, 8_0)iOS8 可用 60 | 第二个枚举LAPolicyDeviceOwnerAuthentication少了WithBiometrics则是使用TouchID或者密码验证,默认是错误两次指纹或者锁定后,弹出输入密码界面;NS_ENUM_AVAILABLE(10_11, 9_0)iOS 9可用 61 | ``` 62 | 接下来是几个实例方法,首先创建```LAContext```实例对象,使用简单的```[LAContext alloc] init]```来创建; 63 | 64 | ``` 65 | canEvaluatePolicy:error:方法用来检查当前设备是否可用touchID,返回一个BOOL值 66 | evaluatePolicy:localizedReason:reply:调用验证方法,注意这里的三个参数: 67 | 第一个参数policy是要使用上面那个LAPolicy的枚举 68 | 第二个参数localizedReason是NSString类型的验证理由 69 | 第三个参数reply则是一个回调Block,block内有一个BOOL类型的success判断是否成功验证,还有一个用于判断错误信息的NSError类型的error 70 | invalidate方法用来废止这个context 71 | ``` 72 | 参数localizedReason的具体讲解:例如使用的TouchID```"XXX"的TouchID 请验证已有指纹```,其中的XXX是你app的name,这黑体字部分无法更改,后面的小字部分```请验证已有指纹```可以通过参数localizedReason自定义; 73 | 74 | ```LAContext```还有一个```localizedFallbackTitle```,是用来自定义弹出的alert底部右侧的feedback按钮的title,默认是输入密码,如果不想显示 feedback 按钮;可以设置 ```feedBackTitle = @""```,```localizedCancelTitle```则是自定义取消按钮的title,不过```localizedCancelTitle```系统10.0才能使用; 75 | 76 | ```objective-c 77 | LAContext *context = [[LAContext alloc] init]; 78 | context.localizedFallbackTitle = @"验证登录密码"; 79 | // LAPolicyDeviceOwnerAuthentication 80 | __weak __typeof(self)weakSelf = self; 81 | [context evaluatePolicy:policy localizedReason:@"通过Home键验证已有手机指纹" reply:^(BOOL success, NSError * _Nullable error) { 82 | 83 | dispatch_async(dispatch_get_main_queue(), ^{ 84 | NSString *message = @""; 85 | if (success) { 86 | message = @"通过了Touch ID 指纹验证"; 87 | block(YES, NO, message); 88 | } else { 89 | //失败操作 90 | LAError errorCode = error.code; 91 | BOOL inputPassword = NO; 92 | switch (errorCode) { 93 | case LAErrorAuthenticationFailed: { 94 | // -1 95 | [SVProgressHUD showErrorWithStatus:@"指纹不匹配"]; 96 | message = @"连续三次指纹识别错误"; 97 | } 98 | break; 99 | 100 | case LAErrorUserCancel: { 101 | // -2 102 | message = @"用户取消验证Touch ID"; 103 | } 104 | break; 105 | 106 | case LAErrorUserFallback: { 107 | // -3 108 | inputPassword = YES; 109 | message = @"用户选择输入密码"; 110 | } 111 | break; 112 | 113 | case LAErrorSystemCancel: { 114 | // -4 TouchID对话框被系统取消,例如按下Home或者电源键 115 | message = @"取消授权,如其他应用切入"; 116 | } 117 | break; 118 | 119 | case LAErrorPasscodeNotSet: { 120 | // -5 121 | [SVProgressHUD showErrorWithStatus:@"此设备未设置系统密码"]; 122 | message = @"设备系统未设置密码"; 123 | } 124 | break; 125 | 126 | case LAErrorTouchIDNotAvailable: { 127 | // -6 128 | [SVProgressHUD showErrorWithStatus:@"此设备不支持 Touch ID"]; 129 | message = @"此设备不支持 Touch ID"; 130 | } 131 | break; 132 | 133 | case LAErrorTouchIDNotEnrolled: { 134 | // -7 135 | [SVProgressHUD showErrorWithStatus:@"用户未录入指纹"]; 136 | message = @"用户未录入指纹"; 137 | } 138 | break; 139 | 140 | case LAErrorTouchIDLockout: { 141 | // -8 连续五次指纹识别错误,TouchID功能被锁定,下一次需要输入系统密码 142 | if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) { 143 | [weakSelf openTouchIDWithPolicy:LAPolicyDeviceOwnerAuthentication touchIDBlock:block]; 144 | } 145 | message = @"Touch ID被锁,需要用户输入密码解锁"; 146 | } 147 | break; 148 | 149 | case LAErrorAppCancel: { 150 | // -9 如突然来了电话,电话应用进入前台,APP被挂起啦 151 | message = @"用户不能控制情况下APP被挂起"; 152 | } 153 | break; 154 | 155 | case LAErrorInvalidContext: { 156 | // -10 157 | message = @"Touch ID 失效"; 158 | } 159 | break; 160 | 161 | default: 162 | // [SVProgressHUD showErrorWithStatus:@"此设备不支持 Touch ID"]; 163 | break; 164 | } 165 | 166 | } 167 | }); 168 | }]; 169 | ``` 170 | 171 | ## 指纹识别的版本问题 172 | 173 | 1.iOS 9 之前是没有```LAErrorTouchIDLockout```锁定这个选项的,默认错误5次后;第6次验证是自动弹出输入密码界面; 174 | 175 | 2.iOS 9 之后锁定指纹识别之后,如果需要立即弹出输入密码界面需要使用```LAPolicyDeviceOwnerAuthentication```这个属性重新发起验证; 176 | 177 | ## 弹窗显示级别问题 178 | 179 | TouchID的弹窗的级别非常之高,高到离谱,经过验证应用程序内部没有比指纹识别的window的级别更高的UIWindowLevel,也就说了他是系统级的弹窗。需要注意的是,如果指纹弹窗显示和消失应用程序会调用: 180 | ``` 181 | - (void)applicationWillResignActive:(UIApplication *)application; 182 | - (void)applicationDidBecomeActive:(UIApplication *)application; 183 | ``` 184 | 只要你的app进入后台或者打开使用都可以弹出TouchID页面,具体显示逻辑视情况判断; 185 | 186 | 目前以上代码足可以满足大多数app中TouchID的使用,不足之处敬请指出; 187 | 188 | 参考文章: 189 | 1.http://www.jianshu.com/p/d44b7d85e0a6 190 | 191 | 2.http://zcill.com/2016/02/29/LocalAuthentication%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0/ 192 | -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 370750B81E0B7753009088B2 /* CALayer+shake.m in Sources */ = {isa = PBXBuildFile; fileRef = 370750AE1E0B7753009088B2 /* CALayer+shake.m */; }; 11 | 370750B91E0B7753009088B2 /* UIView+Ext.m in Sources */ = {isa = PBXBuildFile; fileRef = 370750B01E0B7753009088B2 /* UIView+Ext.m */; }; 12 | 370750C31E0B786E009088B2 /* SCLoginVerifyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 370750C21E0B786E009088B2 /* SCLoginVerifyView.m */; }; 13 | 370750C51E0B83E6009088B2 /* head_Img.JPG in Resources */ = {isa = PBXBuildFile; fileRef = 370750C41E0B83E6009088B2 /* head_Img.JPG */; }; 14 | 370750C71E0BABBF009088B2 /* fingerprint.png in Resources */ = {isa = PBXBuildFile; fileRef = 370750C61E0BABBF009088B2 /* fingerprint.png */; }; 15 | 3728A12D1E0770DE002A803F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3728A1241E0770DE002A803F /* Assets.xcassets */; }; 16 | 3728A12E1E0770DE002A803F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3728A1251E0770DE002A803F /* LaunchScreen.storyboard */; }; 17 | 3728A12F1E0770DE002A803F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3728A1271E0770DE002A803F /* Main.storyboard */; }; 18 | 3728A1301E0770DE002A803F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3728A12B1E0770DE002A803F /* Info.plist */; }; 19 | 3728A1311E0770DE002A803F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3728A12C1E0770DE002A803F /* main.m */; }; 20 | 3728A13D1E077663002A803F /* SCTouchIDController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3728A13B1E077663002A803F /* SCTouchIDController.m */; }; 21 | 3728A13E1E077663002A803F /* SCTouchIDController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3728A13C1E077663002A803F /* SCTouchIDController.xib */; }; 22 | 3728A1421E07768A002A803F /* SCGestureController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3728A1401E07768A002A803F /* SCGestureController.m */; }; 23 | 3728A1431E07768A002A803F /* SCGestureController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3728A1411E07768A002A803F /* SCGestureController.xib */; }; 24 | 3728A1471E077CC5002A803F /* SCSwitchCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3728A1451E077CC5002A803F /* SCSwitchCell.m */; }; 25 | 3728A1481E077CC5002A803F /* SCSwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3728A1461E077CC5002A803F /* SCSwitchCell.xib */; }; 26 | 3728A14E1E078746002A803F /* SCMainCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3728A14C1E078746002A803F /* SCMainCell.m */; }; 27 | 3728A14F1E078746002A803F /* SCMainCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3728A14D1E078746002A803F /* SCMainCell.xib */; }; 28 | 3774040F1E0280A5000AEACE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3774040E1E0280A5000AEACE /* AppDelegate.m */; }; 29 | 377404121E0280A5000AEACE /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 377404111E0280A5000AEACE /* ViewController.m */; }; 30 | 377404251E0280A5000AEACE /* SCTouchDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 377404241E0280A5000AEACE /* SCTouchDemoTests.m */; }; 31 | 377404301E0280A5000AEACE /* SCTouchDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3774042F1E0280A5000AEACE /* SCTouchDemoUITests.m */; }; 32 | 3774043F1E028101000AEACE /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3774043E1E028101000AEACE /* LocalAuthentication.framework */; }; 33 | 377760A11E0A535100D2FA5D /* SCSecureHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 377760A01E0A535100D2FA5D /* SCSecureHelper.m */; }; 34 | 37B69D6A1E09129D00BA19E3 /* SCNormalCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B69D681E09129D00BA19E3 /* SCNormalCell.m */; }; 35 | 37B69D6B1E09129D00BA19E3 /* SCNormalCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 37B69D691E09129D00BA19E3 /* SCNormalCell.xib */; }; 36 | 37B69D771E091DBE00BA19E3 /* MYZCircleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B69D711E091DBE00BA19E3 /* MYZCircleView.m */; }; 37 | 37B69D781E091DBE00BA19E3 /* MYZGestureShapeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B69D731E091DBE00BA19E3 /* MYZGestureShapeView.m */; }; 38 | 37B69D791E091DBE00BA19E3 /* MYZGestureView.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B69D751E091DBE00BA19E3 /* MYZGestureView.m */; }; 39 | 37B69D7D1E091FFA00BA19E3 /* SCGestureSetController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B69D7C1E091FFA00BA19E3 /* SCGestureSetController.m */; }; 40 | 45FB8C1D6E355C17E6206EAC /* libPods-SCTouchDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DBDDC0033CF69B8871E21E59 /* libPods-SCTouchDemo.a */; }; 41 | B984DE7C1F72455D00ABC963 /* GCDDelay.m in Sources */ = {isa = PBXBuildFile; fileRef = B984DE7B1F72455D00ABC963 /* GCDDelay.m */; }; 42 | /* End PBXBuildFile section */ 43 | 44 | /* Begin PBXContainerItemProxy section */ 45 | 377404211E0280A5000AEACE /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 377403FF1E0280A5000AEACE /* Project object */; 48 | proxyType = 1; 49 | remoteGlobalIDString = 377404061E0280A5000AEACE; 50 | remoteInfo = SCTouchDemo; 51 | }; 52 | 3774042C1E0280A5000AEACE /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 377403FF1E0280A5000AEACE /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 377404061E0280A5000AEACE; 57 | remoteInfo = SCTouchDemo; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 370750AD1E0B7753009088B2 /* CALayer+shake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CALayer+shake.h"; sourceTree = ""; }; 63 | 370750AE1E0B7753009088B2 /* CALayer+shake.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CALayer+shake.m"; sourceTree = ""; }; 64 | 370750AF1E0B7753009088B2 /* UIView+Ext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Ext.h"; sourceTree = ""; }; 65 | 370750B01E0B7753009088B2 /* UIView+Ext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Ext.m"; sourceTree = ""; }; 66 | 370750C11E0B786E009088B2 /* SCLoginVerifyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCLoginVerifyView.h; sourceTree = ""; }; 67 | 370750C21E0B786E009088B2 /* SCLoginVerifyView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCLoginVerifyView.m; sourceTree = ""; }; 68 | 370750C41E0B83E6009088B2 /* head_Img.JPG */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = head_Img.JPG; sourceTree = ""; }; 69 | 370750C61E0BABBF009088B2 /* fingerprint.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fingerprint.png; sourceTree = ""; }; 70 | 3728A1241E0770DE002A803F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 71 | 3728A1261E0770DE002A803F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 72 | 3728A1281E0770DE002A803F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 73 | 3728A12B1E0770DE002A803F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 74 | 3728A12C1E0770DE002A803F /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 75 | 3728A13A1E077663002A803F /* SCTouchIDController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCTouchIDController.h; sourceTree = ""; }; 76 | 3728A13B1E077663002A803F /* SCTouchIDController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTouchIDController.m; sourceTree = ""; }; 77 | 3728A13C1E077663002A803F /* SCTouchIDController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCTouchIDController.xib; sourceTree = ""; }; 78 | 3728A13F1E07768A002A803F /* SCGestureController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCGestureController.h; sourceTree = ""; }; 79 | 3728A1401E07768A002A803F /* SCGestureController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCGestureController.m; sourceTree = ""; }; 80 | 3728A1411E07768A002A803F /* SCGestureController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCGestureController.xib; sourceTree = ""; }; 81 | 3728A1441E077CC5002A803F /* SCSwitchCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCSwitchCell.h; sourceTree = ""; }; 82 | 3728A1451E077CC5002A803F /* SCSwitchCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCSwitchCell.m; sourceTree = ""; }; 83 | 3728A1461E077CC5002A803F /* SCSwitchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCSwitchCell.xib; sourceTree = ""; }; 84 | 3728A1491E077E8E002A803F /* SCHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCHeader.h; sourceTree = ""; }; 85 | 3728A14A1E0780D7002A803F /* SCTouchDemo.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCTouchDemo.pch; sourceTree = ""; }; 86 | 3728A14B1E078746002A803F /* SCMainCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMainCell.h; sourceTree = ""; }; 87 | 3728A14C1E078746002A803F /* SCMainCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMainCell.m; sourceTree = ""; }; 88 | 3728A14D1E078746002A803F /* SCMainCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCMainCell.xib; sourceTree = ""; }; 89 | 377404071E0280A5000AEACE /* SCTouchDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCTouchDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 3774040D1E0280A5000AEACE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 91 | 3774040E1E0280A5000AEACE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 92 | 377404101E0280A5000AEACE /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 93 | 377404111E0280A5000AEACE /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 94 | 377404201E0280A5000AEACE /* SCTouchDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SCTouchDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 95 | 377404241E0280A5000AEACE /* SCTouchDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SCTouchDemoTests.m; sourceTree = ""; }; 96 | 377404261E0280A5000AEACE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 97 | 3774042B1E0280A5000AEACE /* SCTouchDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SCTouchDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | 3774042F1E0280A5000AEACE /* SCTouchDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SCTouchDemoUITests.m; sourceTree = ""; }; 99 | 377404311E0280A5000AEACE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 100 | 3774043E1E028101000AEACE /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; }; 101 | 3777609F1E0A535100D2FA5D /* SCSecureHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCSecureHelper.h; sourceTree = ""; }; 102 | 377760A01E0A535100D2FA5D /* SCSecureHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCSecureHelper.m; sourceTree = ""; }; 103 | 37B69D671E09129D00BA19E3 /* SCNormalCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCNormalCell.h; sourceTree = ""; }; 104 | 37B69D681E09129D00BA19E3 /* SCNormalCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCNormalCell.m; sourceTree = ""; }; 105 | 37B69D691E09129D00BA19E3 /* SCNormalCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCNormalCell.xib; sourceTree = ""; }; 106 | 37B69D701E091DBE00BA19E3 /* MYZCircleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYZCircleView.h; sourceTree = ""; }; 107 | 37B69D711E091DBE00BA19E3 /* MYZCircleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYZCircleView.m; sourceTree = ""; }; 108 | 37B69D721E091DBE00BA19E3 /* MYZGestureShapeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYZGestureShapeView.h; sourceTree = ""; }; 109 | 37B69D731E091DBE00BA19E3 /* MYZGestureShapeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYZGestureShapeView.m; sourceTree = ""; }; 110 | 37B69D741E091DBE00BA19E3 /* MYZGestureView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYZGestureView.h; sourceTree = ""; }; 111 | 37B69D751E091DBE00BA19E3 /* MYZGestureView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYZGestureView.m; sourceTree = ""; }; 112 | 37B69D7B1E091FFA00BA19E3 /* SCGestureSetController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCGestureSetController.h; sourceTree = ""; }; 113 | 37B69D7C1E091FFA00BA19E3 /* SCGestureSetController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCGestureSetController.m; sourceTree = ""; }; 114 | 814B532A1C66DBFAA788F671 /* Pods-SCTouchDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SCTouchDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SCTouchDemo/Pods-SCTouchDemo.debug.xcconfig"; sourceTree = ""; }; 115 | B984DE7A1F72455D00ABC963 /* GCDDelay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCDDelay.h; sourceTree = ""; }; 116 | B984DE7B1F72455D00ABC963 /* GCDDelay.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GCDDelay.m; sourceTree = ""; }; 117 | C6279BDEC8F7AD3E12718C4D /* Pods-SCTouchDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SCTouchDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-SCTouchDemo/Pods-SCTouchDemo.release.xcconfig"; sourceTree = ""; }; 118 | DBDDC0033CF69B8871E21E59 /* libPods-SCTouchDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SCTouchDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 119 | /* End PBXFileReference section */ 120 | 121 | /* Begin PBXFrameworksBuildPhase section */ 122 | 377404041E0280A5000AEACE /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 3774043F1E028101000AEACE /* LocalAuthentication.framework in Frameworks */, 127 | 45FB8C1D6E355C17E6206EAC /* libPods-SCTouchDemo.a in Frameworks */, 128 | ); 129 | runOnlyForDeploymentPostprocessing = 0; 130 | }; 131 | 3774041D1E0280A5000AEACE /* Frameworks */ = { 132 | isa = PBXFrameworksBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | runOnlyForDeploymentPostprocessing = 0; 137 | }; 138 | 377404281E0280A5000AEACE /* Frameworks */ = { 139 | isa = PBXFrameworksBuildPhase; 140 | buildActionMask = 2147483647; 141 | files = ( 142 | ); 143 | runOnlyForDeploymentPostprocessing = 0; 144 | }; 145 | /* End PBXFrameworksBuildPhase section */ 146 | 147 | /* Begin PBXGroup section */ 148 | 370750AC1E0B7753009088B2 /* Category */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | B984DE7A1F72455D00ABC963 /* GCDDelay.h */, 152 | B984DE7B1F72455D00ABC963 /* GCDDelay.m */, 153 | 370750AD1E0B7753009088B2 /* CALayer+shake.h */, 154 | 370750AE1E0B7753009088B2 /* CALayer+shake.m */, 155 | 370750AF1E0B7753009088B2 /* UIView+Ext.h */, 156 | 370750B01E0B7753009088B2 /* UIView+Ext.m */, 157 | ); 158 | path = Category; 159 | sourceTree = ""; 160 | }; 161 | 370750BD1E0B7768009088B2 /* GestureTouchID */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 370750C61E0BABBF009088B2 /* fingerprint.png */, 165 | 370750C41E0B83E6009088B2 /* head_Img.JPG */, 166 | 370750C11E0B786E009088B2 /* SCLoginVerifyView.h */, 167 | 370750C21E0B786E009088B2 /* SCLoginVerifyView.m */, 168 | ); 169 | path = GestureTouchID; 170 | sourceTree = ""; 171 | }; 172 | 3728A1231E0770DE002A803F /* Resources */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 3728A1241E0770DE002A803F /* Assets.xcassets */, 176 | 3728A1251E0770DE002A803F /* LaunchScreen.storyboard */, 177 | 3728A1271E0770DE002A803F /* Main.storyboard */, 178 | 3728A1291E0770DE002A803F /* GestureLock */, 179 | 3728A12B1E0770DE002A803F /* Info.plist */, 180 | 3728A12C1E0770DE002A803F /* main.m */, 181 | 3728A1491E077E8E002A803F /* SCHeader.h */, 182 | 3728A14A1E0780D7002A803F /* SCTouchDemo.pch */, 183 | ); 184 | path = Resources; 185 | sourceTree = ""; 186 | }; 187 | 3728A1291E0770DE002A803F /* GestureLock */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 3728A12A1E0770DE002A803F /* GesturePass */, 191 | ); 192 | path = GestureLock; 193 | sourceTree = ""; 194 | }; 195 | 3728A12A1E0770DE002A803F /* GesturePass */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | ); 199 | path = GesturePass; 200 | sourceTree = ""; 201 | }; 202 | 3728A1321E07713C002A803F /* Helper */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 3777609F1E0A535100D2FA5D /* SCSecureHelper.h */, 206 | 377760A01E0A535100D2FA5D /* SCSecureHelper.m */, 207 | ); 208 | path = Helper; 209 | sourceTree = ""; 210 | }; 211 | 3728A1331E07713D002A803F /* ViewControllers */ = { 212 | isa = PBXGroup; 213 | children = ( 214 | 3728A13A1E077663002A803F /* SCTouchIDController.h */, 215 | 3728A13B1E077663002A803F /* SCTouchIDController.m */, 216 | 3728A13C1E077663002A803F /* SCTouchIDController.xib */, 217 | 3728A13F1E07768A002A803F /* SCGestureController.h */, 218 | 3728A1401E07768A002A803F /* SCGestureController.m */, 219 | 3728A1411E07768A002A803F /* SCGestureController.xib */, 220 | 37B69D7B1E091FFA00BA19E3 /* SCGestureSetController.h */, 221 | 37B69D7C1E091FFA00BA19E3 /* SCGestureSetController.m */, 222 | ); 223 | path = ViewControllers; 224 | sourceTree = ""; 225 | }; 226 | 3728A1341E07713D002A803F /* Views */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | 3728A1441E077CC5002A803F /* SCSwitchCell.h */, 230 | 3728A1451E077CC5002A803F /* SCSwitchCell.m */, 231 | 3728A1461E077CC5002A803F /* SCSwitchCell.xib */, 232 | 3728A14B1E078746002A803F /* SCMainCell.h */, 233 | 3728A14C1E078746002A803F /* SCMainCell.m */, 234 | 3728A14D1E078746002A803F /* SCMainCell.xib */, 235 | 37B69D671E09129D00BA19E3 /* SCNormalCell.h */, 236 | 37B69D681E09129D00BA19E3 /* SCNormalCell.m */, 237 | 37B69D691E09129D00BA19E3 /* SCNormalCell.xib */, 238 | ); 239 | path = Views; 240 | sourceTree = ""; 241 | }; 242 | 377403FE1E0280A5000AEACE = { 243 | isa = PBXGroup; 244 | children = ( 245 | 377404091E0280A5000AEACE /* SCTouchDemo */, 246 | 377404231E0280A5000AEACE /* SCTouchDemoTests */, 247 | 3774042E1E0280A5000AEACE /* SCTouchDemoUITests */, 248 | 377404081E0280A5000AEACE /* Products */, 249 | 3774043D1E028101000AEACE /* Frameworks */, 250 | FC8FD60EB3B473DCF061BDFE /* Pods */, 251 | ); 252 | sourceTree = ""; 253 | }; 254 | 377404081E0280A5000AEACE /* Products */ = { 255 | isa = PBXGroup; 256 | children = ( 257 | 377404071E0280A5000AEACE /* SCTouchDemo.app */, 258 | 377404201E0280A5000AEACE /* SCTouchDemoTests.xctest */, 259 | 3774042B1E0280A5000AEACE /* SCTouchDemoUITests.xctest */, 260 | ); 261 | name = Products; 262 | sourceTree = ""; 263 | }; 264 | 377404091E0280A5000AEACE /* SCTouchDemo */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | 3774040D1E0280A5000AEACE /* AppDelegate.h */, 268 | 3774040E1E0280A5000AEACE /* AppDelegate.m */, 269 | 377404101E0280A5000AEACE /* ViewController.h */, 270 | 377404111E0280A5000AEACE /* ViewController.m */, 271 | 370750BD1E0B7768009088B2 /* GestureTouchID */, 272 | 370750AC1E0B7753009088B2 /* Category */, 273 | 37B69D6D1E091DBE00BA19E3 /* GestureView */, 274 | 3728A1321E07713C002A803F /* Helper */, 275 | 3728A1331E07713D002A803F /* ViewControllers */, 276 | 3728A1341E07713D002A803F /* Views */, 277 | 3728A1231E0770DE002A803F /* Resources */, 278 | ); 279 | path = SCTouchDemo; 280 | sourceTree = ""; 281 | }; 282 | 377404231E0280A5000AEACE /* SCTouchDemoTests */ = { 283 | isa = PBXGroup; 284 | children = ( 285 | 377404241E0280A5000AEACE /* SCTouchDemoTests.m */, 286 | 377404261E0280A5000AEACE /* Info.plist */, 287 | ); 288 | path = SCTouchDemoTests; 289 | sourceTree = ""; 290 | }; 291 | 3774042E1E0280A5000AEACE /* SCTouchDemoUITests */ = { 292 | isa = PBXGroup; 293 | children = ( 294 | 3774042F1E0280A5000AEACE /* SCTouchDemoUITests.m */, 295 | 377404311E0280A5000AEACE /* Info.plist */, 296 | ); 297 | path = SCTouchDemoUITests; 298 | sourceTree = ""; 299 | }; 300 | 3774043D1E028101000AEACE /* Frameworks */ = { 301 | isa = PBXGroup; 302 | children = ( 303 | 3774043E1E028101000AEACE /* LocalAuthentication.framework */, 304 | DBDDC0033CF69B8871E21E59 /* libPods-SCTouchDemo.a */, 305 | ); 306 | name = Frameworks; 307 | sourceTree = ""; 308 | }; 309 | 37B69D6D1E091DBE00BA19E3 /* GestureView */ = { 310 | isa = PBXGroup; 311 | children = ( 312 | 37B69D701E091DBE00BA19E3 /* MYZCircleView.h */, 313 | 37B69D711E091DBE00BA19E3 /* MYZCircleView.m */, 314 | 37B69D721E091DBE00BA19E3 /* MYZGestureShapeView.h */, 315 | 37B69D731E091DBE00BA19E3 /* MYZGestureShapeView.m */, 316 | 37B69D741E091DBE00BA19E3 /* MYZGestureView.h */, 317 | 37B69D751E091DBE00BA19E3 /* MYZGestureView.m */, 318 | ); 319 | path = GestureView; 320 | sourceTree = ""; 321 | }; 322 | FC8FD60EB3B473DCF061BDFE /* Pods */ = { 323 | isa = PBXGroup; 324 | children = ( 325 | 814B532A1C66DBFAA788F671 /* Pods-SCTouchDemo.debug.xcconfig */, 326 | C6279BDEC8F7AD3E12718C4D /* Pods-SCTouchDemo.release.xcconfig */, 327 | ); 328 | name = Pods; 329 | sourceTree = ""; 330 | }; 331 | /* End PBXGroup section */ 332 | 333 | /* Begin PBXNativeTarget section */ 334 | 377404061E0280A5000AEACE /* SCTouchDemo */ = { 335 | isa = PBXNativeTarget; 336 | buildConfigurationList = 377404341E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemo" */; 337 | buildPhases = ( 338 | B914C3658413663425F20A33 /* [CP] Check Pods Manifest.lock */, 339 | 377404031E0280A5000AEACE /* Sources */, 340 | 377404041E0280A5000AEACE /* Frameworks */, 341 | 377404051E0280A5000AEACE /* Resources */, 342 | 82BDFD1F664A572A6E743939 /* [CP] Embed Pods Frameworks */, 343 | 23D30D1D5FAC2D4E1560ED1B /* [CP] Copy Pods Resources */, 344 | ); 345 | buildRules = ( 346 | ); 347 | dependencies = ( 348 | ); 349 | name = SCTouchDemo; 350 | productName = SCTouchDemo; 351 | productReference = 377404071E0280A5000AEACE /* SCTouchDemo.app */; 352 | productType = "com.apple.product-type.application"; 353 | }; 354 | 3774041F1E0280A5000AEACE /* SCTouchDemoTests */ = { 355 | isa = PBXNativeTarget; 356 | buildConfigurationList = 377404371E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemoTests" */; 357 | buildPhases = ( 358 | 3774041C1E0280A5000AEACE /* Sources */, 359 | 3774041D1E0280A5000AEACE /* Frameworks */, 360 | 3774041E1E0280A5000AEACE /* Resources */, 361 | ); 362 | buildRules = ( 363 | ); 364 | dependencies = ( 365 | 377404221E0280A5000AEACE /* PBXTargetDependency */, 366 | ); 367 | name = SCTouchDemoTests; 368 | productName = SCTouchDemoTests; 369 | productReference = 377404201E0280A5000AEACE /* SCTouchDemoTests.xctest */; 370 | productType = "com.apple.product-type.bundle.unit-test"; 371 | }; 372 | 3774042A1E0280A5000AEACE /* SCTouchDemoUITests */ = { 373 | isa = PBXNativeTarget; 374 | buildConfigurationList = 3774043A1E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemoUITests" */; 375 | buildPhases = ( 376 | 377404271E0280A5000AEACE /* Sources */, 377 | 377404281E0280A5000AEACE /* Frameworks */, 378 | 377404291E0280A5000AEACE /* Resources */, 379 | ); 380 | buildRules = ( 381 | ); 382 | dependencies = ( 383 | 3774042D1E0280A5000AEACE /* PBXTargetDependency */, 384 | ); 385 | name = SCTouchDemoUITests; 386 | productName = SCTouchDemoUITests; 387 | productReference = 3774042B1E0280A5000AEACE /* SCTouchDemoUITests.xctest */; 388 | productType = "com.apple.product-type.bundle.ui-testing"; 389 | }; 390 | /* End PBXNativeTarget section */ 391 | 392 | /* Begin PBXProject section */ 393 | 377403FF1E0280A5000AEACE /* Project object */ = { 394 | isa = PBXProject; 395 | attributes = { 396 | LastUpgradeCheck = 0820; 397 | ORGANIZATIONNAME = "小广"; 398 | TargetAttributes = { 399 | 377404061E0280A5000AEACE = { 400 | CreatedOnToolsVersion = 8.2; 401 | DevelopmentTeam = D38XPC8URL; 402 | ProvisioningStyle = Manual; 403 | }; 404 | 3774041F1E0280A5000AEACE = { 405 | CreatedOnToolsVersion = 8.2; 406 | ProvisioningStyle = Automatic; 407 | TestTargetID = 377404061E0280A5000AEACE; 408 | }; 409 | 3774042A1E0280A5000AEACE = { 410 | CreatedOnToolsVersion = 8.2; 411 | ProvisioningStyle = Automatic; 412 | TestTargetID = 377404061E0280A5000AEACE; 413 | }; 414 | }; 415 | }; 416 | buildConfigurationList = 377404021E0280A5000AEACE /* Build configuration list for PBXProject "SCTouchDemo" */; 417 | compatibilityVersion = "Xcode 3.2"; 418 | developmentRegion = English; 419 | hasScannedForEncodings = 0; 420 | knownRegions = ( 421 | en, 422 | Base, 423 | ); 424 | mainGroup = 377403FE1E0280A5000AEACE; 425 | productRefGroup = 377404081E0280A5000AEACE /* Products */; 426 | projectDirPath = ""; 427 | projectRoot = ""; 428 | targets = ( 429 | 377404061E0280A5000AEACE /* SCTouchDemo */, 430 | 3774041F1E0280A5000AEACE /* SCTouchDemoTests */, 431 | 3774042A1E0280A5000AEACE /* SCTouchDemoUITests */, 432 | ); 433 | }; 434 | /* End PBXProject section */ 435 | 436 | /* Begin PBXResourcesBuildPhase section */ 437 | 377404051E0280A5000AEACE /* Resources */ = { 438 | isa = PBXResourcesBuildPhase; 439 | buildActionMask = 2147483647; 440 | files = ( 441 | 37B69D6B1E09129D00BA19E3 /* SCNormalCell.xib in Resources */, 442 | 3728A1301E0770DE002A803F /* Info.plist in Resources */, 443 | 3728A1481E077CC5002A803F /* SCSwitchCell.xib in Resources */, 444 | 3728A14F1E078746002A803F /* SCMainCell.xib in Resources */, 445 | 3728A12F1E0770DE002A803F /* Main.storyboard in Resources */, 446 | 3728A13E1E077663002A803F /* SCTouchIDController.xib in Resources */, 447 | 3728A12D1E0770DE002A803F /* Assets.xcassets in Resources */, 448 | 3728A12E1E0770DE002A803F /* LaunchScreen.storyboard in Resources */, 449 | 3728A1431E07768A002A803F /* SCGestureController.xib in Resources */, 450 | 370750C71E0BABBF009088B2 /* fingerprint.png in Resources */, 451 | 370750C51E0B83E6009088B2 /* head_Img.JPG in Resources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | 3774041E1E0280A5000AEACE /* Resources */ = { 456 | isa = PBXResourcesBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | ); 460 | runOnlyForDeploymentPostprocessing = 0; 461 | }; 462 | 377404291E0280A5000AEACE /* Resources */ = { 463 | isa = PBXResourcesBuildPhase; 464 | buildActionMask = 2147483647; 465 | files = ( 466 | ); 467 | runOnlyForDeploymentPostprocessing = 0; 468 | }; 469 | /* End PBXResourcesBuildPhase section */ 470 | 471 | /* Begin PBXShellScriptBuildPhase section */ 472 | 23D30D1D5FAC2D4E1560ED1B /* [CP] Copy Pods Resources */ = { 473 | isa = PBXShellScriptBuildPhase; 474 | buildActionMask = 2147483647; 475 | files = ( 476 | ); 477 | inputPaths = ( 478 | ); 479 | name = "[CP] Copy Pods Resources"; 480 | outputPaths = ( 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | shellPath = /bin/sh; 484 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SCTouchDemo/Pods-SCTouchDemo-resources.sh\"\n"; 485 | showEnvVarsInLog = 0; 486 | }; 487 | 82BDFD1F664A572A6E743939 /* [CP] Embed Pods Frameworks */ = { 488 | isa = PBXShellScriptBuildPhase; 489 | buildActionMask = 2147483647; 490 | files = ( 491 | ); 492 | inputPaths = ( 493 | ); 494 | name = "[CP] Embed Pods Frameworks"; 495 | outputPaths = ( 496 | ); 497 | runOnlyForDeploymentPostprocessing = 0; 498 | shellPath = /bin/sh; 499 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SCTouchDemo/Pods-SCTouchDemo-frameworks.sh\"\n"; 500 | showEnvVarsInLog = 0; 501 | }; 502 | B914C3658413663425F20A33 /* [CP] Check Pods Manifest.lock */ = { 503 | isa = PBXShellScriptBuildPhase; 504 | buildActionMask = 2147483647; 505 | files = ( 506 | ); 507 | inputPaths = ( 508 | ); 509 | name = "[CP] Check Pods Manifest.lock"; 510 | outputPaths = ( 511 | ); 512 | runOnlyForDeploymentPostprocessing = 0; 513 | shellPath = /bin/sh; 514 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 515 | showEnvVarsInLog = 0; 516 | }; 517 | /* End PBXShellScriptBuildPhase section */ 518 | 519 | /* Begin PBXSourcesBuildPhase section */ 520 | 377404031E0280A5000AEACE /* Sources */ = { 521 | isa = PBXSourcesBuildPhase; 522 | buildActionMask = 2147483647; 523 | files = ( 524 | 37B69D6A1E09129D00BA19E3 /* SCNormalCell.m in Sources */, 525 | 377404121E0280A5000AEACE /* ViewController.m in Sources */, 526 | 370750C31E0B786E009088B2 /* SCLoginVerifyView.m in Sources */, 527 | 3728A13D1E077663002A803F /* SCTouchIDController.m in Sources */, 528 | 37B69D781E091DBE00BA19E3 /* MYZGestureShapeView.m in Sources */, 529 | 370750B81E0B7753009088B2 /* CALayer+shake.m in Sources */, 530 | 37B69D791E091DBE00BA19E3 /* MYZGestureView.m in Sources */, 531 | 37B69D7D1E091FFA00BA19E3 /* SCGestureSetController.m in Sources */, 532 | B984DE7C1F72455D00ABC963 /* GCDDelay.m in Sources */, 533 | 3728A1311E0770DE002A803F /* main.m in Sources */, 534 | 3728A1471E077CC5002A803F /* SCSwitchCell.m in Sources */, 535 | 377760A11E0A535100D2FA5D /* SCSecureHelper.m in Sources */, 536 | 3728A14E1E078746002A803F /* SCMainCell.m in Sources */, 537 | 370750B91E0B7753009088B2 /* UIView+Ext.m in Sources */, 538 | 37B69D771E091DBE00BA19E3 /* MYZCircleView.m in Sources */, 539 | 3728A1421E07768A002A803F /* SCGestureController.m in Sources */, 540 | 3774040F1E0280A5000AEACE /* AppDelegate.m in Sources */, 541 | ); 542 | runOnlyForDeploymentPostprocessing = 0; 543 | }; 544 | 3774041C1E0280A5000AEACE /* Sources */ = { 545 | isa = PBXSourcesBuildPhase; 546 | buildActionMask = 2147483647; 547 | files = ( 548 | 377404251E0280A5000AEACE /* SCTouchDemoTests.m in Sources */, 549 | ); 550 | runOnlyForDeploymentPostprocessing = 0; 551 | }; 552 | 377404271E0280A5000AEACE /* Sources */ = { 553 | isa = PBXSourcesBuildPhase; 554 | buildActionMask = 2147483647; 555 | files = ( 556 | 377404301E0280A5000AEACE /* SCTouchDemoUITests.m in Sources */, 557 | ); 558 | runOnlyForDeploymentPostprocessing = 0; 559 | }; 560 | /* End PBXSourcesBuildPhase section */ 561 | 562 | /* Begin PBXTargetDependency section */ 563 | 377404221E0280A5000AEACE /* PBXTargetDependency */ = { 564 | isa = PBXTargetDependency; 565 | target = 377404061E0280A5000AEACE /* SCTouchDemo */; 566 | targetProxy = 377404211E0280A5000AEACE /* PBXContainerItemProxy */; 567 | }; 568 | 3774042D1E0280A5000AEACE /* PBXTargetDependency */ = { 569 | isa = PBXTargetDependency; 570 | target = 377404061E0280A5000AEACE /* SCTouchDemo */; 571 | targetProxy = 3774042C1E0280A5000AEACE /* PBXContainerItemProxy */; 572 | }; 573 | /* End PBXTargetDependency section */ 574 | 575 | /* Begin PBXVariantGroup section */ 576 | 3728A1251E0770DE002A803F /* LaunchScreen.storyboard */ = { 577 | isa = PBXVariantGroup; 578 | children = ( 579 | 3728A1261E0770DE002A803F /* Base */, 580 | ); 581 | name = LaunchScreen.storyboard; 582 | sourceTree = ""; 583 | }; 584 | 3728A1271E0770DE002A803F /* Main.storyboard */ = { 585 | isa = PBXVariantGroup; 586 | children = ( 587 | 3728A1281E0770DE002A803F /* Base */, 588 | ); 589 | name = Main.storyboard; 590 | sourceTree = ""; 591 | }; 592 | /* End PBXVariantGroup section */ 593 | 594 | /* Begin XCBuildConfiguration section */ 595 | 377404321E0280A5000AEACE /* Debug */ = { 596 | isa = XCBuildConfiguration; 597 | buildSettings = { 598 | ALWAYS_SEARCH_USER_PATHS = NO; 599 | CLANG_ANALYZER_NONNULL = YES; 600 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 601 | CLANG_CXX_LIBRARY = "libc++"; 602 | CLANG_ENABLE_MODULES = YES; 603 | CLANG_ENABLE_OBJC_ARC = YES; 604 | CLANG_WARN_BOOL_CONVERSION = YES; 605 | CLANG_WARN_CONSTANT_CONVERSION = YES; 606 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 607 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 608 | CLANG_WARN_EMPTY_BODY = YES; 609 | CLANG_WARN_ENUM_CONVERSION = YES; 610 | CLANG_WARN_INFINITE_RECURSION = YES; 611 | CLANG_WARN_INT_CONVERSION = YES; 612 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 613 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 614 | CLANG_WARN_UNREACHABLE_CODE = YES; 615 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 616 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 617 | COPY_PHASE_STRIP = NO; 618 | DEBUG_INFORMATION_FORMAT = dwarf; 619 | ENABLE_STRICT_OBJC_MSGSEND = YES; 620 | ENABLE_TESTABILITY = YES; 621 | GCC_C_LANGUAGE_STANDARD = gnu99; 622 | GCC_DYNAMIC_NO_PIC = NO; 623 | GCC_NO_COMMON_BLOCKS = YES; 624 | GCC_OPTIMIZATION_LEVEL = 0; 625 | GCC_PREPROCESSOR_DEFINITIONS = ( 626 | "DEBUG=1", 627 | "$(inherited)", 628 | ); 629 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 630 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 631 | GCC_WARN_UNDECLARED_SELECTOR = YES; 632 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 633 | GCC_WARN_UNUSED_FUNCTION = YES; 634 | GCC_WARN_UNUSED_VARIABLE = YES; 635 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 636 | MTL_ENABLE_DEBUG_INFO = YES; 637 | ONLY_ACTIVE_ARCH = YES; 638 | SDKROOT = iphoneos; 639 | }; 640 | name = Debug; 641 | }; 642 | 377404331E0280A5000AEACE /* Release */ = { 643 | isa = XCBuildConfiguration; 644 | buildSettings = { 645 | ALWAYS_SEARCH_USER_PATHS = NO; 646 | CLANG_ANALYZER_NONNULL = YES; 647 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 648 | CLANG_CXX_LIBRARY = "libc++"; 649 | CLANG_ENABLE_MODULES = YES; 650 | CLANG_ENABLE_OBJC_ARC = YES; 651 | CLANG_WARN_BOOL_CONVERSION = YES; 652 | CLANG_WARN_CONSTANT_CONVERSION = YES; 653 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 654 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 655 | CLANG_WARN_EMPTY_BODY = YES; 656 | CLANG_WARN_ENUM_CONVERSION = YES; 657 | CLANG_WARN_INFINITE_RECURSION = YES; 658 | CLANG_WARN_INT_CONVERSION = YES; 659 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 660 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 661 | CLANG_WARN_UNREACHABLE_CODE = YES; 662 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 663 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 664 | COPY_PHASE_STRIP = NO; 665 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 666 | ENABLE_NS_ASSERTIONS = NO; 667 | ENABLE_STRICT_OBJC_MSGSEND = YES; 668 | GCC_C_LANGUAGE_STANDARD = gnu99; 669 | GCC_NO_COMMON_BLOCKS = YES; 670 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 671 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 672 | GCC_WARN_UNDECLARED_SELECTOR = YES; 673 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 674 | GCC_WARN_UNUSED_FUNCTION = YES; 675 | GCC_WARN_UNUSED_VARIABLE = YES; 676 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 677 | MTL_ENABLE_DEBUG_INFO = NO; 678 | SDKROOT = iphoneos; 679 | VALIDATE_PRODUCT = YES; 680 | }; 681 | name = Release; 682 | }; 683 | 377404351E0280A5000AEACE /* Debug */ = { 684 | isa = XCBuildConfiguration; 685 | baseConfigurationReference = 814B532A1C66DBFAA788F671 /* Pods-SCTouchDemo.debug.xcconfig */; 686 | buildSettings = { 687 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 688 | DEVELOPMENT_TEAM = D38XPC8URL; 689 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 690 | GCC_PREFIX_HEADER = "$(SRCROOT)/SCTouchDemo/Resources/SCTouchDemo.pch"; 691 | INFOPLIST_FILE = "$(SRCROOT)/SCTouchDemo/Resources/Info.plist"; 692 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 693 | LIBRARY_SEARCH_PATHS = ( 694 | "$(inherited)", 695 | "$(PROJECT_DIR)/SCTouchDemo/callSdk", 696 | ); 697 | PRODUCT_BUNDLE_IDENTIFIER = com.uama.Butler; 698 | PRODUCT_NAME = "$(TARGET_NAME)"; 699 | PROVISIONING_PROFILE = "95700fa6-11f1-4267-a0ec-ae667214a350"; 700 | PROVISIONING_PROFILE_SPECIFIER = Butler; 701 | }; 702 | name = Debug; 703 | }; 704 | 377404361E0280A5000AEACE /* Release */ = { 705 | isa = XCBuildConfiguration; 706 | baseConfigurationReference = C6279BDEC8F7AD3E12718C4D /* Pods-SCTouchDemo.release.xcconfig */; 707 | buildSettings = { 708 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 709 | DEVELOPMENT_TEAM = ""; 710 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 711 | GCC_PREFIX_HEADER = "$(SRCROOT)/SCTouchDemo/Resources/SCTouchDemo.pch"; 712 | INFOPLIST_FILE = "$(SRCROOT)/SCTouchDemo/Resources/Info.plist"; 713 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 714 | LIBRARY_SEARCH_PATHS = ( 715 | "$(inherited)", 716 | "$(PROJECT_DIR)/SCTouchDemo/callSdk", 717 | ); 718 | PRODUCT_BUNDLE_IDENTIFIER = com.uama.Butler; 719 | PRODUCT_NAME = "$(TARGET_NAME)"; 720 | }; 721 | name = Release; 722 | }; 723 | 377404381E0280A5000AEACE /* Debug */ = { 724 | isa = XCBuildConfiguration; 725 | buildSettings = { 726 | BUNDLE_LOADER = "$(TEST_HOST)"; 727 | INFOPLIST_FILE = SCTouchDemoTests/Info.plist; 728 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 729 | PRODUCT_BUNDLE_IDENTIFIER = XG.SCTouchDemoTests; 730 | PRODUCT_NAME = "$(TARGET_NAME)"; 731 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SCTouchDemo.app/SCTouchDemo"; 732 | }; 733 | name = Debug; 734 | }; 735 | 377404391E0280A5000AEACE /* Release */ = { 736 | isa = XCBuildConfiguration; 737 | buildSettings = { 738 | BUNDLE_LOADER = "$(TEST_HOST)"; 739 | INFOPLIST_FILE = SCTouchDemoTests/Info.plist; 740 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 741 | PRODUCT_BUNDLE_IDENTIFIER = XG.SCTouchDemoTests; 742 | PRODUCT_NAME = "$(TARGET_NAME)"; 743 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SCTouchDemo.app/SCTouchDemo"; 744 | }; 745 | name = Release; 746 | }; 747 | 3774043B1E0280A5000AEACE /* Debug */ = { 748 | isa = XCBuildConfiguration; 749 | buildSettings = { 750 | INFOPLIST_FILE = SCTouchDemoUITests/Info.plist; 751 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 752 | PRODUCT_BUNDLE_IDENTIFIER = XG.SCTouchDemoUITests; 753 | PRODUCT_NAME = "$(TARGET_NAME)"; 754 | TEST_TARGET_NAME = SCTouchDemo; 755 | }; 756 | name = Debug; 757 | }; 758 | 3774043C1E0280A5000AEACE /* Release */ = { 759 | isa = XCBuildConfiguration; 760 | buildSettings = { 761 | INFOPLIST_FILE = SCTouchDemoUITests/Info.plist; 762 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 763 | PRODUCT_BUNDLE_IDENTIFIER = XG.SCTouchDemoUITests; 764 | PRODUCT_NAME = "$(TARGET_NAME)"; 765 | TEST_TARGET_NAME = SCTouchDemo; 766 | }; 767 | name = Release; 768 | }; 769 | /* End XCBuildConfiguration section */ 770 | 771 | /* Begin XCConfigurationList section */ 772 | 377404021E0280A5000AEACE /* Build configuration list for PBXProject "SCTouchDemo" */ = { 773 | isa = XCConfigurationList; 774 | buildConfigurations = ( 775 | 377404321E0280A5000AEACE /* Debug */, 776 | 377404331E0280A5000AEACE /* Release */, 777 | ); 778 | defaultConfigurationIsVisible = 0; 779 | defaultConfigurationName = Release; 780 | }; 781 | 377404341E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemo" */ = { 782 | isa = XCConfigurationList; 783 | buildConfigurations = ( 784 | 377404351E0280A5000AEACE /* Debug */, 785 | 377404361E0280A5000AEACE /* Release */, 786 | ); 787 | defaultConfigurationIsVisible = 0; 788 | defaultConfigurationName = Release; 789 | }; 790 | 377404371E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemoTests" */ = { 791 | isa = XCConfigurationList; 792 | buildConfigurations = ( 793 | 377404381E0280A5000AEACE /* Debug */, 794 | 377404391E0280A5000AEACE /* Release */, 795 | ); 796 | defaultConfigurationIsVisible = 0; 797 | defaultConfigurationName = Release; 798 | }; 799 | 3774043A1E0280A5000AEACE /* Build configuration list for PBXNativeTarget "SCTouchDemoUITests" */ = { 800 | isa = XCConfigurationList; 801 | buildConfigurations = ( 802 | 3774043B1E0280A5000AEACE /* Debug */, 803 | 3774043C1E0280A5000AEACE /* Release */, 804 | ); 805 | defaultConfigurationIsVisible = 0; 806 | defaultConfigurationName = Release; 807 | }; 808 | /* End XCConfigurationList section */ 809 | }; 810 | rootObject = 377403FF1E0280A5000AEACE /* Project object */; 811 | } 812 | -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/project.xcworkspace/xcuserdata/xiaoguang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo.xcodeproj/project.xcworkspace/xcuserdata/xiaoguang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/xcuserdata/xiaoguang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/xcuserdata/xiaoguang.xcuserdatad/xcschemes/SCTouchDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /SCTouchDemo.xcodeproj/xcuserdata/xiaoguang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SCTouchDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 377404061E0280A5000AEACE 16 | 17 | primary 18 | 19 | 20 | 3774041F1E0280A5000AEACE 21 | 22 | primary 23 | 24 | 25 | 3774042A1E0280A5000AEACE 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /SCTouchDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SCTouchDemo.xcworkspace/xcuserdata/xiaoguang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /SCTouchDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. 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 | -------------------------------------------------------------------------------- /SCTouchDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. 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 | 20 | [self startAppSetting]; 21 | return YES; 22 | } 23 | 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application { 26 | // 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. 27 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 28 | } 29 | 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // 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. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // 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. 44 | } 45 | 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | // 设置SVProgressHUD 52 | - (void)startAppSetting { 53 | [SVProgressHUD setDefaultStyle:SVProgressHUDStyleDark]; // 警示view样式 54 | //[SVProgressHUD setDefaultMaskType:SVProgressHUDMaskTypeClear]; // 设置此属性 一段时间自动消失 55 | [SVProgressHUD setMinimumDismissTimeInterval:1.2]; // 设置消失时间 56 | } 57 | 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/CALayer+shake.h: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+shake.h 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/8/10. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CALayer (shake) 12 | 13 | - (void)shake; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/CALayer+shake.m: -------------------------------------------------------------------------------- 1 | // 2 | // CALayer+shake.m 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/8/10. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 8 | 9 | #import "CALayer+shake.h" 10 | 11 | @implementation CALayer (shake) 12 | 13 | - (void)shake { 14 | CAKeyframeAnimation * keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"]; 15 | CGFloat x = 5; 16 | keyAnimation.values = @[@(-x),@(0),@(x),@(0),@(-x),@(0),@(x),@(0)]; 17 | keyAnimation.duration = 0.3; 18 | keyAnimation.repeatCount = 2; 19 | keyAnimation.removedOnCompletion = YES; 20 | [self addAnimation:keyAnimation forKey:nil]; 21 | } 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/GCDDelay.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDDelay.h 3 | // 口算达人 4 | // 5 | // Created by app on 2017/6/27. 6 | // Copyright © 2017年 William. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^GCDTask)(BOOL cancel); 12 | typedef void(^gcdBlock)(); 13 | 14 | @interface GCDDelay : NSObject 15 | 16 | + (GCDTask)gcdDelay:(NSTimeInterval)time task:(gcdBlock)block; 17 | 18 | + (void)gcdCancel:(GCDTask)task; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/GCDDelay.m: -------------------------------------------------------------------------------- 1 | // 2 | // GCDDelay.m 3 | // 口算达人 4 | // 5 | // Created by app on 2017/6/27. 6 | // Copyright © 2017年 William. All rights reserved. 7 | // 8 | 9 | #import "GCDDelay.h" 10 | 11 | @implementation GCDDelay 12 | 13 | /// 开启GCD延时 14 | + (void)gcdLater:(NSTimeInterval)time block:(gcdBlock)block { 15 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC)), dispatch_get_main_queue(), block); 16 | } 17 | 18 | /// 获取GCD延时任务 19 | + (GCDTask)gcdDelay:(NSTimeInterval)time task:(gcdBlock)block { 20 | 21 | __block dispatch_block_t closure = block; 22 | __block GCDTask result; 23 | GCDTask delayedClosure = ^(BOOL cancel){ 24 | if (closure) { 25 | if (!cancel) { 26 | dispatch_async(dispatch_get_main_queue(), closure); 27 | } 28 | } 29 | closure = nil; 30 | result = nil; 31 | }; 32 | result = delayedClosure; 33 | [self gcdLater:time block:^{ 34 | if (result) 35 | result(NO); 36 | }]; 37 | 38 | return result; 39 | } 40 | 41 | /// 取消GCD任务 42 | + (void)gcdCancel:(GCDTask)task { 43 | if (task) { 44 | task(YES); 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/UIView+Ext.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Ext.h 3 | // IDCardTest 4 | // 5 | // Created by 小广 on 16/9/1. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // UIView扩展 8 | 9 | #import 10 | 11 | @interface UIView (Ext) 12 | 13 | /** 14 | Returns the view's view controller (may be nil). 15 | */ 16 | @property (nonatomic, readonly) UIViewController *viewController; 17 | 18 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 19 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 20 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 21 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 22 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 23 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 24 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 25 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 26 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 27 | @property (nonatomic) CGSize size; ///< Shortcut for frame.size. 28 | @property (nonatomic, readonly) CGRect screenFrame; ///< View frame on the screen, taking into account scroll views. 29 | 30 | /** 31 | Create a snapshot image of the complete view hierarchy. 32 | This method should be called in main thread. 33 | */ 34 | - (UIImage *)snapshotImage; 35 | 36 | /** 37 | Create a snapshot PDF of the complete view hierarchy. 38 | This method should be called in main thread. 39 | */ 40 | - (NSData *)snapshotPDF; 41 | 42 | /** 43 | Shortcut to set the view.layer's shadow 44 | 45 | @param color Shadow Color 46 | @param offset Shadow offset 47 | @param radius Shadow radius 48 | */ 49 | - (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius; 50 | 51 | /** 52 | Remove all subviews. 53 | 54 | @warning Never call this method inside your view's drawRect: method. 55 | */ 56 | - (void)removeAllSubviews; 57 | 58 | 59 | /** 60 | * 给矩形view添加圆角 61 | * view 要改变的view 62 | * corners 要改变的圆角的位置(左上,左下,右上,右下,可用 | 多选) 63 | * cornerRadii 圆角的大小 64 | */ 65 | - (void)setRoundedRectWithView:(UIView *)view 66 | roundingCorners:(UIRectCorner)corners 67 | cornerRadii:(CGSize)cornerRadii; 68 | 69 | /** 70 | * 获取当前 View 的 Nib 71 | * 72 | * @return Nib 73 | */ 74 | + (UINib *)loadNib; 75 | 76 | /** 77 | * 根据 NibName 获取对应的 Nib 78 | * 79 | * @param nibName 80 | * 81 | * @return Nib 82 | */ 83 | + (UINib *)loadNibNamed:(NSString*)nibName; 84 | 85 | /** 86 | * 根据 NibName bundle 获取对应的 Nib 87 | * 88 | * @param nibName 89 | * @param bundle 90 | * 91 | * @return Nib 92 | */ 93 | + (UINib *)loadNibNamed:(NSString*)nibName bundle:(NSBundle *)bundle; 94 | 95 | 96 | /** 97 | * 根据当前类同名的 Nib 获取 当前类实例 98 | * 99 | * @return 当前类实例 100 | */ 101 | + (instancetype)loadInstanceFromNib; 102 | 103 | 104 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName; 105 | 106 | 107 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName owner:(id)owner; 108 | 109 | 110 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName 111 | owner:(id)owner 112 | bundle:(NSBundle *)bundle; 113 | 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /SCTouchDemo/Category/UIView+Ext.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Ext.m 3 | // IDCardTest 4 | // 5 | // Created by 小广 on 16/9/1. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // UIView扩展 8 | 9 | #import "UIView+Ext.h" 10 | 11 | @implementation UIView (Ext) 12 | 13 | #pragma mark - Frame 14 | 15 | - (CGFloat)left { 16 | return self.frame.origin.x; 17 | } 18 | 19 | - (void)setLeft:(CGFloat)x { 20 | CGRect frame = self.frame; 21 | frame.origin.x = x; 22 | self.frame = frame; 23 | } 24 | 25 | - (CGFloat)top { 26 | return self.frame.origin.y; 27 | } 28 | 29 | - (void)setTop:(CGFloat)y { 30 | CGRect frame = self.frame; 31 | frame.origin.y = y; 32 | self.frame = frame; 33 | } 34 | 35 | - (CGFloat)right { 36 | return self.frame.origin.x + self.frame.size.width; 37 | } 38 | 39 | - (void)setRight:(CGFloat)right { 40 | CGRect frame = self.frame; 41 | frame.origin.x = right - frame.size.width; 42 | self.frame = frame; 43 | } 44 | 45 | - (CGFloat)bottom { 46 | return self.frame.origin.y + self.frame.size.height; 47 | } 48 | 49 | - (void)setBottom:(CGFloat)bottom { 50 | CGRect frame = self.frame; 51 | frame.origin.y = bottom - frame.size.height; 52 | self.frame = frame; 53 | } 54 | 55 | - (CGFloat)width { 56 | return self.frame.size.width; 57 | } 58 | 59 | - (void)setWidth:(CGFloat)width { 60 | CGRect frame = self.frame; 61 | frame.size.width = width; 62 | self.frame = frame; 63 | } 64 | 65 | - (CGFloat)height { 66 | return self.frame.size.height; 67 | } 68 | 69 | - (void)setHeight:(CGFloat)height { 70 | CGRect frame = self.frame; 71 | frame.size.height = height; 72 | self.frame = frame; 73 | } 74 | 75 | - (CGFloat)centerX { 76 | return self.center.x; 77 | } 78 | 79 | - (void)setCenterX:(CGFloat)centerX { 80 | self.center = CGPointMake(centerX, self.center.y); 81 | } 82 | 83 | - (CGFloat)centerY { 84 | return self.center.y; 85 | } 86 | 87 | - (void)setCenterY:(CGFloat)centerY { 88 | self.center = CGPointMake(self.center.x, centerY); 89 | } 90 | 91 | - (CGPoint)origin { 92 | return self.frame.origin; 93 | } 94 | 95 | - (void)setOrigin:(CGPoint)origin { 96 | CGRect frame = self.frame; 97 | frame.origin = origin; 98 | self.frame = frame; 99 | } 100 | 101 | - (CGSize)size { 102 | return self.frame.size; 103 | } 104 | 105 | - (void)setSize:(CGSize)size { 106 | CGRect frame = self.frame; 107 | frame.size = size; 108 | self.frame = frame; 109 | } 110 | 111 | - (CGRect)screenFrame { 112 | CGPoint origin = CGPointZero; 113 | for (UIView *view = self; view; view = view.superview) { 114 | origin.x += view.left; 115 | origin.y += view.top; 116 | 117 | if ([view isKindOfClass:[UIScrollView class]]) { 118 | UIScrollView *scrollView = (UIScrollView *)view; 119 | origin.x -= scrollView.contentOffset.x; 120 | origin.y -= scrollView.contentOffset.y; 121 | } 122 | } 123 | return CGRectMake(origin.x, origin.y, self.width, self.height); 124 | } 125 | 126 | #pragma mark - Methods 127 | - (UIImage *)snapshotImage { 128 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0); 129 | if ([self respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) { 130 | // more (about 15x) faster than `renderInContext`. available from iOS7. 131 | [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; 132 | } else { 133 | [self.layer renderInContext:UIGraphicsGetCurrentContext()]; 134 | } 135 | UIImage *snap = UIGraphicsGetImageFromCurrentImageContext(); 136 | UIGraphicsEndImageContext(); 137 | return snap; 138 | } 139 | 140 | - (NSData *)snapshotPDF { 141 | CGRect bounds = self.bounds; 142 | NSMutableData* data = [NSMutableData data]; 143 | CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData((__bridge CFMutableDataRef)data); 144 | CGContextRef context = CGPDFContextCreate(consumer, &bounds, NULL); 145 | CGDataConsumerRelease(consumer); 146 | if (!context) return nil; 147 | CGPDFContextBeginPage(context, NULL); 148 | CGContextTranslateCTM(context, 0, bounds.size.height); 149 | CGContextScaleCTM(context, 1.0, -1.0); 150 | [self.layer renderInContext:context]; 151 | CGPDFContextEndPage(context); 152 | CGPDFContextClose(context); 153 | CGContextRelease(context); 154 | return data; 155 | } 156 | 157 | - (void)setLayerShadow:(UIColor*)color offset:(CGSize)offset radius:(CGFloat)radius { 158 | self.layer.shadowColor = color.CGColor; 159 | self.layer.shadowOffset = offset; 160 | self.layer.shadowRadius = radius; 161 | self.layer.shadowOpacity = 1; 162 | self.layer.shouldRasterize = YES; 163 | self.layer.rasterizationScale = [UIScreen mainScreen].scale; 164 | } 165 | 166 | - (void)removeAllSubviews { 167 | while (self.subviews.count) { 168 | [self.subviews.lastObject removeFromSuperview]; 169 | } 170 | } 171 | 172 | - (UIViewController *)viewController { 173 | for (UIView *view = self; view; view = view.superview) { 174 | UIResponder *nextResponder = [view nextResponder]; 175 | if ([nextResponder isKindOfClass:[UIViewController class]]) { 176 | return (UIViewController *)nextResponder; 177 | } 178 | } 179 | return nil; 180 | } 181 | 182 | /** 给矩形view添加圆角*/ 183 | - (void)setRoundedRectWithView:(UIView *)view 184 | roundingCorners:(UIRectCorner)corners 185 | cornerRadii:(CGSize)cornerRadii { 186 | 187 | UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds 188 | byRoundingCorners:corners 189 | cornerRadii:cornerRadii]; 190 | CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; 191 | maskLayer.frame = view.bounds; 192 | maskLayer.path = maskPath.CGPath; 193 | view.layer.mask = maskLayer; 194 | } 195 | 196 | 197 | #pragma mark - Nib 198 | 199 | + (UINib *)loadNib { 200 | return [self loadNibNamed:NSStringFromClass([self class])]; 201 | } 202 | 203 | 204 | + (UINib *)loadNibNamed:(NSString *)nibName { 205 | return [self loadNibNamed:nibName bundle:[NSBundle mainBundle]]; 206 | } 207 | 208 | 209 | + (UINib *)loadNibNamed:(NSString *)nibName bundle:(NSBundle *)bundle { 210 | return [UINib nibWithNibName:nibName bundle:bundle]; 211 | } 212 | 213 | 214 | + (instancetype)loadInstanceFromNib { 215 | return [self loadInstanceFromNibWithName:NSStringFromClass([self class])]; 216 | } 217 | 218 | 219 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName { 220 | return [self loadInstanceFromNibWithName:nibName owner:nil]; 221 | } 222 | 223 | 224 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName owner:(id)owner { 225 | return [self loadInstanceFromNibWithName:nibName owner:owner bundle:[NSBundle mainBundle]]; 226 | } 227 | 228 | 229 | + (instancetype)loadInstanceFromNibWithName:(NSString *)nibName 230 | owner:(id)owner 231 | bundle:(NSBundle *)bundle { 232 | UIView *result = nil; 233 | NSArray* elements = [bundle loadNibNamed:nibName owner:owner options:nil]; 234 | for (id object in elements) { 235 | if ([object isKindOfClass:[self class]]) { 236 | result = object; 237 | break; 238 | } 239 | } 240 | return result; 241 | } 242 | 243 | 244 | @end 245 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureTouchID/SCLoginVerifyView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCLoginVerifyView.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/22. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势/TouchID登录验证的view 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, SCLoginVerifyType) { 12 | SCLoginVerifyTypeTouchID, // 指纹密码验证登录 13 | SCLoginVerifyTypeGesture // 手势密码验证登录 14 | }; 15 | 16 | typedef void(^SCLoginVerifyBlock)(BOOL success); 17 | 18 | @interface SCLoginVerifyView : UIView 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame verifyType:(SCLoginVerifyType)verifyType; 21 | 22 | /// 显示view 23 | - (void)showView; 24 | 25 | /// 验证结果的回调 26 | - (void)loginVerifyResult:(SCLoginVerifyBlock)block; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureTouchID/SCLoginVerifyView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCLoginVerifyView.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/22. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势/TouchID登录验证的view 8 | 9 | #import "SCLoginVerifyView.h" 10 | #import "MYZGestureView.h" 11 | #import "MYZCircleView.h" 12 | #import "MYZGestureShapeView.h" 13 | #import "CALayer+shake.h" 14 | #import "SCSecureHelper.h" 15 | 16 | NSString * const passwordErrorMessage = @"手势密码错误"; 17 | 18 | #define MarginTop 64.0 19 | #define Margin 15.0 20 | #define BottomHeight 44.0 21 | #define HeadWH 80.0 22 | #define TouchIDHeight 100.0 23 | 24 | 25 | @interface SCLoginVerifyView () 26 | /// 手势九宫格 27 | @property (nonatomic, strong) MYZGestureView * gestureView; 28 | /// 手势错误的提示 29 | @property (nonatomic, strong) UILabel * messageLabel; 30 | /// 顶部的头像按钮 31 | @property (nonatomic, strong) UIImageView *headImageView; 32 | /// 底部的切换登录方式按钮 33 | @property (nonatomic, strong) UIButton * bottomButton; 34 | /// 指纹验证按钮 35 | @property (nonatomic, strong) UIButton * touchIDButton; 36 | @property (nonatomic, assign) SCLoginVerifyType verifyType; 37 | @property (nonatomic, assign) SCLoginVerifyBlock block; 38 | 39 | /// 防止按钮重复点击 40 | @property (nonatomic, assign) BOOL bottomBtnClick; 41 | @property (nonatomic, assign) BOOL touchBtnClick; 42 | 43 | 44 | 45 | @end 46 | 47 | @implementation SCLoginVerifyView 48 | 49 | #pragma mark - 初始化方法 50 | - (instancetype)initWithFrame:(CGRect)frame { 51 | self = [super initWithFrame:frame]; 52 | if (self) { 53 | // 默认指纹密码登录 54 | _verifyType = SCLoginVerifyTypeTouchID; 55 | [self layoutUI:_verifyType]; 56 | } 57 | return self; 58 | } 59 | 60 | - (instancetype)initWithFrame:(CGRect)frame verifyType:(SCLoginVerifyType)verifyType { 61 | self = [super initWithFrame:frame]; 62 | if (self) { 63 | _verifyType = verifyType; 64 | [self layoutUI:verifyType]; 65 | } 66 | return self; 67 | } 68 | 69 | #pragma mark - 对外方法 70 | /// 验证结果的回调 71 | - (void)loginVerifyResult:(SCLoginVerifyBlock)block { 72 | self.block = block; 73 | } 74 | 75 | /// 显示view(此方法是加载在window上 ,遮住导航条) 76 | - (void)showView { 77 | 78 | [UIView animateWithDuration:0.25 animations:^{ 79 | UIWindow * window = [[[UIApplication sharedApplication] delegate] window]; 80 | [window addSubview:self]; 81 | 82 | } completion:^(BOOL finished) { 83 | // 84 | self.alpha = 1.0; 85 | }]; 86 | 87 | 88 | } 89 | 90 | #pragma mark - 布局UI 91 | /// 根据验证类型布局UI 92 | - (void)layoutUI:(SCLoginVerifyType)verifyType { 93 | self.alpha = 0.5; 94 | self.backgroundColor = [UIColor whiteColor]; 95 | // 顶部的头像和底部的切换登录方式按钮,是共有的 96 | // 头像 97 | if (!_headImageView) { 98 | CGFloat posX = (ScreenWidth - HeadWH) * 0.5; 99 | 100 | _headImageView = [[UIImageView alloc] initWithFrame:CGRectMake(posX, MarginTop, HeadWH, HeadWH)]; 101 | _headImageView.clipsToBounds = YES; 102 | _headImageView.layer.cornerRadius = HeadWH * 0.5; 103 | } 104 | _headImageView.image = [UIImage imageNamed:@"head_Img.JPG"]; 105 | [self addSubview:_headImageView]; 106 | 107 | // 底部按钮 108 | 109 | if (!_bottomButton) { 110 | CGFloat posY = ScreenHeight - BottomHeight * 1.5; 111 | _bottomButton = [UIButton buttonWithType:UIButtonTypeCustom]; 112 | _bottomButton.backgroundColor = [UIColor clearColor]; 113 | _bottomButton.frame = CGRectMake(0.0, posY, ScreenWidth, BottomHeight); 114 | _bottomButton.titleLabel.font = [UIFont systemFontOfSize:14.0]; 115 | [_bottomButton setTitleColor:UIColorFrom16RGB(0x3393F2, 1.0) forState:UIControlStateNormal]; 116 | [_bottomButton setTitle:@"切换其他账号登录" forState:UIControlStateNormal]; 117 | } 118 | [_bottomButton addTarget:self action:@selector(bottomButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 119 | [self addSubview:_bottomButton]; 120 | 121 | 122 | switch (verifyType) { 123 | 124 | case SCLoginVerifyTypeTouchID: 125 | // 指纹密码登录 126 | [self loginVerifyTouchID]; 127 | break; 128 | 129 | case SCLoginVerifyTypeGesture: 130 | // 手势密码登录 131 | [self loginVerifyGesture]; 132 | break; 133 | 134 | default: 135 | break; 136 | } 137 | } 138 | 139 | /// 指纹密码登录 140 | - (void)loginVerifyTouchID { 141 | if (!_touchIDButton) { 142 | CGFloat posY = CGRectGetMaxY(_headImageView.frame) + TouchIDHeight; 143 | UIButton *touchIDButton = [UIButton buttonWithType:UIButtonTypeCustom]; 144 | touchIDButton.frame = CGRectMake(0.0, posY, ScreenWidth, TouchIDHeight); 145 | touchIDButton.titleLabel.font = [UIFont systemFontOfSize:12.0]; 146 | touchIDButton.imageEdgeInsets = UIEdgeInsetsMake(-30.0, 100.0, 0.0, 0.0); // 上左下右 147 | touchIDButton.titleEdgeInsets = UIEdgeInsetsMake(50.0, -40.0, 0.0, 0.0); 148 | [touchIDButton setTitleColor:UIColorFrom16RGB(0x3393F2, 1.0) forState:UIControlStateNormal]; 149 | [touchIDButton setImage:[UIImage imageNamed:@"fingerprint"] forState:UIControlStateNormal]; 150 | [touchIDButton setTitle:@"点击进行指纹解锁" forState:UIControlStateNormal]; 151 | _touchIDButton = touchIDButton; 152 | } 153 | [_touchIDButton addTarget:self action:@selector(touchIDButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 154 | [self addSubview:_touchIDButton]; 155 | [self touchIDButtonAction:_touchIDButton]; 156 | } 157 | 158 | /// 手势密码登录 159 | - (void)loginVerifyGesture { 160 | 161 | // 错误的警告Label 162 | if (!_messageLabel) { 163 | CGFloat labelY = CGRectGetMaxY(self.headImageView.frame) + Margin; 164 | _messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, labelY, ScreenWidth, LabelHeight)]; 165 | _messageLabel.textAlignment = NSTextAlignmentCenter; 166 | _messageLabel.font = [UIFont systemFontOfSize:15.0]; 167 | _messageLabel.backgroundColor = [UIColor clearColor]; 168 | _messageLabel.textColor = SCCircleErrorColor; 169 | } 170 | [self addSubview:_messageLabel]; 171 | 172 | // 手势九宫格 173 | if (!_gestureView) { 174 | CGFloat gestureViewX = (ScreenWidth - GestureWH) * 0.5; 175 | CGFloat gestureViewY = CGRectGetMaxY(self.messageLabel.frame) + LabelHeight; 176 | _gestureView = [[MYZGestureView alloc] initWithFrame:CGRectMake(gestureViewX, gestureViewY, GestureWH, GestureWH)]; 177 | } 178 | //是否显示指示手势划过的方向箭头, 在初始设置手势密码的时候才显示, 其他的不用显示 179 | _gestureView.hideGesturePath = ![SCSecureHelper gestureShowStatus]; 180 | [self addSubview:_gestureView]; 181 | 182 | kWeakSelf 183 | [self.gestureView setGestureResult:^BOOL(NSString *gestureCode) { 184 | return [weakSelf gestureResult:gestureCode]; 185 | }]; 186 | 187 | } 188 | 189 | #pragma mark - 内部方法 190 | 191 | // 手势密码结果的处理 192 | - (BOOL)gestureResult:(NSString *)gestureCode { 193 | 194 | if (gestureCode.length >= 4) { 195 | // 验证原密码 196 | NSString * saveGestureCode = [SCSecureHelper gainGestureCodeKey]; 197 | if ([gestureCode isEqualToString:saveGestureCode]) { 198 | // 验证成功 进行进一步处理 199 | [self dismiss]; 200 | return YES; 201 | } 202 | // 错误就提醒 203 | [self showGestureCodeErrorMessage]; 204 | } else { 205 | [self showGestureCodeErrorMessage]; 206 | } 207 | 208 | return NO; 209 | } 210 | 211 | /// 手势密码错误的提示 212 | - (void)showGestureCodeErrorMessage { 213 | self.messageLabel.text = passwordErrorMessage; 214 | self.messageLabel.textColor = SCCircleErrorColor; 215 | [self.messageLabel.layer shake]; 216 | } 217 | 218 | /// 移除view 219 | - (void)dismiss { 220 | kWeakSelf 221 | [UIView animateWithDuration:0.25 animations:^{ 222 | weakSelf.alpha = 0; 223 | } completion:^(BOOL finished) { 224 | [weakSelf removeFromSuperview]; 225 | }]; 226 | } 227 | 228 | #pragma mark - action事件 229 | 230 | // 进行指纹解锁按钮的action 231 | - (void)touchIDButtonAction:(UIButton *)sender { 232 | if (self.touchBtnClick) { 233 | return; 234 | } 235 | self.touchBtnClick = YES; 236 | kWeakSelf 237 | [[SCSecureHelper shareInstance] openTouchID:^(BOOL success, BOOL inputPassword, NSString *message) { 238 | weakSelf.touchBtnClick = NO; 239 | if (success) { 240 | [weakSelf dismiss]; 241 | } 242 | }]; 243 | 244 | } 245 | 246 | // 切换其他账号登录 247 | - (void)bottomButtonAction:(UIButton *)sender { 248 | if (self.bottomBtnClick) { 249 | return; 250 | } 251 | self.bottomBtnClick = YES; 252 | [self dismiss]; 253 | self.bottomBtnClick = NO; 254 | } 255 | 256 | 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureTouchID/fingerprint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/GestureTouchID/fingerprint.png -------------------------------------------------------------------------------- /SCTouchDemo/GestureTouchID/head_Img.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/GestureTouchID/head_Img.JPG -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZCircleView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MYZCircleView.h 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/8/1. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 绘制手势时,圆圈的圆心 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, GestureViewStatus) { 12 | GestureViewStatusNormal, 13 | GestureViewStatusSelected, 14 | GestureViewStatusSelectedAndShowArrow, 15 | GestureViewStatusError, 16 | GestureViewStatusErrorAndShowArrow, 17 | }; 18 | 19 | @interface MYZCircleView : UIView 20 | 21 | @property (nonatomic, assign) GestureViewStatus circleStatus; 22 | 23 | /** 相邻两圆圈连线的方向角度 */ 24 | @property (nonatomic) CGFloat angle; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZCircleView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MYZCircleView.m 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/8/1. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 绘制圆圈 8 | 9 | #import "MYZCircleView.h" 10 | 11 | //外空心圆边界宽度 12 | CGFloat const circleBorderWidth = 1.0f; 13 | //内部的实心圆所占外圆的比例大小 14 | CGFloat const circleRatio = 0.3f; 15 | //三角形箭头的边长 16 | CGFloat const arrowH = 8.0; 17 | 18 | @interface MYZCircleView () 19 | 20 | @property (nonatomic, strong) UIColor * circleColor; 21 | 22 | @end 23 | 24 | @implementation MYZCircleView 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame { 27 | if (self = [super initWithFrame:frame]) { 28 | self.backgroundColor = [UIColor clearColor]; 29 | self.circleStatus = GestureViewStatusNormal; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)drawRect:(CGRect)rect { 35 | // Drawing code 36 | 37 | CGContextRef cr = UIGraphicsGetCurrentContext(); 38 | CGContextClearRect(cr, rect); 39 | 40 | //画外圆圈 41 | CGFloat circleDiameter = MIN(rect.size.width, rect.size.height) - circleBorderWidth * 2.0; 42 | CGRect circleInRect = CGRectMake(circleBorderWidth, circleBorderWidth, circleDiameter, circleDiameter); 43 | CGContextAddEllipseInRect(cr, circleInRect); 44 | CGContextSetLineWidth(cr, circleBorderWidth); 45 | [self.circleColor set]; 46 | CGContextStrokePath(cr); 47 | 48 | //画内实心圆 49 | if (self.circleStatus != GestureViewStatusNormal) { 50 | 51 | CGFloat filledCircleDiameter = circleDiameter * circleRatio; 52 | CGFloat filledCircleX = (rect.size.width - filledCircleDiameter)*0.5; 53 | CGFloat filledCircleY = (rect.size.height - filledCircleDiameter)*0.5; 54 | CGContextAddEllipseInRect(cr, CGRectMake(filledCircleX, filledCircleY, filledCircleDiameter, filledCircleDiameter)); 55 | [self.circleColor set]; 56 | CGContextFillPath(cr); 57 | 58 | //画指示方向三角箭头 59 | if (self.circleStatus == GestureViewStatusSelectedAndShowArrow 60 | || self.circleStatus == GestureViewStatusErrorAndShowArrow) { 61 | //先平移到圆心然后在旋转然后在平移回来 62 | CGFloat offset = MIN(rect.size.width, rect.size.height) * 0.5; 63 | CGContextTranslateCTM(cr, offset, offset); 64 | CGContextRotateCTM(cr, self.angle); 65 | CGContextTranslateCTM(cr, -offset, -offset); 66 | 67 | CGFloat arrowMargin = (filledCircleY - arrowH) * 0.5; 68 | CGContextMoveToPoint(cr, (rect.size.width - arrowH * 1.5) * 0.5 , filledCircleY - arrowMargin); 69 | CGContextAddLineToPoint(cr, (rect.size.width + arrowH * 1.5) * 0.5 , filledCircleY - arrowMargin); 70 | CGContextAddLineToPoint(cr, rect.size.width * 0.5 , filledCircleY - arrowMargin - arrowH); 71 | CGContextClosePath(cr); 72 | CGContextSetFillColorWithColor(cr, self.circleColor.CGColor); 73 | CGContextFillPath(cr); 74 | } 75 | } 76 | } 77 | 78 | 79 | - (void)setCircleStatus:(GestureViewStatus)circleStatus { 80 | _circleStatus = circleStatus; 81 | 82 | if (_circleStatus == GestureViewStatusNormal) { 83 | self.circleColor = SCCircleNormalColor; 84 | 85 | } else if (_circleStatus == GestureViewStatusSelected 86 | || _circleStatus == GestureViewStatusSelectedAndShowArrow) { 87 | 88 | self.circleColor = SCCircleSelectedColor; 89 | 90 | } else if (_circleStatus == GestureViewStatusError 91 | || _circleStatus == GestureViewStatusErrorAndShowArrow) { 92 | 93 | self.circleColor = SCCircleErrorColor; 94 | } 95 | 96 | [self setNeedsDisplay]; 97 | } 98 | 99 | 100 | 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZGestureShapeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MYZGestureShapeView.h 3 | // PasscodeLockDemo 4 | // 5 | // Created by 159CaiMini02 on 16/8/4. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 手势的缩略图 8 | 9 | #import 10 | 11 | @interface MYZGestureShapeView : UIView 12 | 13 | @property (nonatomic, copy) NSString * gestureCode; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZGestureShapeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MYZGestureShapeView.m 3 | // PasscodeLockDemo 4 | // 5 | // Created by 159CaiMini02 on 16/8/4. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 手势的缩略图 8 | 9 | #import "MYZGestureShapeView.h" 10 | 11 | @interface MYZGestureShapeView () 12 | 13 | @property (nonatomic, strong) NSMutableArray * numberArray; 14 | 15 | @end 16 | 17 | @implementation MYZGestureShapeView 18 | 19 | - (NSMutableArray *)numberArray { 20 | if (!_numberArray) { 21 | NSMutableArray * array = [NSMutableArray arrayWithCapacity:9]; 22 | 23 | for (NSInteger i = 0; i < 9; i++) { 24 | NSNumber * n = [NSNumber numberWithInteger:0]; 25 | [array addObject:n]; 26 | } 27 | _numberArray = array; 28 | } 29 | return _numberArray; 30 | } 31 | 32 | 33 | // Only override drawRect: if you perform custom drawing. 34 | // An empty implementation adversely affects performance during animation. 35 | - (void)drawRect:(CGRect)rect { 36 | // Drawing code 37 | 38 | CGContextRef cr = UIGraphicsGetCurrentContext(); 39 | 40 | CGFloat squareWH = MIN(rect.size.width, rect.size.height); 41 | CGFloat marginBorder = 3.0; 42 | CGFloat marginNear = 3.0; 43 | CGFloat circleWH = (squareWH - (marginBorder+marginNear) * 2.0)/3.0; 44 | 45 | for (NSInteger i = 0; i < 9; i++) { 46 | NSInteger currentRow = i / 3; 47 | NSInteger currentColumn = i % 3; 48 | CGFloat circleX = marginBorder + (marginNear + circleWH) * currentColumn; 49 | CGFloat circleY = marginBorder + (marginNear + circleWH) * currentRow; 50 | CGRect circleFrame = CGRectMake(circleX, circleY, circleWH, circleWH); 51 | 52 | CGContextAddEllipseInRect(cr, circleFrame); 53 | [SCCircleNormalColor set]; 54 | 55 | NSNumber * n = [self.numberArray objectAtIndex:i]; 56 | if (n.integerValue == 1) { 57 | CGContextFillPath(cr); 58 | } else { 59 | CGContextSetLineWidth(cr, 1.0); 60 | CGContextStrokePath(cr); 61 | } 62 | } 63 | } 64 | 65 | // 66 | - (void)setGestureCode:(NSString *)gestureCode { 67 | _gestureCode = gestureCode; 68 | 69 | self.numberArray = nil; 70 | for (NSInteger i = 0; i < gestureCode.length; i ++) { 71 | 72 | NSString * subStr = [gestureCode substringWithRange:NSMakeRange(i, 1)]; 73 | NSInteger subInt = subStr.integerValue; 74 | [self.numberArray replaceObjectAtIndex:subInt withObject:[NSNumber numberWithInteger:1]]; 75 | } 76 | 77 | [self setNeedsDisplay]; 78 | } 79 | 80 | 81 | 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZGestureView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MYZGestureView.h 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/7/28. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 手势的九宫格 8 | 9 | #import 10 | 11 | @interface MYZGestureView : UIView 12 | 13 | /** 14 | * 手势操作完成回调方法,参数为手势划过的顺序密码0-8 15 | */ 16 | @property (nonatomic, copy) BOOL(^gestureResult)(NSString * gestureCode); 17 | 18 | /** 19 | * 是否显示手势划过的轨迹 20 | */ 21 | @property (nonatomic, getter=isHideGesturePath) BOOL hideGesturePath; 22 | 23 | /** 24 | * 是否显示手势划过的方向,就是每个圆里的三角箭头 25 | */ 26 | @property (nonatomic, getter=isShowArrowDirection) BOOL showArrowDirection; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /SCTouchDemo/GestureView/MYZGestureView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MYZGestureView.m 3 | // PasscodeLockDemo 4 | // 5 | // Created by MA806P on 16/7/28. 6 | // Copyright © 2016年 myz. All rights reserved. 7 | // 手势的九宫格 8 | 9 | #import "MYZGestureView.h" 10 | #import "MYZCircleView.h" 11 | #import "GCDDelay.h" 12 | 13 | /// 圆圈九宫格边界大小 14 | CGFloat const CircleViewMarginBorder = 5.0f; 15 | CGFloat const CircleViewMarginNear = 30.0f; 16 | /// 圆圈视图tag初始值 17 | NSInteger const CircleViewBaseTag = 100; 18 | /// 连线的宽度 19 | CGFloat const LineWidth = 4.0f; 20 | 21 | @interface MYZGestureView () 22 | 23 | @property (nonatomic, strong) NSMutableArray * selectCircleArray; 24 | @property (nonatomic, assign) CGPoint currentTouchPoint; 25 | @property (nonatomic, assign) GestureViewStatus gestureViewStatus; 26 | @property (nonatomic, strong) UIColor * lineColor; 27 | 28 | /// 取消绘制的GCD任务回调 29 | @property (nonatomic, copy) GCDTask gcdTask; 30 | 31 | @end 32 | 33 | @implementation MYZGestureView 34 | 35 | #pragma mark - initializer 36 | 37 | - (instancetype)initWithFrame:(CGRect)frame { 38 | if (self = [super initWithFrame:frame]) { 39 | [self initSubviews]; 40 | } 41 | return self; 42 | } 43 | 44 | /// 子视图初始化 45 | - (void)initSubviews { 46 | self.backgroundColor = [UIColor clearColor]; 47 | 48 | for (NSInteger i=0; i < 9; i ++) { 49 | MYZCircleView * circle = [[MYZCircleView alloc] init]; 50 | circle.tag = i+CircleViewBaseTag; 51 | [self addSubview:circle]; 52 | } 53 | } 54 | 55 | 56 | - (void)layoutSubviews { 57 | [super layoutSubviews]; 58 | 59 | //手势解锁视图的大小 60 | CGFloat squareWH = MIN(self.frame.size.width, self.frame.size.height); 61 | CGFloat offsetY = 0.0; 62 | CGFloat offsetX = 0.0; 63 | if (self.frame.size.width >= self.frame.size.height) { 64 | offsetX = (self.frame.size.width - self.frame.size.height) * 0.5; 65 | } else { 66 | offsetY = (self.frame.size.height - self.frame.size.width) * 0.5; 67 | } 68 | 69 | //小圆的大小 70 | CGFloat circleWH = (squareWH - (CircleViewMarginBorder+CircleViewMarginNear)*2.0)/3.0; 71 | 72 | for (NSInteger i = 0; i < 9; i ++) { 73 | MYZCircleView * circleView = [self viewWithTag:i+CircleViewBaseTag]; 74 | 75 | NSInteger currentRow = i / 3; 76 | NSInteger currentColumn = i % 3; 77 | CGFloat circleX = CircleViewMarginBorder + (CircleViewMarginNear + circleWH) * currentColumn; 78 | CGFloat circleY = CircleViewMarginBorder + (CircleViewMarginNear + circleWH) * currentRow; 79 | 80 | circleView.frame = CGRectMake(circleX + offsetX, circleY + offsetY, circleWH, circleWH); 81 | } 82 | 83 | } 84 | 85 | #pragma mark - setter getter 86 | 87 | - (NSMutableArray *)selectCircleArray { 88 | if (!_selectCircleArray) { 89 | _selectCircleArray = [NSMutableArray array]; 90 | } 91 | return _selectCircleArray; 92 | } 93 | 94 | - (void)setGestureViewStatus:(GestureViewStatus)gestureViewStatus { 95 | _gestureViewStatus = gestureViewStatus; 96 | 97 | if (_gestureViewStatus == GestureViewStatusNormal) { 98 | self.lineColor = SCCircleNormalColor; 99 | } else if (_gestureViewStatus == GestureViewStatusSelected) { 100 | self.lineColor = SCCircleSelectedColor; 101 | } else if (_gestureViewStatus == GestureViewStatusError) { 102 | self.lineColor = SCCircleErrorColor; 103 | } 104 | } 105 | 106 | #pragma mark - draw view 107 | 108 | - (void)drawRect:(CGRect)rect { 109 | if (self.isHideGesturePath) { 110 | return; 111 | } 112 | 113 | self.clearsContextBeforeDrawing = YES; 114 | CGContextRef cr = UIGraphicsGetCurrentContext(); 115 | 116 | //剪切,画线只能在圆外画 117 | CGContextAddRect(cr, rect); 118 | for (NSInteger i = 0; i < 9; i ++) { 119 | MYZCircleView * circleView = [self viewWithTag:(i + CircleViewBaseTag)]; 120 | CGContextAddEllipseInRect(cr, circleView.frame); 121 | } 122 | CGContextEOClip(cr); 123 | 124 | //画线 125 | for (NSInteger i = 0; i < self.selectCircleArray.count; i ++) { 126 | MYZCircleView * circleView = self.selectCircleArray[i]; 127 | CGPoint circleCentre = circleView.center; 128 | if (i == 0) { 129 | CGContextMoveToPoint(cr, circleCentre.x, circleCentre.y); 130 | } else { 131 | CGContextAddLineToPoint(cr, circleCentre.x, circleCentre.y); 132 | } 133 | } 134 | 135 | 136 | [self.selectCircleArray enumerateObjectsUsingBlock:^(MYZCircleView * circleView, NSUInteger idx, BOOL * _Nonnull stop) { 137 | if (circleView.circleStatus != GestureViewStatusError 138 | && circleView.circleStatus != GestureViewStatusErrorAndShowArrow) { 139 | CGContextAddLineToPoint(cr, self.currentTouchPoint.x, self.currentTouchPoint.y); 140 | } 141 | }]; 142 | 143 | CGContextSetLineCap(cr, kCGLineCapRound); 144 | CGContextSetLineWidth(cr, LineWidth); 145 | [self.lineColor set]; 146 | CGContextStrokePath(cr); 147 | 148 | } 149 | 150 | 151 | #pragma mark - Touch event 152 | 153 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 154 | [GCDDelay gcdCancel:self.gcdTask]; 155 | [self cleanViews]; 156 | [self checkCircleViewTouch:touches]; 157 | } 158 | 159 | 160 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 161 | [self checkCircleViewTouch:touches]; 162 | [self setNeedsDisplay]; 163 | } 164 | 165 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 166 | 167 | NSMutableString * gestureCode = [NSMutableString string]; 168 | for (MYZCircleView * circleView in self.selectCircleArray) { 169 | [gestureCode appendFormat:@"%ld",(long)(circleView.tag - CircleViewBaseTag)]; 170 | //circleView.circleStatus = GestureViewStatusNormal; 171 | } 172 | 173 | if (gestureCode.length > 0 && self.gestureResult != nil) { 174 | BOOL successGesture = self.gestureResult(gestureCode); 175 | if (successGesture) { 176 | [self cleanViews]; 177 | } else { 178 | if (!self.isHideGesturePath) { 179 | for (NSInteger i = 0; i < self.selectCircleArray.count; i ++) { 180 | MYZCircleView * circleView = self.selectCircleArray[i]; 181 | BOOL showArrow = (self.isShowArrowDirection && i != self.selectCircleArray.count - 1); 182 | //判断是否显示指示方向的箭头, 最后一个不用显示 183 | circleView.circleStatus = showArrow ? GestureViewStatusErrorAndShowArrow : GestureViewStatusError; 184 | self.gestureViewStatus = GestureViewStatusError; 185 | [self setNeedsDisplay]; 186 | } 187 | } 188 | 189 | self.gcdTask = [GCDDelay gcdDelay:0.3 task:^{ 190 | [self cleanViews]; 191 | }]; 192 | } 193 | } 194 | } 195 | 196 | 197 | - (void)cleanViews { 198 | for (MYZCircleView * circleView in self.selectCircleArray) { 199 | circleView.circleStatus = GestureViewStatusNormal; 200 | } 201 | [self.selectCircleArray removeAllObjects]; 202 | [self setNeedsDisplay]; 203 | } 204 | 205 | 206 | - (void)checkCircleViewTouch:(NSSet *)touches { 207 | UITouch *touch = [touches anyObject]; 208 | CGPoint point = [touch locationInView:self]; 209 | self.currentTouchPoint = point; 210 | 211 | for (NSInteger i = 0; i < 9; i ++) { 212 | MYZCircleView * circleView = [self viewWithTag:i+CircleViewBaseTag]; 213 | 214 | if(CGRectContainsPoint(circleView.frame, point) && ![self.selectCircleArray containsObject:circleView]) { 215 | if (!self.isHideGesturePath) { 216 | circleView.circleStatus = GestureViewStatusSelected; 217 | self.gestureViewStatus = GestureViewStatusSelected; 218 | 219 | //计算最后两个选中圆圈的角度,为了画出指示箭头 220 | if (self.selectCircleArray.count > 0 && self.isShowArrowDirection) { 221 | 222 | MYZCircleView * lastSelectCircleView = [self.selectCircleArray lastObject]; 223 | 224 | CGFloat x1 = circleView.center.x; 225 | CGFloat y1 = circleView.center.y; 226 | CGFloat x2 = lastSelectCircleView.center.x; 227 | CGFloat y2 = lastSelectCircleView.center.y; 228 | 229 | CGFloat angle = atan2(y1 - y2, x1 - x2) + M_PI_2; 230 | lastSelectCircleView.angle = angle; 231 | lastSelectCircleView.circleStatus = GestureViewStatusSelectedAndShowArrow; 232 | } 233 | } 234 | 235 | [self.selectCircleArray addObject:circleView]; 236 | break; 237 | } 238 | } 239 | } 240 | 241 | 242 | 243 | @end 244 | -------------------------------------------------------------------------------- /SCTouchDemo/Helper/SCSecureHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCSecureHelper.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/21. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 指纹密码/手势密码的helper类 8 | 9 | #import 10 | 11 | /** 12 | 指纹密码结果的回调block 13 | 14 | @param success 验证的结果 15 | @param inputPassword 输入登录密码验证 16 | @param message 验证结果的提示信息 17 | */ 18 | typedef void(^SCTouchIDOpenBlock)(BOOL success, BOOL inputPassword, NSString *message); 19 | 20 | @interface SCSecureHelper : NSObject 21 | /// 单例 22 | + (SCSecureHelper *)shareInstance; 23 | 24 | /// 手势密码的开启状态 25 | + (BOOL)gestureOpenStatus; 26 | 27 | /// 手势密码轨迹显示开启状态 28 | + (BOOL)gestureShowStatus; 29 | 30 | /// 关闭/开启手势密码 31 | + (void)openGesture:(BOOL)open; 32 | 33 | /// 关闭/开启手势密码 34 | + (void)openGestureShow:(BOOL)open; 35 | 36 | /// 获取保存手势密码的code 37 | + (NSString *)gainGestureCodeKey; 38 | 39 | /// 保存手势密码的code 40 | + (void)saveGestureCodeKey:(NSString *)gestureCode; 41 | 42 | /// 关闭/开启touchID 43 | + (void)touchIDOpen:(BOOL)open; 44 | /// touchID开启状态 45 | + (BOOL)touchIDOpenStatus; 46 | 47 | /// 开启指纹扫描的函数 48 | - (void)openTouchID:(SCTouchIDOpenBlock)block; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /SCTouchDemo/Helper/SCSecureHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCSecureHelper.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/21. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import "SCSecureHelper.h" 10 | #import 11 | 12 | // ios 版本判断 13 | #define SCSystemVersion [[[UIDevice currentDevice] systemVersion] floatValue] 14 | #define iOS9 (SCSystemVersion >= 9.0) 15 | 16 | @interface SCSecureHelper () 17 | 18 | @property (nonatomic, copy) SCTouchIDOpenBlock block; 19 | 20 | @end 21 | 22 | @implementation SCSecureHelper 23 | 24 | /// 单例 25 | + (SCSecureHelper *)shareInstance { 26 | 27 | static SCSecureHelper *helper = nil; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | helper = [[SCSecureHelper alloc] init]; 31 | }); 32 | return helper; 33 | } 34 | 35 | #pragma mark - 手势密码(Public) 36 | /// 手势密码的开启状态 37 | + (BOOL)gestureOpenStatus { 38 | NSNumber *tempNum = [[NSUserDefaults standardUserDefaults] objectForKey:Gesture_Password_Open]; 39 | BOOL gestureOpen = tempNum ? [tempNum boolValue] : NO; 40 | return gestureOpen; 41 | } 42 | 43 | /// 手势密码轨迹显示开启状态 44 | + (BOOL)gestureShowStatus { 45 | NSNumber *tempNum = [[NSUserDefaults standardUserDefaults] objectForKey:Gesture_Password_Show]; 46 | BOOL gestureShow = tempNum ? [tempNum boolValue] : NO; 47 | return gestureShow; 48 | } 49 | 50 | /// 关闭/开启手势密码 51 | + (void)openGesture:(BOOL)open { 52 | 53 | NSNumber *tempNum = open ? @(1) : @(0); 54 | [[NSUserDefaults standardUserDefaults] setObject:tempNum forKey:Gesture_Password_Open]; 55 | [[NSUserDefaults standardUserDefaults] setObject:tempNum forKey:Gesture_Password_Show]; 56 | [[NSUserDefaults standardUserDefaults] synchronize]; 57 | if (!open) { 58 | [self saveGestureCodeKey:nil]; 59 | } 60 | } 61 | 62 | /// 关闭/开启手势密码 63 | + (void)openGestureShow:(BOOL)open { 64 | NSNumber *tempNum = open ? @(1) : @(0); 65 | [[NSUserDefaults standardUserDefaults] setObject:tempNum forKey:Gesture_Password_Show]; 66 | [[NSUserDefaults standardUserDefaults] synchronize]; 67 | } 68 | 69 | /// 获取保存手势密码的code 70 | + (NSString *)gainGestureCodeKey { 71 | NSString *gestureCode = [[NSUserDefaults standardUserDefaults] objectForKey:Gesture_Code_Key]; 72 | return gestureCode ? gestureCode : @""; 73 | } 74 | /// 保存手势密码的code 75 | + (void)saveGestureCodeKey:(NSString *)gestureCode { 76 | [[NSUserDefaults standardUserDefaults] setObject:gestureCode forKey:Gesture_Code_Key]; 77 | [[NSUserDefaults standardUserDefaults] synchronize]; 78 | } 79 | 80 | #pragma mark - TouchID(Public) 81 | /// touchID开启状态 82 | + (BOOL)touchIDOpenStatus { 83 | NSNumber *tempNum = [[NSUserDefaults standardUserDefaults] objectForKey:TouchID_Password_Open]; 84 | BOOL touchID = tempNum ? [tempNum boolValue] : NO; 85 | return touchID; 86 | } 87 | 88 | /// 关闭/开启touchID 89 | + (void)touchIDOpen:(BOOL)open { 90 | NSNumber *tempNum = open ? @(1) : @(0); 91 | [[NSUserDefaults standardUserDefaults] setObject:tempNum forKey:TouchID_Password_Open]; 92 | [[NSUserDefaults standardUserDefaults] synchronize]; 93 | } 94 | 95 | /// 开启指纹扫描的函数 96 | - (void)openTouchID:(SCTouchIDOpenBlock)block { 97 | self.block = block; 98 | [self openTouchIDWithPolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics touchIDBlock:block]; 99 | 100 | } 101 | 102 | #pragma mark - 内部方法(Private) 103 | 104 | #pragma mark - 手势密码(Private) 105 | 106 | #pragma mark - TouchID(Private) 107 | /* 108 | typedef NS_ENUM(NSInteger, LAError) 109 | { 110 | LAErrorAuthenticationFailed, // 验证信息出错,就是说你指纹不对 111 | LAErrorUserCancel // 用户取消了验证 112 | LAErrorUserFallback // 用户点击了手动输入密码的按钮,所以被取消了 113 | LAErrorSystemCancel // 被系统取消,就是说你现在进入别的应用了,不在刚刚那个页面,所以没法验证 114 | LAErrorPasscodeNotSet // 用户没有设置TouchID 115 | LAErrorTouchIDNotAvailable // 用户设备不支持TouchID 116 | LAErrorTouchIDNotEnrolled // 用户没有设置手指指纹 117 | LAErrorTouchIDLockout // 用户错误次数太多,现在被锁住了 118 | LAErrorAppCancel // 在验证中被其他app中断 119 | LAErrorInvalidContext // 请求验证出错 120 | } 121 | 122 | typedef NS_ENUM(NSInteger, LAPolicy) 123 | { 124 | LAPolicyDeviceOwnerAuthenticationWithBiometrics NS_ENUM_AVAILABLE(NA, 8_0) __WATCHOS_AVAILABLE(3.0) __TVOS_AVAILABLE(10.0) = kLAPolicyDeviceOwnerAuthenticationWithBiometrics, 125 | LAPolicyDeviceOwnerAuthentication NS_ENUM_AVAILABLE(10_11, 9_0) = kLAPolicyDeviceOwnerAuthentication 126 | 127 | } 128 | 第一个枚举LAPolicyDeviceOwnerAuthenticationWithBiometrics就是说,用的是手指指纹去验证的;NS_ENUM_AVAILABLE(NA, 8_0)iOS8 可用 129 | 第二个枚举LAPolicyDeviceOwnerAuthentication少了WithBiometrics则是使用TouchID或者密码验证,默认是错误两次指纹或者锁定后,弹出输入密码界面;NS_ENUM_AVAILABLE(10_11, 9_0)iOS 9可用 130 | */ 131 | /// 开启指纹扫描 132 | - (void)openTouchIDWithPolicy:(LAPolicy )policy touchIDBlock:(SCTouchIDOpenBlock)block { 133 | LAContext *context = [[LAContext alloc] init]; 134 | context.localizedFallbackTitle = @"验证登录密码"; 135 | // LAPolicyDeviceOwnerAuthentication 136 | kWeakSelf 137 | [context evaluatePolicy:policy localizedReason:@"通过Home键验证已有手机指纹" reply:^(BOOL success, NSError * _Nullable error) { 138 | 139 | dispatch_async(dispatch_get_main_queue(), ^{ 140 | NSString *message = @""; 141 | if (success) { 142 | message = @"通过了Touch ID 指纹验证"; 143 | block(YES, NO, message); 144 | } else { 145 | //失败操作 146 | LAError errorCode = error.code; 147 | BOOL inputPassword = NO; 148 | switch (errorCode) { 149 | case LAErrorAuthenticationFailed: { 150 | // -1 151 | [SVProgressHUD showErrorWithStatus:@"指纹不匹配"]; 152 | message = @"连续三次指纹识别错误"; 153 | } 154 | break; 155 | 156 | case LAErrorUserCancel: { 157 | // -2 158 | message = @"用户取消验证Touch ID"; 159 | } 160 | break; 161 | 162 | case LAErrorUserFallback: { 163 | // -3 164 | inputPassword = YES; 165 | message = @"用户选择输入密码"; 166 | } 167 | break; 168 | 169 | case LAErrorSystemCancel: { 170 | // -4 TouchID对话框被系统取消,例如按下Home或者电源键 171 | message = @"取消授权,如其他应用切入"; 172 | } 173 | break; 174 | 175 | case LAErrorPasscodeNotSet: { 176 | // -5 177 | [SVProgressHUD showErrorWithStatus:@"此设备未设置系统密码"]; 178 | message = @"设备系统未设置密码"; 179 | } 180 | break; 181 | 182 | case LAErrorTouchIDNotAvailable: { 183 | // -6 184 | [SVProgressHUD showErrorWithStatus:@"此设备不支持 Touch ID"]; 185 | message = @"此设备不支持 Touch ID"; 186 | } 187 | break; 188 | 189 | case LAErrorTouchIDNotEnrolled: { 190 | // -7 191 | [SVProgressHUD showErrorWithStatus:@"用户未录入指纹"]; 192 | message = @"用户未录入指纹"; 193 | } 194 | break; 195 | 196 | case LAErrorTouchIDLockout: { 197 | // -8 连续五次指纹识别错误,TouchID功能被锁定,下一次需要输入系统密码 198 | if (iOS9) { 199 | [weakSelf openTouchIDWithPolicy:LAPolicyDeviceOwnerAuthentication touchIDBlock:block]; 200 | } 201 | message = @"Touch ID被锁,需要用户输入密码解锁"; 202 | } 203 | break; 204 | 205 | case LAErrorAppCancel: { 206 | // -9 如突然来了电话,电话应用进入前台,APP被挂起啦 207 | message = @"用户不能控制情况下APP被挂起"; 208 | } 209 | break; 210 | 211 | case LAErrorInvalidContext: { 212 | // -10 213 | message = @"Touch ID 失效"; 214 | } 215 | break; 216 | 217 | default: 218 | // [SVProgressHUD showErrorWithStatus:@"此设备不支持 Touch ID"]; 219 | break; 220 | } 221 | block(success, inputPassword, message); 222 | } 223 | }); 224 | }]; 225 | } 226 | 227 | @end 228 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/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 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/scangreen.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "scangreen@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "scangreen@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/scangreen.imageset/scangreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/Resources/Assets.xcassets/scangreen.imageset/scangreen@2x.png -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/scangreen.imageset/scangreen@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/Resources/Assets.xcassets/scangreen.imageset/scangreen@3x.png -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/searcharrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "searcharrow@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "searcharrow@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/searcharrow.imageset/searcharrow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/Resources/Assets.xcassets/searcharrow.imageset/searcharrow@2x.png -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Assets.xcassets/searcharrow.imageset/searcharrow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/SCTouchDemo/Resources/Assets.xcassets/searcharrow.imageset/searcharrow@3x.png -------------------------------------------------------------------------------- /SCTouchDemo/Resources/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 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/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 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | 手势/TouchID登录 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/SCHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCHeader.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #ifndef SCHeader_h 10 | #define SCHeader_h 11 | 12 | // 指纹密码是否开启的key 13 | #define TouchID_Password_Open @"TouchID_Password_Open" 14 | // 手势密码是否开启的key 15 | #define Gesture_Password_Open @"Gesture_Password_Open" 16 | // 记录手势密码GestureCodeKey 17 | #define Gesture_Code_Key @"Gesture_Code_Key" 18 | // 手势密码是否显示轨迹的key 19 | #define Gesture_Password_Show @"Gesture_Password_Show" 20 | 21 | 22 | #define ScreenBounds [UIScreen mainScreen].bounds 23 | #define ScreenWidth ScreenBounds.size.width 24 | #define ScreenHeight ScreenBounds.size.height 25 | #define SCViewHeight CGRectGetHeight(ScreenBounds) - 44.0 - 20.0 // 去掉导航条的高度 26 | 27 | /// 根据16位RBG值转换成颜色,格式:UIColorFrom16RGB(0xFF0000) 28 | #define UIColorFrom16RGB(rgbValue,alp) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:alp] 29 | 30 | /// 根据10位RBG值转换成颜色, 格式:UIColorFrom10RGB(255,255,255) 31 | #define UIColorFrom10RGB(RED, GREEN, BLUE,alp) [UIColor colorWithRed:RED/255.0 green:GREEN/255.0 blue:BLUE/255.0 alpha:alp] 32 | 33 | 34 | /// 手势圆圈正常的颜色 35 | #define SCCircleNormalColor UIColorFrom16RGB(0x33CCFF,1.0) 36 | /// 手势圆圈选中的颜色 37 | #define SCCircleSelectedColor UIColorFrom16RGB(0x3393F2,1.0) 38 | /// 手势圆圈错误的颜色 39 | #define SCCircleErrorColor UIColorFrom16RGB(0xFF0033,1.0) 40 | 41 | // 手势密码提示Label的高度 42 | #define LabelHeight 20.0f 43 | // 手势密码九宫格view的宽高 44 | #define GestureWH 260.0f 45 | 46 | // 对于block的弱引用 47 | #define kWeakSelf __weak __typeof(self)weakSelf = self; 48 | 49 | #endif /* SCHeader_h */ 50 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/SCTouchDemo.pch: -------------------------------------------------------------------------------- 1 | // 2 | // SCTouchDemo.pch 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #ifndef SCTouchDemo_pch 10 | #define SCTouchDemo_pch 11 | 12 | #import "SCHeader.h" 13 | #import "SVProgressHUD.h" 14 | #import "UIView+Ext.h" 15 | 16 | #endif /* SCTouchDemo_pch */ 17 | -------------------------------------------------------------------------------- /SCTouchDemo/Resources/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. 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 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SCGestureController.h" 11 | #import "SCTouchIDController.h" 12 | #import "SCMainCell.h" 13 | #import "SCSecureHelper.h" 14 | #import "SCLoginVerifyView.h" 15 | 16 | @interface ViewController () 17 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 18 | @property (nonatomic, strong) NSMutableArray *dataArray; 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | self.title = @"安全设置"; 27 | self.automaticallyAdjustsScrollViewInsets = NO; 28 | [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([SCMainCell class]) bundle:nil] 29 | forCellReuseIdentifier:NSStringFromClass([SCMainCell class])]; 30 | } 31 | 32 | - (void)viewWillAppear:(BOOL)animated { 33 | [super viewWillAppear:animated]; 34 | [self reloadArray]; 35 | [self.tableView reloadData]; 36 | } 37 | 38 | - (void)reloadArray { 39 | if (self.dataArray.count > 0) { 40 | [self.dataArray removeAllObjects]; 41 | } 42 | BOOL gestureOpen = [SCSecureHelper gestureOpenStatus]; 43 | BOOL touchIDOpen = [SCSecureHelper touchIDOpenStatus]; 44 | self.dataArray = [NSMutableArray arrayWithArray:@[@"手势密码", @"指纹密码"]]; 45 | if (gestureOpen) { 46 | [self.dataArray addObject:@"手势密码验证"]; 47 | } 48 | 49 | if (touchIDOpen) { 50 | [self.dataArray addObject:@"指纹密码验证"]; 51 | } 52 | } 53 | 54 | // iOS8 分割线右移15像素 下面将其归零 55 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 56 | 57 | if ([cell respondsToSelector:@selector(setSeparatorInset:)]) { 58 | [cell setSeparatorInset:UIEdgeInsetsZero]; 59 | } 60 | 61 | if ([cell respondsToSelector:@selector(setLayoutMargins:)]) { 62 | [cell setLayoutMargins:UIEdgeInsetsZero]; 63 | } 64 | } 65 | 66 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 67 | return self.dataArray.count; 68 | } 69 | 70 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { 71 | 72 | return CGFLOAT_MIN; 73 | } 74 | 75 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 76 | return CGFLOAT_MIN; 77 | } 78 | 79 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 80 | return 44.0; 81 | } 82 | 83 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 84 | 85 | SCMainCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SCMainCell class]) forIndexPath:indexPath]; 86 | NSString *title = self.dataArray[indexPath.row]; 87 | NSString *content = @""; 88 | if ([title isEqualToString:@"手势密码"]) { 89 | content = [SCSecureHelper gestureOpenStatus] ? @"已开启" : @"未设置"; 90 | } else if ([title isEqualToString:@"指纹密码"]) { 91 | content = [SCSecureHelper touchIDOpenStatus] ? @"已开启" : @"未设置"; 92 | } 93 | cell.titleLabel.text = title; 94 | cell.contentLabel.text = content; 95 | return cell; 96 | } 97 | 98 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 99 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 100 | 101 | SCLoginVerifyType type = SCLoginVerifyTypeTouchID; 102 | UIViewController *controller = nil; 103 | NSString *tempTitle = self.dataArray[indexPath.row]; 104 | if ([tempTitle isEqualToString:@"手势密码"]) { 105 | controller = [[SCGestureController alloc] init]; 106 | } else if ([tempTitle isEqualToString:@"指纹密码"]) { 107 | controller = [[SCTouchIDController alloc] init]; 108 | } else if ([tempTitle isEqualToString:@"手势密码验证"]) { 109 | type = SCLoginVerifyTypeGesture; 110 | } else if ([tempTitle isEqualToString:@"指纹密码验证"]) { 111 | type = SCLoginVerifyTypeTouchID; 112 | } 113 | if (controller) { 114 | [self.navigationController pushViewController:controller animated:YES]; 115 | } else { 116 | SCLoginVerifyView *verifyView = [[SCLoginVerifyView alloc] initWithFrame:CGRectMake(0.0, 0.0, ScreenWidth, ScreenHeight) verifyType:type]; 117 | [verifyView showView]; 118 | } 119 | } 120 | 121 | - (void)didReceiveMemoryWarning { 122 | [super didReceiveMemoryWarning]; 123 | // Dispose of any resources that can be recreated. 124 | } 125 | 126 | /* 127 | 对编辑图片后图片反转90度的处理 128 | */ 129 | - (UIImage *)fixOrientation:(UIImage *)aImage { 130 | 131 | if (aImage.imageOrientation == UIImageOrientationUp) 132 | return aImage; 133 | 134 | CGAffineTransform transform = CGAffineTransformIdentity; 135 | 136 | switch (aImage.imageOrientation) { 137 | case UIImageOrientationDown: 138 | case UIImageOrientationDownMirrored: 139 | transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); 140 | transform = CGAffineTransformRotate(transform, M_PI); 141 | break; 142 | 143 | case UIImageOrientationLeft: 144 | case UIImageOrientationLeftMirrored: 145 | transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); 146 | transform = CGAffineTransformRotate(transform, M_PI_2); 147 | break; 148 | 149 | case UIImageOrientationRight: 150 | case UIImageOrientationRightMirrored: 151 | transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); 152 | transform = CGAffineTransformRotate(transform, -M_PI_2); 153 | break; 154 | default: 155 | break; 156 | } 157 | 158 | switch (aImage.imageOrientation) { 159 | case UIImageOrientationUpMirrored: 160 | case UIImageOrientationDownMirrored: 161 | transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); 162 | transform = CGAffineTransformScale(transform, -1, 1); 163 | break; 164 | 165 | case UIImageOrientationLeftMirrored: 166 | case UIImageOrientationRightMirrored: 167 | transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); 168 | transform = CGAffineTransformScale(transform, -1, 1); 169 | break; 170 | default: 171 | break; 172 | } 173 | 174 | CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, 175 | CGImageGetBitsPerComponent(aImage.CGImage), 0, 176 | CGImageGetColorSpace(aImage.CGImage), 177 | CGImageGetBitmapInfo(aImage.CGImage)); 178 | CGContextConcatCTM(ctx, transform); 179 | switch (aImage.imageOrientation) { 180 | case UIImageOrientationLeft: 181 | case UIImageOrientationLeftMirrored: 182 | case UIImageOrientationRight: 183 | case UIImageOrientationRightMirrored: 184 | // Grr... 185 | CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); 186 | break; 187 | 188 | default: 189 | CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); 190 | break; 191 | } 192 | 193 | 194 | CGImageRef cgimg = CGBitmapContextCreateImage(ctx); 195 | UIImage *img = [UIImage imageWithCGImage:cgimg]; 196 | CGContextRelease(ctx); 197 | CGImageRelease(cgimg); 198 | return img; 199 | } 200 | 201 | 202 | @end 203 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCGestureController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCGestureController.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势密码 8 | 9 | #import 10 | 11 | @interface SCGestureController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCGestureController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCGestureController.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势密码 8 | 9 | #import "SCGestureController.h" 10 | #import "SCGestureSetController.h" 11 | #import "SCSecureHelper.h" 12 | #import "SCSwitchCell.h" 13 | #import "SCNormalCell.h" 14 | 15 | @interface SCGestureController () 16 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 17 | @property (nonatomic, strong) UISwitch *openSwitch; // 开启/关闭手势的Switch 18 | @property (nonatomic, strong) UISwitch *showSwitch; // 显示/隐藏手势轨迹的Switch 19 | @property (nonatomic, strong) NSArray *dataArray; 20 | 21 | @end 22 | 23 | @implementation SCGestureController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | self.title = @"手势密码"; 28 | 29 | [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([SCSwitchCell class]) bundle:nil] 30 | forCellReuseIdentifier:NSStringFromClass([SCSwitchCell class])]; 31 | [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([SCNormalCell class]) bundle:nil] 32 | forCellReuseIdentifier:NSStringFromClass([SCNormalCell class])]; 33 | 34 | [self reloadGestureData]; 35 | } 36 | 37 | - (void)didReceiveMemoryWarning { 38 | [super didReceiveMemoryWarning]; 39 | } 40 | 41 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 42 | return self.dataArray.count; 43 | } 44 | 45 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { 46 | 47 | return CGFLOAT_MIN; 48 | } 49 | 50 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 51 | return CGFLOAT_MIN; 52 | } 53 | 54 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 55 | return 44.0; 56 | } 57 | 58 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 59 | 60 | UITableViewCell *cell = nil; 61 | 62 | if ([SCSecureHelper gestureOpenStatus] && indexPath.row == 2) { 63 | 64 | SCNormalCell *normalCell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SCNormalCell class]) forIndexPath:indexPath]; 65 | normalCell.titleLabel.text = self.dataArray[indexPath.row]; 66 | normalCell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 67 | cell = normalCell; 68 | 69 | } else { 70 | 71 | SCSwitchCell *switchCell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SCSwitchCell class]) forIndexPath:indexPath]; 72 | NSString *title = self.dataArray[indexPath.row]; 73 | BOOL isOn = indexPath.row == 0 ? [SCSecureHelper gestureOpenStatus] : [SCSecureHelper gestureShowStatus]; 74 | kWeakSelf 75 | [switchCell setupSwitchOn:isOn switchBlock:^(UISwitch *switchButton) { 76 | if (indexPath.row == 0) { 77 | [weakSelf gestureOpenSwitchAction:switchButton]; 78 | return ; 79 | } 80 | [weakSelf gestureShowSwitchAction:switchButton]; 81 | }]; 82 | switchCell.titleLabel.text = title; 83 | cell = switchCell; 84 | } 85 | 86 | return cell; 87 | } 88 | 89 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 90 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 91 | if ([SCSecureHelper gestureOpenStatus] && indexPath.row == 2) { 92 | // 修改手势密码的处理 93 | SCGestureSetController *controller = [[SCGestureSetController alloc] init]; 94 | controller.gestureSetType = SCGestureSetTypeChange; 95 | [controller gestureSetComplete:^(BOOL success) { 96 | if (success) { 97 | [SVProgressHUD showSuccessWithStatus:@"修改手势成功"]; 98 | } 99 | }]; 100 | [self.navigationController pushViewController:controller animated:YES]; 101 | } 102 | } 103 | 104 | // 刷新手势密码开启状态,对应的显示内容 105 | - (void)reloadGestureData { 106 | BOOL isOpen = [SCSecureHelper gestureOpenStatus]; 107 | NSLog(@"isOpen====状态是%@==",(isOpen ? @"开启" : @"关闭")); 108 | if (isOpen) { 109 | self.dataArray = @[@"手势密码", @"显示手势密码轨迹", @"修改手势密码"]; 110 | return; 111 | } 112 | self.dataArray = @[@"手势密码"]; 113 | } 114 | 115 | // 显示/隐藏手势密码轨迹 116 | - (void)gestureShowSwitchAction:(UISwitch *)sender { 117 | BOOL show = [SCSecureHelper gestureShowStatus]; 118 | // 直接取反就行 119 | [SCSecureHelper openGestureShow:!show]; 120 | [sender setOn:!show animated:YES]; 121 | } 122 | 123 | // 开启/关闭手势密码 124 | - (void)gestureOpenSwitchAction:(UISwitch *)sender { 125 | 126 | BOOL isOpen = [SCSecureHelper gestureOpenStatus]; 127 | // isOpen是YES代表之前是开启的,现在关闭,setType为SCGestureSetTypeDelete; 128 | // isOpen是NO代表之前是关闭的,现在开启,setType为SCGestureSetTypeSetting; 129 | SCGestureSetType setType = isOpen ? SCGestureSetTypeDelete : SCGestureSetTypeSetting; 130 | 131 | SCGestureSetController *controller = [[SCGestureSetController alloc] init]; 132 | controller.gestureSetType = setType; 133 | kWeakSelf 134 | [controller gestureSetComplete:^(BOOL success) { 135 | [weakSelf showSwitchOnStatus:sender handleSuccess:success]; 136 | }]; 137 | [self.navigationController pushViewController:controller animated:YES]; 138 | } 139 | 140 | /// handleResult 手势操作结果 success是操作结果yes成功,no 失败 141 | - (void)showSwitchOnStatus:(UISwitch *)sender handleSuccess:(BOOL)success { 142 | 143 | BOOL isOpen = [SCSecureHelper gestureOpenStatus]; 144 | if (!success) { 145 | // 不成功,依旧为当前的值 146 | [sender setOn:isOpen animated:YES]; 147 | return; 148 | } 149 | 150 | // isOpen是YES代表之前是开启的,现在关闭,nowOpenStatus为NO; isOpen是NO代表之前是关闭的,现在开启,nowOpenStatus为YES; 151 | BOOL nowOpenStatus = isOpen ? NO : YES; 152 | [SCSecureHelper openGesture:nowOpenStatus]; 153 | [sender setOn:nowOpenStatus animated:YES]; 154 | [self reloadGestureData]; 155 | if (!isOpen) { 156 | [SVProgressHUD showSuccessWithStatus:@"手势密码已设置"]; 157 | } 158 | [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic]; 159 | } 160 | 161 | - (void)dealloc { 162 | NSLog(@"%@===dealloc",NSStringFromClass([self class])); 163 | } 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCGestureController.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCGestureSetController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCGestureSetController.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/20. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势设置/删除/修改页面 8 | 9 | #import 10 | 11 | typedef NS_ENUM(NSInteger, SCGestureSetType) { 12 | SCGestureSetTypeSetting = 1, // 设置 13 | SCGestureSetTypeDelete, // 删除设置 14 | SCGestureSetTypeChange // 修改 15 | }; 16 | 17 | // 设置的结果 18 | typedef void(^SCGestureSetBlock)(BOOL success); 19 | 20 | @interface SCGestureSetController : UIViewController 21 | 22 | @property (nonatomic, assign) SCGestureSetType gestureSetType; 23 | 24 | /// 设置手势完成的回调 25 | - (void)gestureSetComplete:(SCGestureSetBlock)block; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCGestureSetController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCGestureSetController.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/20. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 手势设置/删除/修改页面 8 | 9 | #import "SCGestureSetController.h" 10 | #import "SCGestureController.h" 11 | #import "MYZGestureView.h" 12 | #import "MYZCircleView.h" 13 | #import "MYZGestureShapeView.h" 14 | #import "CALayer+shake.h" 15 | #import "SCSecureHelper.h" 16 | 17 | NSString * const promptDefaultMessage = @"绘制解锁图案"; 18 | NSString * const promptSetAgainMessage = @"再次绘制解锁图案"; 19 | NSString * const promptSetAgainErrorMessage = @"前后设置不一致"; 20 | NSString * const promptChangeGestureMessage = @"请输入原手势密码"; 21 | NSString * const promptPointShortMessage = @"连接至少4个点,请重新设置"; 22 | NSString * const promptPasswordErrorMessage = @"手势密码错误"; 23 | 24 | #define MarginTop 25.0 25 | #define Margin 8.0 26 | #define ShapeWH 40.0 27 | #define BottomHeight 44.0 28 | 29 | @interface SCGestureSetController () 30 | 31 | /// 手势缩略图 32 | @property (nonatomic, strong) MYZGestureShapeView * shapeView; 33 | /// 手势九宫格 34 | @property (nonatomic, strong) MYZGestureView * gestureView; 35 | @property (nonatomic, strong) UILabel * messageLabel; 36 | /// 底部的验证登录密码按钮 37 | @property (nonatomic, strong) UIButton * bottomButton; 38 | @property (nonatomic, copy) NSString * firstGestureCode; 39 | @property (nonatomic, copy) SCGestureSetBlock block; 40 | 41 | @end 42 | 43 | @implementation SCGestureSetController 44 | 45 | - (instancetype)init { 46 | if (self = [super init]) { 47 | _gestureSetType = SCGestureSetTypeChange; 48 | } 49 | return self; 50 | } 51 | 52 | - (void)viewDidLoad { 53 | [super viewDidLoad]; 54 | 55 | [self setupNav]; 56 | [self setupSubViews]; 57 | } 58 | 59 | - (void)didReceiveMemoryWarning { 60 | [super didReceiveMemoryWarning]; 61 | } 62 | 63 | #pragma mark - 布局UI 64 | - (void)setupSubViews { 65 | // 针对和UIScrollView类无关控件的处理,这样设置坐标从(0,0)开始时,不会被导航条遮挡. 66 | self.navigationController.navigationBar.translucent = NO; 67 | self.view.backgroundColor = [UIColor whiteColor]; 68 | [self.view addSubview:self.shapeView]; 69 | [self.view addSubview:self.messageLabel]; 70 | [self.view addSubview:self.bottomButton]; 71 | // 底部按钮,只有修改/删除type显示 72 | self.bottomButton.hidden = self.gestureSetType != SCGestureSetTypeSetting ? NO : YES; 73 | if (self.gestureSetType != SCGestureSetTypeSetting) { 74 | if ([self.shapeView superview]) { 75 | [self.shapeView removeFromSuperview]; 76 | } 77 | self.messageLabel.top = MarginTop + Margin; 78 | self.messageLabel.text = promptChangeGestureMessage; 79 | } 80 | [self setupGestureView]; 81 | 82 | } 83 | 84 | - (void)setupNav { 85 | self.title = @"设置手势密码"; 86 | if (self.gestureSetType == SCGestureSetTypeDelete) { 87 | self.title = @"验证手势密码"; 88 | } 89 | 90 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(back)]; 91 | [self setupNavRightItem]; 92 | } 93 | 94 | - (void)setupNavRightItem { 95 | if (self.gestureSetType != SCGestureSetTypeSetting) { 96 | return; 97 | } 98 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"重设" style:UIBarButtonItemStylePlain target:self action:@selector(resetGesture)]; 99 | } 100 | 101 | /// 手势九宫格 102 | - (void)setupGestureView { 103 | 104 | [self.view addSubview:self.gestureView]; 105 | //是否显示指示手势划过的方向箭头, 在初始设置手势密码的时候才显示, 其他的不用显示 106 | self.gestureView.showArrowDirection = self.gestureSetType == SCGestureSetTypeSetting ? YES : NO; 107 | 108 | kWeakSelf 109 | [self.gestureView setGestureResult:^BOOL(NSString *gestureCode) { 110 | return [weakSelf gestureResult:gestureCode]; 111 | }]; 112 | } 113 | 114 | // 验证手势密码成功后,修改手势密码用到 115 | - (void)resetTopViews { 116 | if (self.gestureSetType != SCGestureSetTypeChange) { 117 | return; 118 | } 119 | self.gestureSetType = SCGestureSetTypeSetting; 120 | if (![self.shapeView superview]) { 121 | [self.view addSubview:self.shapeView]; 122 | } 123 | self.bottomButton.hidden = YES; 124 | self.messageLabel.top = MarginTop + ShapeWH + Margin; 125 | self.messageLabel.text = promptDefaultMessage; 126 | self.messageLabel.textColor = [UIColor blackColor]; 127 | } 128 | #pragma mark - 懒加载 129 | 130 | /// 缩略图 131 | - (MYZGestureShapeView *)shapeView { 132 | if (!_shapeView) { 133 | CGFloat shapX = (ScreenWidth - ShapeWH) * 0.5; 134 | _shapeView = [[MYZGestureShapeView alloc] initWithFrame:CGRectMake(shapX, MarginTop, ShapeWH, ShapeWH)]; 135 | _shapeView.backgroundColor = [UIColor clearColor]; 136 | } 137 | return _shapeView; 138 | } 139 | 140 | /// 手势九宫格 141 | - (MYZGestureView *)gestureView { 142 | if (!_gestureView) { 143 | CGFloat gestureViewX = (ScreenWidth - GestureWH) * 0.5; 144 | CGFloat gestureViewY = MarginTop * 2.0 + Margin + ShapeWH + LabelHeight; 145 | _gestureView = [[MYZGestureView alloc] initWithFrame:CGRectMake(gestureViewX, gestureViewY, GestureWH, GestureWH)]; 146 | } 147 | return _gestureView; 148 | } 149 | 150 | - (UILabel *)messageLabel { 151 | if (!_messageLabel) { 152 | CGFloat labelY = MarginTop + ShapeWH + Margin; 153 | _messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, labelY, ScreenWidth, LabelHeight)]; 154 | _messageLabel.textAlignment = NSTextAlignmentCenter; 155 | _messageLabel.font = [UIFont systemFontOfSize:16.0]; 156 | _messageLabel.text = promptDefaultMessage; 157 | } 158 | return _messageLabel; 159 | } 160 | 161 | // 底部按钮 162 | - (UIButton *)bottomButton { 163 | if (!_bottomButton) { 164 | CGFloat posY = SCViewHeight - BottomHeight * 1.5; 165 | _bottomButton = [UIButton buttonWithType:UIButtonTypeCustom]; 166 | _bottomButton.backgroundColor = [UIColor clearColor]; 167 | _bottomButton.frame = CGRectMake(0.0, posY, ScreenWidth, BottomHeight); 168 | _bottomButton.titleLabel.font = [UIFont systemFontOfSize:14.0]; 169 | [_bottomButton setTitleColor:UIColorFrom16RGB(0x666666, 1.0) forState:UIControlStateNormal]; 170 | [_bottomButton setTitle:@"验证登录密码" forState:UIControlStateNormal]; 171 | [_bottomButton addTarget:self action:@selector(bottomButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 172 | } 173 | return _bottomButton; 174 | } 175 | 176 | #pragma mark - 对外方法 177 | /// 设置手势完成的回调 178 | - (void)gestureSetComplete:(SCGestureSetBlock)block { 179 | self.block = block; 180 | } 181 | 182 | #pragma mark - action事件 183 | - (void)back { 184 | // 点击返回设置失败 185 | if (self.block) { 186 | self.block(NO); 187 | } 188 | [self popViewController]; 189 | } 190 | 191 | - (void)bottomButtonAction:(UIButton *)sender { 192 | // 验证登录密码 193 | } 194 | 195 | #pragma mark - 对内方法 196 | // 重设手势 197 | - (void)resetGesture { 198 | [self.navigationItem.rightBarButtonItem setEnabled:NO]; 199 | self.shapeView.gestureCode = nil; 200 | self.firstGestureCode = nil; 201 | self.messageLabel.text = promptDefaultMessage; 202 | self.messageLabel.textColor = [UIColor blackColor]; 203 | self.bottomButton.hidden = YES; 204 | } 205 | 206 | - (void)popViewController { 207 | [self.navigationController popViewControllerAnimated:YES]; 208 | } 209 | 210 | // 手势密码结果的处理 211 | - (BOOL)gestureResult:(NSString *)gestureCode { 212 | 213 | if (self.gestureSetType == SCGestureSetTypeSetting) { 214 | // 首次设置手势密码 215 | 216 | if (gestureCode.length >= 4) { 217 | if (!self.firstGestureCode) { 218 | self.shapeView.gestureCode = gestureCode; 219 | self.firstGestureCode = gestureCode; 220 | self.messageLabel.text = promptSetAgainMessage; 221 | //导航栏上的重置按钮 222 | [self.navigationItem.rightBarButtonItem setEnabled:YES]; 223 | 224 | return YES; 225 | } else if ([self.firstGestureCode isEqualToString:gestureCode]) { 226 | // 第二次绘制成功,返回上一页 227 | [SCSecureHelper saveGestureCodeKey:gestureCode]; 228 | if (self.block) { 229 | self.block(YES); 230 | } 231 | [self popViewController]; 232 | return YES; 233 | } 234 | } 235 | // 点数少于4 或者 前后不一致 236 | if (gestureCode.length < 4 || self.firstGestureCode != nil) { 237 | // 点数少于4 或者 前后不一致 238 | self.messageLabel.text = gestureCode.length < 4 ? promptPointShortMessage : promptSetAgainErrorMessage; 239 | self.messageLabel.textColor = SCCircleErrorColor; 240 | [self.messageLabel.layer shake]; 241 | return NO; 242 | } 243 | 244 | } else if (self.gestureSetType == SCGestureSetTypeDelete) { 245 | // 关闭手势密码 246 | if ([self verifyGestureCodeWitCode:gestureCode]) { 247 | //密码验证成功,回调关闭你手势密码 248 | [SCSecureHelper saveGestureCodeKey:nil]; 249 | if (self.block) { 250 | self.block(YES); 251 | } 252 | [self popViewController]; 253 | 254 | return YES; 255 | } 256 | return NO; 257 | 258 | } else if (self.gestureSetType == SCGestureSetTypeChange) { 259 | // 修改手势密码,修改前先验证原密码 260 | if ([self verifyGestureCodeWitCode:gestureCode]) { 261 | [self resetTopViews]; 262 | return YES; 263 | } 264 | return NO; 265 | } 266 | 267 | return NO; 268 | } 269 | 270 | 271 | // 验证原密码 272 | - (BOOL)verifyGestureCodeWitCode:(NSString *)gestureCode { 273 | NSString * saveGestureCode = [SCSecureHelper gainGestureCodeKey]; 274 | if ([gestureCode isEqualToString:saveGestureCode]) { 275 | return YES; 276 | } else { 277 | self.messageLabel.text = promptPasswordErrorMessage; 278 | self.messageLabel.textColor = SCCircleErrorColor; 279 | [self.messageLabel.layer shake]; 280 | return NO; 281 | } 282 | } 283 | 284 | - (void)dealloc { 285 | NSLog(@"%@===dealloc",NSStringFromClass([self class])); 286 | } 287 | 288 | @end 289 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCTouchIDController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCTouchIDController.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 指纹密码 8 | 9 | #import 10 | 11 | @interface SCTouchIDController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCTouchIDController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCTouchIDController.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 指纹密码 8 | 9 | #import "SCTouchIDController.h" 10 | #import "SCSecureHelper.h" 11 | #import "SCSwitchCell.h" 12 | 13 | @interface SCTouchIDController () 14 | 15 | @property (weak, nonatomic) IBOutlet UITableView *tableView; 16 | 17 | @end 18 | 19 | @implementation SCTouchIDController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | self.title = @"指纹密码"; 24 | [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([SCSwitchCell class]) bundle:nil] 25 | forCellReuseIdentifier:NSStringFromClass([SCSwitchCell class])]; 26 | } 27 | 28 | - (void)didReceiveMemoryWarning { 29 | [super didReceiveMemoryWarning]; 30 | } 31 | 32 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 33 | return 1; 34 | } 35 | 36 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { 37 | 38 | return CGFLOAT_MIN; 39 | } 40 | 41 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 42 | return CGFLOAT_MIN; 43 | } 44 | 45 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 46 | return 44.0; 47 | } 48 | 49 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 50 | 51 | SCSwitchCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([SCSwitchCell class]) forIndexPath:indexPath]; 52 | cell.titleLabel.text = @"指纹解锁"; 53 | kWeakSelf 54 | [cell setupSwitchOn:[SCSecureHelper touchIDOpenStatus] switchBlock:^(UISwitch *switchButton) { 55 | [weakSelf switchButtonAction:switchButton]; 56 | }]; 57 | return cell; 58 | } 59 | 60 | // 开启/关闭指纹识别的操作 61 | - (void)switchButtonAction:(UISwitch *)sender { 62 | __weak UISwitch *weakSwitch = sender; 63 | kWeakSelf 64 | [[SCSecureHelper shareInstance] openTouchID:^(BOOL success, BOOL inputPassword, NSString *message) { 65 | 66 | if (inputPassword && !success) { 67 | // inputPassword是yes的话,调用登录页面,进行输入登录密码进行验证,相当于退出了登录,重新登录 68 | [weakSelf changeTouchIDByPassword:sender]; 69 | return ; 70 | } 71 | // 如果其他结果 72 | [weakSelf showSwitchOnStatus:weakSwitch handleSuccess:success]; 73 | }]; 74 | } 75 | 76 | // 验证登录密码,来改变指纹识别的开闭 77 | // 跳转到登录页面,登录成功的block回调里面处理,和指纹识别类似的判断 78 | - (void)changeTouchIDByPassword:(UISwitch *)sender { 79 | 80 | // 弱引用 81 | //kWeakSelf 82 | 83 | // 以下代码是在登录结果的block里进行的处理 84 | BOOL success = YES; 85 | [self showSwitchOnStatus:sender handleSuccess:success]; 86 | } 87 | 88 | /// handleResult 指纹识别/登录成功的操作结果 success是操作结果yes成功,no 失败 89 | - (void)showSwitchOnStatus:(UISwitch *)sender handleSuccess:(BOOL)success { 90 | BOOL isOpen = [SCSecureHelper touchIDOpenStatus]; 91 | if (isOpen) { 92 | // isOpen是YES,代表之前是开启状态 93 | if (success) { 94 | // 之前是开启状态,验证成功后就是关闭状态 95 | [SVProgressHUD showSuccessWithStatus:@"指纹验证已关闭"]; 96 | [SCSecureHelper touchIDOpen:NO]; 97 | [sender setOn:NO animated:YES]; 98 | } else { 99 | // 之前是开启状态,验证失败后仍是开启状态 100 | [sender setOn:YES animated:YES]; 101 | } 102 | } else { 103 | // isOpen是NO,代表之前是关闭状态 104 | if (success) { 105 | // 之前是关闭状态,验证成功后就是开启状态 106 | [SVProgressHUD showSuccessWithStatus:@"指纹验证开启成功"]; 107 | [SCSecureHelper touchIDOpen:YES]; 108 | [sender setOn:YES animated:YES]; 109 | } else { 110 | // 之前是关闭状态,验证失败后仍是关闭状态 111 | [sender setOn:NO animated:YES]; 112 | } 113 | } 114 | } 115 | 116 | - (void)dealloc { 117 | NSLog(@"%@===dealloc",NSStringFromClass([self class])); 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /SCTouchDemo/ViewControllers/SCTouchIDController.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCMainCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCMainCell.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 安全设置主页的cell 8 | 9 | #import 10 | 11 | @interface SCMainCell : UITableViewCell 12 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 13 | @property (weak, nonatomic) IBOutlet UILabel *contentLabel; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCMainCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCMainCell.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import "SCMainCell.h" 10 | 11 | @implementation SCMainCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCMainCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCNormalCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCNormalCell.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/20. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCNormalCell : UITableViewCell 12 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCNormalCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCNormalCell.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/20. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import "SCNormalCell.h" 10 | 11 | @implementation SCNormalCell 12 | 13 | - (void)awakeFromNib { 14 | [super awakeFromNib]; 15 | // Initialization code 16 | } 17 | 18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 19 | [super setSelected:selected animated:animated]; 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCNormalCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCSwitchCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // SCSwitchCell.h 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 有UISwitch按钮的cell 8 | 9 | #import 10 | 11 | typedef void(^SCSwitchBlock)(UISwitch *switchButton); 12 | 13 | @interface SCSwitchCell : UITableViewCell 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *titleLabel; 16 | 17 | - (void)setupSwitchOn:(BOOL)isOn switchBlock:(SCSwitchBlock)block; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCSwitchCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCSwitchCell.m 3 | // SCTouchDemo 4 | // 5 | // Created by 小广 on 2016/12/19. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import "SCSwitchCell.h" 10 | 11 | @interface SCSwitchCell () 12 | @property (weak, nonatomic) IBOutlet UISwitch *switchButton; 13 | 14 | @property (nonatomic, copy) SCSwitchBlock block; 15 | @end 16 | 17 | @implementation SCSwitchCell 18 | - (void)awakeFromNib { 19 | [super awakeFromNib]; 20 | // Initialization code 21 | } 22 | 23 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 24 | [super setSelected:selected animated:animated]; 25 | } 26 | 27 | - (void)setupSwitchOn:(BOOL)isOn switchBlock:(SCSwitchBlock)block { 28 | [self.switchButton setOn:isOn animated:YES]; 29 | self.block = block; 30 | } 31 | 32 | - (IBAction)switchAction:(UISwitch *)sender { 33 | if (self.block) { 34 | self.block(sender); 35 | } 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /SCTouchDemo/Views/SCSwitchCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /SCTouchDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | -------------------------------------------------------------------------------- /SCTouchDemoTests/SCTouchDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCTouchDemoTests.m 3 | // SCTouchDemoTests 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCTouchDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation SCTouchDemoTests 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 | -------------------------------------------------------------------------------- /SCTouchDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | -------------------------------------------------------------------------------- /SCTouchDemoUITests/SCTouchDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SCTouchDemoUITests.m 3 | // SCTouchDemoUITests 4 | // 5 | // Created by 小广 on 2016/12/15. 6 | // Copyright © 2016年 小广. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SCTouchDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation SCTouchDemoUITests 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 | -------------------------------------------------------------------------------- /images/develop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XGPASS/XGTouchDemo/b602dd7103e233d4d09effbfd284a4a0ceb43765/images/develop.gif --------------------------------------------------------------------------------