├── 2.gif ├── README.md ├── yanZhengCode.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── wangshuanglong.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── wangshuanglong.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ └── yanZhengCode.xcscheme ├── yanZhengCode ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CaptchaView.h ├── CaptchaView.m ├── Info.plist ├── NHGraphCoder.h ├── NHGraphCoder.m ├── Resources │ └── 4.JPG ├── ViewController.h ├── ViewController.m ├── main.m └── vendor │ ├── FBShimmering │ ├── FBShimmering-Prefix.pch │ ├── FBShimmering.h │ ├── FBShimmeringLayer.h │ ├── FBShimmeringLayer.m │ ├── FBShimmeringView.h │ └── FBShimmeringView.m │ └── JGAFImageCache │ ├── JGAFImageCache.h │ └── JGAFImageCache.m ├── yanZhengCodeTests ├── Info.plist └── yanZhengCodeTests.m └── yanZhengCodeUITests ├── Info.plist └── yanZhengCodeUITests.m /2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/YanZhengCode/75b6fc24af7f9bc12ba3a5b22b59a27cdee3ad39/2.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yanZhengCode 2 | 图片验证码和滑块验证码 3 | 4 | 在此要感谢 hu jiaju 和 jerehedu。 5 | 6 | ![效果图.gif](http://upload-images.jianshu.io/upload_images/1708447-05f1d0bb9dd11577.gif?imageMogr2/auto-orient/strip) 7 | 8 | ![极验验证码.gif](http://upload-images.jianshu.io/upload_images/1708447-70da231f930ee53d.gif?imageMogr2/auto-orient/strip) 9 | -------------------------------------------------------------------------------- /yanZhengCode.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 31782EBD1D517CF900B7411D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782EBC1D517CF900B7411D /* main.m */; }; 11 | 31782EC01D517CF900B7411D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782EBF1D517CF900B7411D /* AppDelegate.m */; }; 12 | 31782EC31D517CF900B7411D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782EC21D517CF900B7411D /* ViewController.m */; }; 13 | 31782EC61D517CF900B7411D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 31782EC41D517CF900B7411D /* Main.storyboard */; }; 14 | 31782EC81D517CF900B7411D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 31782EC71D517CF900B7411D /* Assets.xcassets */; }; 15 | 31782ECB1D517CF900B7411D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 31782EC91D517CF900B7411D /* LaunchScreen.storyboard */; }; 16 | 31782ED61D517CF900B7411D /* yanZhengCodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782ED51D517CF900B7411D /* yanZhengCodeTests.m */; }; 17 | 31782EE11D517CF900B7411D /* yanZhengCodeUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782EE01D517CF900B7411D /* yanZhengCodeUITests.m */; }; 18 | 31782EF01D5189DC00B7411D /* CaptchaView.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782EEF1D5189DC00B7411D /* CaptchaView.m */; }; 19 | 31782F021D51916100B7411D /* NHGraphCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782F011D51916100B7411D /* NHGraphCoder.m */; }; 20 | 31782F0E1D51916C00B7411D /* FBShimmeringLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782F081D51916C00B7411D /* FBShimmeringLayer.m */; }; 21 | 31782F0F1D51916C00B7411D /* FBShimmeringView.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782F0A1D51916C00B7411D /* FBShimmeringView.m */; }; 22 | 31782F101D51916C00B7411D /* JGAFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 31782F0D1D51916C00B7411D /* JGAFImageCache.m */; }; 23 | 31782F121D51943200B7411D /* 4.JPG in Resources */ = {isa = PBXBuildFile; fileRef = 31782F111D51943200B7411D /* 4.JPG */; }; 24 | 31782F141D5197B900B7411D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 31782F131D5197B900B7411D /* CoreGraphics.framework */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 31782ED21D517CF900B7411D /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 31782EB01D517CF900B7411D /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 31782EB71D517CF900B7411D; 33 | remoteInfo = yanZhengCode; 34 | }; 35 | 31782EDD1D517CF900B7411D /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 31782EB01D517CF900B7411D /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 31782EB71D517CF900B7411D; 40 | remoteInfo = yanZhengCode; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 31782EB81D517CF900B7411D /* yanZhengCode.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = yanZhengCode.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 31782EBC1D517CF900B7411D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 31782EBE1D517CF900B7411D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 31782EBF1D517CF900B7411D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 31782EC11D517CF900B7411D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 50 | 31782EC21D517CF900B7411D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 51 | 31782EC51D517CF900B7411D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 52 | 31782EC71D517CF900B7411D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53 | 31782ECA1D517CF900B7411D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | 31782ECC1D517CF900B7411D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 31782ED11D517CF900B7411D /* yanZhengCodeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = yanZhengCodeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 31782ED51D517CF900B7411D /* yanZhengCodeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = yanZhengCodeTests.m; sourceTree = ""; }; 57 | 31782ED71D517CF900B7411D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 31782EDC1D517CF900B7411D /* yanZhengCodeUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = yanZhengCodeUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 31782EE01D517CF900B7411D /* yanZhengCodeUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = yanZhengCodeUITests.m; sourceTree = ""; }; 60 | 31782EE21D517CF900B7411D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 31782EEE1D5189DC00B7411D /* CaptchaView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaptchaView.h; sourceTree = ""; }; 62 | 31782EEF1D5189DC00B7411D /* CaptchaView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CaptchaView.m; sourceTree = ""; }; 63 | 31782F001D51916100B7411D /* NHGraphCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NHGraphCoder.h; sourceTree = ""; }; 64 | 31782F011D51916100B7411D /* NHGraphCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NHGraphCoder.m; sourceTree = ""; }; 65 | 31782F051D51916C00B7411D /* FBShimmering-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBShimmering-Prefix.pch"; sourceTree = ""; }; 66 | 31782F061D51916C00B7411D /* FBShimmering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmering.h; sourceTree = ""; }; 67 | 31782F071D51916C00B7411D /* FBShimmeringLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmeringLayer.h; sourceTree = ""; }; 68 | 31782F081D51916C00B7411D /* FBShimmeringLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBShimmeringLayer.m; sourceTree = ""; }; 69 | 31782F091D51916C00B7411D /* FBShimmeringView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmeringView.h; sourceTree = ""; }; 70 | 31782F0A1D51916C00B7411D /* FBShimmeringView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBShimmeringView.m; sourceTree = ""; }; 71 | 31782F0C1D51916C00B7411D /* JGAFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGAFImageCache.h; sourceTree = ""; }; 72 | 31782F0D1D51916C00B7411D /* JGAFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGAFImageCache.m; sourceTree = ""; }; 73 | 31782F111D51943200B7411D /* 4.JPG */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = 4.JPG; sourceTree = ""; }; 74 | 31782F131D5197B900B7411D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 31782EB51D517CF900B7411D /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 31782F141D5197B900B7411D /* CoreGraphics.framework in Frameworks */, 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 31782ECE1D517CF900B7411D /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | 31782ED91D517CF900B7411D /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | /* End PBXFrameworksBuildPhase section */ 101 | 102 | /* Begin PBXGroup section */ 103 | 31782EAF1D517CF900B7411D = { 104 | isa = PBXGroup; 105 | children = ( 106 | 31782F131D5197B900B7411D /* CoreGraphics.framework */, 107 | 31782EBA1D517CF900B7411D /* yanZhengCode */, 108 | 31782ED41D517CF900B7411D /* yanZhengCodeTests */, 109 | 31782EDF1D517CF900B7411D /* yanZhengCodeUITests */, 110 | 31782EB91D517CF900B7411D /* Products */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | 31782EB91D517CF900B7411D /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 31782EB81D517CF900B7411D /* yanZhengCode.app */, 118 | 31782ED11D517CF900B7411D /* yanZhengCodeTests.xctest */, 119 | 31782EDC1D517CF900B7411D /* yanZhengCodeUITests.xctest */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | 31782EBA1D517CF900B7411D /* yanZhengCode */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 31782F031D51916C00B7411D /* vendor */, 128 | 31782F001D51916100B7411D /* NHGraphCoder.h */, 129 | 31782F011D51916100B7411D /* NHGraphCoder.m */, 130 | 31782EF11D51914F00B7411D /* Resources */, 131 | 31782EBE1D517CF900B7411D /* AppDelegate.h */, 132 | 31782EBF1D517CF900B7411D /* AppDelegate.m */, 133 | 31782EEE1D5189DC00B7411D /* CaptchaView.h */, 134 | 31782EEF1D5189DC00B7411D /* CaptchaView.m */, 135 | 31782EC11D517CF900B7411D /* ViewController.h */, 136 | 31782EC21D517CF900B7411D /* ViewController.m */, 137 | 31782EC41D517CF900B7411D /* Main.storyboard */, 138 | 31782EC71D517CF900B7411D /* Assets.xcassets */, 139 | 31782EC91D517CF900B7411D /* LaunchScreen.storyboard */, 140 | 31782ECC1D517CF900B7411D /* Info.plist */, 141 | 31782EBB1D517CF900B7411D /* Supporting Files */, 142 | ); 143 | path = yanZhengCode; 144 | sourceTree = ""; 145 | }; 146 | 31782EBB1D517CF900B7411D /* Supporting Files */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 31782EBC1D517CF900B7411D /* main.m */, 150 | ); 151 | name = "Supporting Files"; 152 | sourceTree = ""; 153 | }; 154 | 31782ED41D517CF900B7411D /* yanZhengCodeTests */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 31782ED51D517CF900B7411D /* yanZhengCodeTests.m */, 158 | 31782ED71D517CF900B7411D /* Info.plist */, 159 | ); 160 | path = yanZhengCodeTests; 161 | sourceTree = ""; 162 | }; 163 | 31782EDF1D517CF900B7411D /* yanZhengCodeUITests */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 31782EE01D517CF900B7411D /* yanZhengCodeUITests.m */, 167 | 31782EE21D517CF900B7411D /* Info.plist */, 168 | ); 169 | path = yanZhengCodeUITests; 170 | sourceTree = ""; 171 | }; 172 | 31782EF11D51914F00B7411D /* Resources */ = { 173 | isa = PBXGroup; 174 | children = ( 175 | 31782F111D51943200B7411D /* 4.JPG */, 176 | ); 177 | path = Resources; 178 | sourceTree = ""; 179 | }; 180 | 31782F031D51916C00B7411D /* vendor */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | 31782F041D51916C00B7411D /* FBShimmering */, 184 | 31782F0B1D51916C00B7411D /* JGAFImageCache */, 185 | ); 186 | path = vendor; 187 | sourceTree = ""; 188 | }; 189 | 31782F041D51916C00B7411D /* FBShimmering */ = { 190 | isa = PBXGroup; 191 | children = ( 192 | 31782F051D51916C00B7411D /* FBShimmering-Prefix.pch */, 193 | 31782F061D51916C00B7411D /* FBShimmering.h */, 194 | 31782F071D51916C00B7411D /* FBShimmeringLayer.h */, 195 | 31782F081D51916C00B7411D /* FBShimmeringLayer.m */, 196 | 31782F091D51916C00B7411D /* FBShimmeringView.h */, 197 | 31782F0A1D51916C00B7411D /* FBShimmeringView.m */, 198 | ); 199 | path = FBShimmering; 200 | sourceTree = ""; 201 | }; 202 | 31782F0B1D51916C00B7411D /* JGAFImageCache */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 31782F0C1D51916C00B7411D /* JGAFImageCache.h */, 206 | 31782F0D1D51916C00B7411D /* JGAFImageCache.m */, 207 | ); 208 | path = JGAFImageCache; 209 | sourceTree = ""; 210 | }; 211 | /* End PBXGroup section */ 212 | 213 | /* Begin PBXNativeTarget section */ 214 | 31782EB71D517CF900B7411D /* yanZhengCode */ = { 215 | isa = PBXNativeTarget; 216 | buildConfigurationList = 31782EE51D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCode" */; 217 | buildPhases = ( 218 | 31782EB41D517CF900B7411D /* Sources */, 219 | 31782EB51D517CF900B7411D /* Frameworks */, 220 | 31782EB61D517CF900B7411D /* Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | ); 226 | name = yanZhengCode; 227 | productName = yanZhengCode; 228 | productReference = 31782EB81D517CF900B7411D /* yanZhengCode.app */; 229 | productType = "com.apple.product-type.application"; 230 | }; 231 | 31782ED01D517CF900B7411D /* yanZhengCodeTests */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 31782EE81D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCodeTests" */; 234 | buildPhases = ( 235 | 31782ECD1D517CF900B7411D /* Sources */, 236 | 31782ECE1D517CF900B7411D /* Frameworks */, 237 | 31782ECF1D517CF900B7411D /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | 31782ED31D517CF900B7411D /* PBXTargetDependency */, 243 | ); 244 | name = yanZhengCodeTests; 245 | productName = yanZhengCodeTests; 246 | productReference = 31782ED11D517CF900B7411D /* yanZhengCodeTests.xctest */; 247 | productType = "com.apple.product-type.bundle.unit-test"; 248 | }; 249 | 31782EDB1D517CF900B7411D /* yanZhengCodeUITests */ = { 250 | isa = PBXNativeTarget; 251 | buildConfigurationList = 31782EEB1D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCodeUITests" */; 252 | buildPhases = ( 253 | 31782ED81D517CF900B7411D /* Sources */, 254 | 31782ED91D517CF900B7411D /* Frameworks */, 255 | 31782EDA1D517CF900B7411D /* Resources */, 256 | ); 257 | buildRules = ( 258 | ); 259 | dependencies = ( 260 | 31782EDE1D517CF900B7411D /* PBXTargetDependency */, 261 | ); 262 | name = yanZhengCodeUITests; 263 | productName = yanZhengCodeUITests; 264 | productReference = 31782EDC1D517CF900B7411D /* yanZhengCodeUITests.xctest */; 265 | productType = "com.apple.product-type.bundle.ui-testing"; 266 | }; 267 | /* End PBXNativeTarget section */ 268 | 269 | /* Begin PBXProject section */ 270 | 31782EB01D517CF900B7411D /* Project object */ = { 271 | isa = PBXProject; 272 | attributes = { 273 | LastUpgradeCheck = 0730; 274 | ORGANIZATIONNAME = "王双龙"; 275 | TargetAttributes = { 276 | 31782EB71D517CF900B7411D = { 277 | CreatedOnToolsVersion = 7.3; 278 | }; 279 | 31782ED01D517CF900B7411D = { 280 | CreatedOnToolsVersion = 7.3; 281 | TestTargetID = 31782EB71D517CF900B7411D; 282 | }; 283 | 31782EDB1D517CF900B7411D = { 284 | CreatedOnToolsVersion = 7.3; 285 | TestTargetID = 31782EB71D517CF900B7411D; 286 | }; 287 | }; 288 | }; 289 | buildConfigurationList = 31782EB31D517CF900B7411D /* Build configuration list for PBXProject "yanZhengCode" */; 290 | compatibilityVersion = "Xcode 3.2"; 291 | developmentRegion = English; 292 | hasScannedForEncodings = 0; 293 | knownRegions = ( 294 | en, 295 | Base, 296 | ); 297 | mainGroup = 31782EAF1D517CF900B7411D; 298 | productRefGroup = 31782EB91D517CF900B7411D /* Products */; 299 | projectDirPath = ""; 300 | projectRoot = ""; 301 | targets = ( 302 | 31782EB71D517CF900B7411D /* yanZhengCode */, 303 | 31782ED01D517CF900B7411D /* yanZhengCodeTests */, 304 | 31782EDB1D517CF900B7411D /* yanZhengCodeUITests */, 305 | ); 306 | }; 307 | /* End PBXProject section */ 308 | 309 | /* Begin PBXResourcesBuildPhase section */ 310 | 31782EB61D517CF900B7411D /* Resources */ = { 311 | isa = PBXResourcesBuildPhase; 312 | buildActionMask = 2147483647; 313 | files = ( 314 | 31782F121D51943200B7411D /* 4.JPG in Resources */, 315 | 31782ECB1D517CF900B7411D /* LaunchScreen.storyboard in Resources */, 316 | 31782EC81D517CF900B7411D /* Assets.xcassets in Resources */, 317 | 31782EC61D517CF900B7411D /* Main.storyboard in Resources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 31782ECF1D517CF900B7411D /* Resources */ = { 322 | isa = PBXResourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 31782EDA1D517CF900B7411D /* Resources */ = { 329 | isa = PBXResourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | }; 335 | /* End PBXResourcesBuildPhase section */ 336 | 337 | /* Begin PBXSourcesBuildPhase section */ 338 | 31782EB41D517CF900B7411D /* Sources */ = { 339 | isa = PBXSourcesBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | 31782F0E1D51916C00B7411D /* FBShimmeringLayer.m in Sources */, 343 | 31782EC31D517CF900B7411D /* ViewController.m in Sources */, 344 | 31782F101D51916C00B7411D /* JGAFImageCache.m in Sources */, 345 | 31782EC01D517CF900B7411D /* AppDelegate.m in Sources */, 346 | 31782F0F1D51916C00B7411D /* FBShimmeringView.m in Sources */, 347 | 31782F021D51916100B7411D /* NHGraphCoder.m in Sources */, 348 | 31782EBD1D517CF900B7411D /* main.m in Sources */, 349 | 31782EF01D5189DC00B7411D /* CaptchaView.m in Sources */, 350 | ); 351 | runOnlyForDeploymentPostprocessing = 0; 352 | }; 353 | 31782ECD1D517CF900B7411D /* Sources */ = { 354 | isa = PBXSourcesBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | 31782ED61D517CF900B7411D /* yanZhengCodeTests.m in Sources */, 358 | ); 359 | runOnlyForDeploymentPostprocessing = 0; 360 | }; 361 | 31782ED81D517CF900B7411D /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 31782EE11D517CF900B7411D /* yanZhengCodeUITests.m in Sources */, 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | }; 369 | /* End PBXSourcesBuildPhase section */ 370 | 371 | /* Begin PBXTargetDependency section */ 372 | 31782ED31D517CF900B7411D /* PBXTargetDependency */ = { 373 | isa = PBXTargetDependency; 374 | target = 31782EB71D517CF900B7411D /* yanZhengCode */; 375 | targetProxy = 31782ED21D517CF900B7411D /* PBXContainerItemProxy */; 376 | }; 377 | 31782EDE1D517CF900B7411D /* PBXTargetDependency */ = { 378 | isa = PBXTargetDependency; 379 | target = 31782EB71D517CF900B7411D /* yanZhengCode */; 380 | targetProxy = 31782EDD1D517CF900B7411D /* PBXContainerItemProxy */; 381 | }; 382 | /* End PBXTargetDependency section */ 383 | 384 | /* Begin PBXVariantGroup section */ 385 | 31782EC41D517CF900B7411D /* Main.storyboard */ = { 386 | isa = PBXVariantGroup; 387 | children = ( 388 | 31782EC51D517CF900B7411D /* Base */, 389 | ); 390 | name = Main.storyboard; 391 | sourceTree = ""; 392 | }; 393 | 31782EC91D517CF900B7411D /* LaunchScreen.storyboard */ = { 394 | isa = PBXVariantGroup; 395 | children = ( 396 | 31782ECA1D517CF900B7411D /* Base */, 397 | ); 398 | name = LaunchScreen.storyboard; 399 | sourceTree = ""; 400 | }; 401 | /* End PBXVariantGroup section */ 402 | 403 | /* Begin XCBuildConfiguration section */ 404 | 31782EE31D517CF900B7411D /* Debug */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ALWAYS_SEARCH_USER_PATHS = NO; 408 | CLANG_ANALYZER_NONNULL = YES; 409 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 410 | CLANG_CXX_LIBRARY = "libc++"; 411 | CLANG_ENABLE_MODULES = YES; 412 | CLANG_ENABLE_OBJC_ARC = YES; 413 | CLANG_WARN_BOOL_CONVERSION = YES; 414 | CLANG_WARN_CONSTANT_CONVERSION = YES; 415 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 416 | CLANG_WARN_EMPTY_BODY = YES; 417 | CLANG_WARN_ENUM_CONVERSION = YES; 418 | CLANG_WARN_INT_CONVERSION = YES; 419 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 420 | CLANG_WARN_UNREACHABLE_CODE = YES; 421 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 422 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 423 | COPY_PHASE_STRIP = NO; 424 | DEBUG_INFORMATION_FORMAT = dwarf; 425 | ENABLE_STRICT_OBJC_MSGSEND = YES; 426 | ENABLE_TESTABILITY = YES; 427 | GCC_C_LANGUAGE_STANDARD = gnu99; 428 | GCC_DYNAMIC_NO_PIC = NO; 429 | GCC_NO_COMMON_BLOCKS = YES; 430 | GCC_OPTIMIZATION_LEVEL = 0; 431 | GCC_PREPROCESSOR_DEFINITIONS = ( 432 | "DEBUG=1", 433 | "$(inherited)", 434 | ); 435 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 436 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 437 | GCC_WARN_UNDECLARED_SELECTOR = YES; 438 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 439 | GCC_WARN_UNUSED_FUNCTION = YES; 440 | GCC_WARN_UNUSED_VARIABLE = YES; 441 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 442 | MTL_ENABLE_DEBUG_INFO = YES; 443 | ONLY_ACTIVE_ARCH = YES; 444 | SDKROOT = iphoneos; 445 | TARGETED_DEVICE_FAMILY = "1,2"; 446 | }; 447 | name = Debug; 448 | }; 449 | 31782EE41D517CF900B7411D /* Release */ = { 450 | isa = XCBuildConfiguration; 451 | buildSettings = { 452 | ALWAYS_SEARCH_USER_PATHS = NO; 453 | CLANG_ANALYZER_NONNULL = YES; 454 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 455 | CLANG_CXX_LIBRARY = "libc++"; 456 | CLANG_ENABLE_MODULES = YES; 457 | CLANG_ENABLE_OBJC_ARC = YES; 458 | CLANG_WARN_BOOL_CONVERSION = YES; 459 | CLANG_WARN_CONSTANT_CONVERSION = YES; 460 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 461 | CLANG_WARN_EMPTY_BODY = YES; 462 | CLANG_WARN_ENUM_CONVERSION = YES; 463 | CLANG_WARN_INT_CONVERSION = YES; 464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 465 | CLANG_WARN_UNREACHABLE_CODE = YES; 466 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 467 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 468 | COPY_PHASE_STRIP = NO; 469 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 470 | ENABLE_NS_ASSERTIONS = NO; 471 | ENABLE_STRICT_OBJC_MSGSEND = YES; 472 | GCC_C_LANGUAGE_STANDARD = gnu99; 473 | GCC_NO_COMMON_BLOCKS = YES; 474 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 475 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 476 | GCC_WARN_UNDECLARED_SELECTOR = YES; 477 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 478 | GCC_WARN_UNUSED_FUNCTION = YES; 479 | GCC_WARN_UNUSED_VARIABLE = YES; 480 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 481 | MTL_ENABLE_DEBUG_INFO = NO; 482 | SDKROOT = iphoneos; 483 | TARGETED_DEVICE_FAMILY = "1,2"; 484 | VALIDATE_PRODUCT = YES; 485 | }; 486 | name = Release; 487 | }; 488 | 31782EE61D517CF900B7411D /* Debug */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | INFOPLIST_FILE = yanZhengCode/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 494 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCode; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | }; 497 | name = Debug; 498 | }; 499 | 31782EE71D517CF900B7411D /* Release */ = { 500 | isa = XCBuildConfiguration; 501 | buildSettings = { 502 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 503 | INFOPLIST_FILE = yanZhengCode/Info.plist; 504 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 505 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCode; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | }; 508 | name = Release; 509 | }; 510 | 31782EE91D517CF900B7411D /* Debug */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | BUNDLE_LOADER = "$(TEST_HOST)"; 514 | INFOPLIST_FILE = yanZhengCodeTests/Info.plist; 515 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 516 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCodeTests; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/yanZhengCode.app/yanZhengCode"; 519 | }; 520 | name = Debug; 521 | }; 522 | 31782EEA1D517CF900B7411D /* Release */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | BUNDLE_LOADER = "$(TEST_HOST)"; 526 | INFOPLIST_FILE = yanZhengCodeTests/Info.plist; 527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 528 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCodeTests; 529 | PRODUCT_NAME = "$(TARGET_NAME)"; 530 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/yanZhengCode.app/yanZhengCode"; 531 | }; 532 | name = Release; 533 | }; 534 | 31782EEC1D517CF900B7411D /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | INFOPLIST_FILE = yanZhengCodeUITests/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 539 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCodeUITests; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | TEST_TARGET_NAME = yanZhengCode; 542 | }; 543 | name = Debug; 544 | }; 545 | 31782EED1D517CF900B7411D /* Release */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | INFOPLIST_FILE = yanZhengCodeUITests/Info.plist; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 550 | PRODUCT_BUNDLE_IDENTIFIER = com.chinaDaily.yanZhengCodeUITests; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | TEST_TARGET_NAME = yanZhengCode; 553 | }; 554 | name = Release; 555 | }; 556 | /* End XCBuildConfiguration section */ 557 | 558 | /* Begin XCConfigurationList section */ 559 | 31782EB31D517CF900B7411D /* Build configuration list for PBXProject "yanZhengCode" */ = { 560 | isa = XCConfigurationList; 561 | buildConfigurations = ( 562 | 31782EE31D517CF900B7411D /* Debug */, 563 | 31782EE41D517CF900B7411D /* Release */, 564 | ); 565 | defaultConfigurationIsVisible = 0; 566 | defaultConfigurationName = Release; 567 | }; 568 | 31782EE51D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCode" */ = { 569 | isa = XCConfigurationList; 570 | buildConfigurations = ( 571 | 31782EE61D517CF900B7411D /* Debug */, 572 | 31782EE71D517CF900B7411D /* Release */, 573 | ); 574 | defaultConfigurationIsVisible = 0; 575 | defaultConfigurationName = Release; 576 | }; 577 | 31782EE81D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCodeTests" */ = { 578 | isa = XCConfigurationList; 579 | buildConfigurations = ( 580 | 31782EE91D517CF900B7411D /* Debug */, 581 | 31782EEA1D517CF900B7411D /* Release */, 582 | ); 583 | defaultConfigurationIsVisible = 0; 584 | defaultConfigurationName = Release; 585 | }; 586 | 31782EEB1D517CF900B7411D /* Build configuration list for PBXNativeTarget "yanZhengCodeUITests" */ = { 587 | isa = XCConfigurationList; 588 | buildConfigurations = ( 589 | 31782EEC1D517CF900B7411D /* Debug */, 590 | 31782EED1D517CF900B7411D /* Release */, 591 | ); 592 | defaultConfigurationIsVisible = 0; 593 | defaultConfigurationName = Release; 594 | }; 595 | /* End XCConfigurationList section */ 596 | }; 597 | rootObject = 31782EB01D517CF900B7411D /* Project object */; 598 | } 599 | -------------------------------------------------------------------------------- /yanZhengCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /yanZhengCode.xcodeproj/project.xcworkspace/xcuserdata/wangshuanglong.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/YanZhengCode/75b6fc24af7f9bc12ba3a5b22b59a27cdee3ad39/yanZhengCode.xcodeproj/project.xcworkspace/xcuserdata/wangshuanglong.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /yanZhengCode.xcodeproj/xcuserdata/wangshuanglong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | yanZhengCode.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 31782EB71D517CF900B7411D 16 | 17 | primary 18 | 19 | 20 | 31782ED01D517CF900B7411D 21 | 22 | primary 23 | 24 | 25 | 31782EDB1D517CF900B7411D 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /yanZhengCode.xcodeproj/xcuserdata/wangshuanglong.xcuserdatad/xcschemes/yanZhengCode.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 | -------------------------------------------------------------------------------- /yanZhengCode/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 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 | -------------------------------------------------------------------------------- /yanZhengCode/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 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 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /yanZhengCode/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /yanZhengCode/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 | -------------------------------------------------------------------------------- /yanZhengCode/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 | -------------------------------------------------------------------------------- /yanZhengCode/CaptchaView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CaptchaView.h 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | DefaultType, 13 | CountType 14 | } IdentifyingCodeType; 15 | 16 | @interface CaptchaView : UIView 17 | 18 | @property (nonatomic, retain) NSArray *changeArray; //字符素材数组 19 | @property (nonatomic, retain) NSMutableString *changeString; //验证码的字符串 20 | 21 | @property (nonatomic,assign) IdentifyingCodeType CodeType;//验证码类型 22 | @property (nonatomic,assign) NSString * countString;//算术验证码的字符串 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame WithType:(IdentifyingCodeType)type; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /yanZhengCode/CaptchaView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CaptchaView.m 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import "CaptchaView.h" 10 | 11 | #define kRandomColor [UIColor colorWithRed:arc4random() % 256 / 256.0 green:arc4random() % 256 / 256.0 blue:arc4random() % 256 / 256.0 alpha:1.0]; 12 | #define kLineCount 6 13 | #define kLineWidth 1.0 14 | #define kCharCount 4 15 | #define kFontSize [UIFont systemFontOfSize:arc4random() % 8 + 15] 16 | 17 | @implementation CaptchaView 18 | 19 | - (instancetype)initWithFrame:(CGRect)frame WithType:(IdentifyingCodeType)type 20 | { 21 | if (self = [super initWithFrame:frame]) { 22 | 23 | self.layer.cornerRadius = 5.0; //设置layer圆角半径 24 | self.layer.masksToBounds = YES; //隐藏边界 25 | self.backgroundColor = kRandomColor; 26 | 27 | if (type == DefaultType) { 28 | 29 | [self changeCaptcha]; 30 | 31 | }else if (type == CountType){ 32 | 33 | [self changeCount]; 34 | } 35 | 36 | _CodeType = type; 37 | } 38 | 39 | return self; 40 | } 41 | #pragma mark 更换验证码,得到更换的验证码的字符串 42 | 43 | //字符串验证码 44 | -(void)changeCaptcha 45 | { 46 | //<一>从字符数组中随机抽取相应数量的字符,组成验证码字符串 47 | //数组中存放的是全部可选的字符,可以是字母,也可以是中文 48 | self.changeArray = [[NSArray alloc] initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K",@"L",@"M",@"N",@"O",@"P",@"Q",@"R",@"S",@"T",@"U",@"V",@"W",@"X",@"Y",@"Z",@"a",@"b",@"c",@"d",@"e",@"f",@"g",@"h",@"i",@"j",@"k",@"l",@"m",@"n",@"o",@"p",@"q",@"r",@"s",@"t",@"u",@"v",@"w",@"x",@"y",@"z",@"中",@"国",@"日",@"报",nil]; 49 | 50 | //如果能确定最大需要的容量,使用initWithCapacity:来设置,好处是当元素个数不超过容量时,添加元素不需要重新分配内存 51 | NSMutableString *getStr = [[NSMutableString alloc] initWithCapacity:kCharCount]; 52 | self.changeString = [[NSMutableString alloc] initWithCapacity:kCharCount]; 53 | 54 | //随机从数组中选取需要个数的字符,然后拼接为一个字符串 55 | for(int i = 0; i < kCharCount; i++) 56 | { 57 | NSInteger index = arc4random() % ([self.changeArray count] - 1); 58 | getStr = [self.changeArray objectAtIndex:index]; 59 | 60 | self.changeString = (NSMutableString *)[self.changeString stringByAppendingString:getStr]; 61 | } 62 | 63 | NSLog(@"%@",self.changeString); 64 | } 65 | 66 | //算数验证码 67 | - (void)changeCount{ 68 | 69 | self.changeString = [[NSMutableString alloc] initWithCapacity:kCharCount]; 70 | int i = arc4random() % 10; 71 | self.changeString = (NSMutableString *)[NSMutableString stringWithFormat:@"%d+",i]; 72 | int j = arc4random() %10; 73 | 74 | self.changeString = (NSMutableString *)[self.changeString stringByAppendingString:[NSString stringWithFormat:@"%d=",j]]; 75 | self.countString = [NSString stringWithFormat:@"%d",(i+ j)]; 76 | 77 | NSLog(@"%@",self.changeString); 78 | 79 | } 80 | 81 | #pragma mark 点击view时调用,因为当前类自身就是UIView,点击更换验证码可以直接写到这个方法中,不用再额外添加手势 82 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 83 | { 84 | //点击界面,切换验证码 85 | if (_CodeType == CountType) { 86 | [self changeCount]; 87 | }else if (_CodeType == DefaultType){ 88 | [self changeCaptcha]; 89 | } 90 | 91 | //setNeedsDisplay调用drawRect方法来实现view的绘制 92 | [self setNeedsDisplay]; 93 | } 94 | 95 | #pragma mark 绘制界面(1.UIView初始化后自动调用; 2.调用setNeedsDisplay方法时会自动调用) 96 | - (void)drawRect:(CGRect)rect { 97 | // 重写父类方法,首先要调用父类的方法 98 | [super drawRect:rect]; 99 | 100 | //设置随机背景颜色 101 | self.backgroundColor = kRandomColor; 102 | 103 | //获得要显示验证码字符串,根据长度,计算每个字符显示的大概位置 104 | NSString *text = [NSString stringWithFormat:@"%@",self.changeString]; 105 | CGSize cSize = [@"S" sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:20]}]; 106 | int width = rect.size.width / text.length - cSize.width; 107 | int height = rect.size.height - cSize.height; 108 | CGPoint point; 109 | 110 | //依次绘制每一个字符,可以设置显示的每个字符的字体大小、颜色、样式等 111 | float pX, pY; 112 | for (int i = 0; i < text.length; i++) 113 | { 114 | pX = arc4random() % width + rect.size.width / text.length * i; 115 | pY = arc4random() % height; 116 | point = CGPointMake(pX, pY); 117 | unichar c = [text characterAtIndex:i]; 118 | NSString *textC = [NSString stringWithFormat:@"%C", c]; 119 | 120 | [textC drawAtPoint:point withAttributes:@{NSFontAttributeName:kFontSize}]; 121 | } 122 | 123 | //调用drawRect:之前,系统会向栈中压入一个CGContextRef,调用UIGraphicsGetCurrentContext()会取栈顶的CGContextRef 124 | CGContextRef context = UIGraphicsGetCurrentContext(); 125 | //设置画线宽度 126 | CGContextSetLineWidth(context, kLineWidth); 127 | 128 | //绘制干扰的彩色直线 129 | for(int i = 0; i < kLineCount; i++) 130 | { 131 | //设置线的随机颜色 132 | UIColor *color = kRandomColor; 133 | CGContextSetStrokeColorWithColor(context, [color CGColor]); 134 | //设置线的起点 135 | pX = arc4random() % (int)rect.size.width; 136 | pY = arc4random() % (int)rect.size.height; 137 | CGContextMoveToPoint(context, pX, pY); 138 | //设置线终点 139 | pX = arc4random() % (int)rect.size.width; 140 | pY = arc4random() % (int)rect.size.height; 141 | CGContextAddLineToPoint(context, pX, pY); 142 | //画线 143 | CGContextStrokePath(context); 144 | } 145 | } 146 | 147 | 148 | /* 149 | // Only override drawRect: if you perform custom drawing. 150 | // An empty implementation adversely affects performance during animation. 151 | - (void)drawRect:(CGRect)rect { 152 | // Drawing code 153 | } 154 | */ 155 | 156 | @end 157 | -------------------------------------------------------------------------------- /yanZhengCode/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | CFBundleDevelopmentRegion 11 | en 12 | CFBundleExecutable 13 | $(EXECUTABLE_NAME) 14 | CFBundleIdentifier 15 | $(PRODUCT_BUNDLE_IDENTIFIER) 16 | CFBundleInfoDictionaryVersion 17 | 6.0 18 | CFBundleName 19 | $(PRODUCT_NAME) 20 | CFBundlePackageType 21 | APPL 22 | CFBundleShortVersionString 23 | 1.0 24 | CFBundleSignature 25 | ???? 26 | CFBundleVersion 27 | 1 28 | LSRequiresIPhoneOS 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /yanZhengCode/NHGraphCoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // NHGraphCoder.h 3 | // NHGraphCoderPro 4 | // 5 | // Created by hu jiaju on 16/5/30. 6 | // Copyright © 2016年 hu jiaju. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @class NHGraphCoder; 14 | typedef void(^NHGraphEvent)(NHGraphCoder *coder, BOOL success); 15 | typedef UIImage * _Nonnull (^NHRefreshEvent)(); 16 | 17 | @interface NHGraphCoder : UIView 18 | 19 | - (id)init NS_UNAVAILABLE; 20 | 21 | - (id)initWithFrame:(CGRect)frame NS_UNAVAILABLE; 22 | 23 | - (id)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE; 24 | 25 | /** 26 | * @brief generate graph code view 27 | * 28 | * @param img the bg image's instance 29 | * 30 | * @return the graphic code view 31 | */ 32 | + (NHGraphCoder *)codeWithImage:(UIImage *)img; 33 | 34 | /** 35 | * @brief generate graph code view 36 | * 37 | * @param url the bg image's url 38 | * @attention :TO BE Implementationed ! 39 | * 40 | * @return the graphic code view 41 | */ 42 | + (NHGraphCoder *)codeWithURL:(NSString *)url; 43 | 44 | /** 45 | * @brief reset state 46 | */ 47 | - (void)resetStateForDetect; 48 | 49 | /** 50 | * @brief deal with the event 51 | * 52 | * @param event the event 53 | */ 54 | - (void)handleGraphicCoderVerifyEvent:(NHGraphEvent)event; 55 | 56 | NS_ASSUME_NONNULL_END 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /yanZhengCode/NHGraphCoder.m: -------------------------------------------------------------------------------- 1 | // 2 | // NHGraphCoder.m 3 | // NHGraphCoderPro 4 | // 5 | // Created by hu jiaju on 16/5/30. 6 | // Copyright © 2016年 hu jiaju. All rights reserved. 7 | // 8 | 9 | #import "NHGraphCoder.h" 10 | #import 11 | #import "FBShimmeringView.h" 12 | #import "JGAFImageCache.h" 13 | 14 | #ifndef PBSCREEN_WIDTH 15 | #define PBSCREEN_WIDTH ([[UIScreen mainScreen]bounds].size.width) 16 | #endif 17 | #ifndef PBSCREEN_HEIGHT 18 | #define PBSCREEN_HEIGHT ([[UIScreen mainScreen]bounds].size.height) 19 | #endif 20 | #ifndef PBSCREEN_SCALE 21 | #define PBSCREEN_SCALE ([UIScreen mainScreen].scale) 22 | #endif 23 | #ifndef PBCONTENT_OFFSET 24 | #define PBCONTENT_OFFSET (20*PBSCREEN_SCALE) 25 | #endif 26 | #ifndef PBCONTENT_SIZE 27 | #define PBCONTENT_SIZE (PBSCREEN_WIDTH - PBCONTENT_OFFSET*2) 28 | #endif 29 | 30 | #ifndef PBTile_SIZE 31 | #define PBTile_SIZE 100 32 | #endif 33 | #ifndef PB_BALL_RADIUS_SCALE 34 | #define PB_BALL_RADIUS_SCALE 4 35 | #endif 36 | #ifndef PB_BALL_RADIUS_OFFSET 37 | #define PB_BALL_RADIUS_OFFSET 0.5 38 | #endif 39 | #ifndef PB_BALL_SIZE 40 | #define PB_BALL_SIZE (PBTile_SIZE/PB_BALL_SCALE) 41 | #endif 42 | #ifndef PBSLIDER_SIZE 43 | #define PBSLIDER_SIZE 40 44 | #endif 45 | 46 | 47 | 48 | #pragma mark -- Custom Slider -- 49 | 50 | typedef void(^NHSliderEvent)(CGFloat p, BOOL end); 51 | 52 | @interface NHGraphicSlider : UIControl 53 | 54 | /** 55 | * @brief the block event 56 | */ 57 | @property (nonatomic, copy) NHSliderEvent event; 58 | 59 | /** 60 | * The current value of the receiver. 61 | * 62 | * Setting this property causes the receiver to redraw itself using the new value. 63 | * If you try to set a value that is below the minimum or above the maximum value, the minimum or maximum value is set instead. The default value of this property is 0.0. 64 | */ 65 | @property (nonatomic) float value; 66 | 67 | /** 68 | * The minimum value of the receiver. 69 | * 70 | * If you change the value of this property, and the current value of the receiver is below the new minimum, the current value is adjusted to match the new minimum value automatically. 71 | * The default value of this property is 0.0. 72 | */ 73 | @property (nonatomic) float minimumValue; 74 | 75 | /** 76 | * The maximum value of the receiver. 77 | * 78 | * If you change the value of this property, and the current value of the receiver is above the new maximum, the current value is adjusted to match the new maximum value automatically. 79 | * The default value of this property is 1.0. 80 | */ 81 | @property (nonatomic) float maximumValue; 82 | 83 | /** 84 | * The color shown for the portion of the slider that is filled. 85 | */ 86 | @property(nonatomic, retain) UIColor *minimumTrackTintColor; 87 | 88 | /** 89 | * The color shown for the portion of the slider that is not filled. 90 | */ 91 | @property(nonatomic, retain) UIColor *maximumTrackTintColor; 92 | 93 | /** 94 | * The color used to tint the standard thumb. 95 | */ 96 | @property(nonatomic, retain) UIColor *thumbTintColor; 97 | 98 | /** 99 | * @brief called when the slider's value changed 100 | * 101 | * @param event the value change 102 | */ 103 | - (void)handleSliderValueChangedEvent:(NHSliderEvent)event; 104 | 105 | @end 106 | 107 | @interface NHGraphicSlider () 108 | 109 | @property (nonatomic, assign, getter=isContinuous) BOOL continuous; 110 | @property (nonatomic, assign) CGPoint thumbCenterPoint; 111 | 112 | @property (nonatomic, assign) BOOL sliderAbel,sliding; 113 | @property (nonatomic, strong) UISlider *slider; 114 | @property (nonatomic, strong) FBShimmeringView *shimmer; 115 | 116 | #pragma mark - Init and Setup methods 117 | - (void)setup; 118 | 119 | #pragma mark - Thumb management methods 120 | - (BOOL)isPointInThumb:(CGPoint)point; 121 | 122 | @end 123 | 124 | @implementation NHGraphicSlider 125 | 126 | @synthesize value = _value; 127 | - (void)setValue:(float)value { 128 | if (value != _value) { 129 | if (value > self.maximumValue) { value = self.maximumValue; } 130 | if (value < self.minimumValue) { value = self.minimumValue; } 131 | _value = value; 132 | [self setNeedsDisplay]; 133 | if (self.isContinuous) { 134 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 135 | } 136 | } 137 | } 138 | @synthesize minimumValue = _minimumValue; 139 | - (void)setMinimumValue:(float)minimumValue { 140 | if (minimumValue != _minimumValue) { 141 | _minimumValue = minimumValue; 142 | if (self.maximumValue < self.minimumValue) { self.maximumValue = self.minimumValue; } 143 | if (self.value < self.minimumValue) { self.value = self.minimumValue; } 144 | } 145 | } 146 | @synthesize maximumValue = _maximumValue; 147 | - (void)setMaximumValue:(float)maximumValue { 148 | if (maximumValue != _maximumValue) { 149 | _maximumValue = maximumValue; 150 | if (self.minimumValue > self.maximumValue) { self.minimumValue = self.maximumValue; } 151 | if (self.value > self.maximumValue) { self.value = self.maximumValue; } 152 | } 153 | } 154 | 155 | @synthesize minimumTrackTintColor = _minimumTrackTintColor; 156 | - (void)setMinimumTrackTintColor:(UIColor *)minimumTrackTintColor { 157 | if (![minimumTrackTintColor isEqual:_minimumTrackTintColor]) { 158 | _minimumTrackTintColor = minimumTrackTintColor; 159 | [self setNeedsDisplay]; 160 | } 161 | } 162 | 163 | @synthesize maximumTrackTintColor = _maximumTrackTintColor; 164 | - (void)setMaximumTrackTintColor:(UIColor *)maximumTrackTintColor { 165 | if (![maximumTrackTintColor isEqual:_maximumTrackTintColor]) { 166 | _maximumTrackTintColor = maximumTrackTintColor; 167 | [self setNeedsDisplay]; 168 | } 169 | } 170 | 171 | @synthesize thumbTintColor = _thumbTintColor; 172 | - (void)setThumbTintColor:(UIColor *)thumbTintColor { 173 | if (![thumbTintColor isEqual:_thumbTintColor]) { 174 | _thumbTintColor = thumbTintColor; 175 | [self setNeedsDisplay]; 176 | } 177 | } 178 | 179 | @synthesize continuous = _continuous; 180 | 181 | //@synthesize sliderStyle = _sliderStyle; 182 | //- (void)setSliderStyle:(UICircularSliderStyle)sliderStyle { 183 | // if (sliderStyle != _sliderStyle) { 184 | // _sliderStyle = sliderStyle; 185 | // [self setNeedsDisplay]; 186 | // } 187 | //} 188 | 189 | @synthesize thumbCenterPoint = _thumbCenterPoint; 190 | 191 | /** @name Init and Setup methods */ 192 | #pragma mark - Init and Setup methods 193 | - (id)initWithFrame:(CGRect)frame { 194 | self = [super initWithFrame:frame]; 195 | if (self) { 196 | [self setup]; 197 | } 198 | return self; 199 | } 200 | - (void)awakeFromNib { 201 | [self setup]; 202 | } 203 | 204 | - (void)setup { 205 | self.sliderAbel = true; 206 | self.value = 0.0; 207 | self.minimumValue = 0.0; 208 | self.maximumValue = 1.0; 209 | self.minimumTrackTintColor = [UIColor blueColor]; 210 | self.maximumTrackTintColor = [UIColor whiteColor]; 211 | self.thumbTintColor = [UIColor darkGrayColor]; 212 | self.continuous = YES; 213 | self.thumbCenterPoint = CGPointZero; 214 | 215 | /** 216 | * This tapGesture isn't used yet but will allow to jump to a specific location in the circle 217 | */ 218 | // UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHappened:)]; 219 | // [self addGestureRecognizer:tapGestureRecognizer]; 220 | // 221 | // UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureHappened:)]; 222 | // panGestureRecognizer.maximumNumberOfTouches = panGestureRecognizer.minimumNumberOfTouches; 223 | // [self addGestureRecognizer:panGestureRecognizer]; 224 | 225 | self.backgroundColor = [UIColor lightGrayColor]; 226 | 227 | 228 | _slider = [[UISlider alloc] initWithFrame:self.bounds]; 229 | _slider.autoresizingMask = UIViewAutoresizingFlexibleWidth; 230 | CGPoint ctr = _slider.center; 231 | CGRect sliderFrame = _slider.frame; 232 | sliderFrame.size.width -= 4; //each "edge" of the track is 2 pixels wide 233 | _slider.frame = sliderFrame; 234 | _slider.center = ctr; 235 | _slider.backgroundColor = [UIColor clearColor]; 236 | 237 | UIImage *thumbImage = [self thumbWithColor:[UIColor greenColor]]; 238 | [_slider setThumbImage:thumbImage forState:UIControlStateNormal]; 239 | 240 | UIImage *clearImage = [self clearPixel];// 241 | [_slider setMaximumTrackImage:clearImage forState:UIControlStateNormal]; 242 | [_slider setMinimumTrackImage:clearImage forState:UIControlStateNormal]; 243 | 244 | _slider.minimumValue = 0.0; 245 | _slider.maximumValue = 1.0; 246 | _slider.continuous = YES; 247 | _slider.value = 0.0; 248 | 249 | CGSize thumbSize = thumbImage.size; 250 | CGFloat infoStart_x = thumbSize.width ; 251 | CGRect bounds = CGRectMake(infoStart_x,0,CGRectGetWidth(self.bounds)-infoStart_x,CGRectGetHeight(self.bounds)); 252 | UILabel *infoLabel = [[UILabel alloc] initWithFrame:bounds]; 253 | //infoLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 254 | infoLabel.textColor = [UIColor whiteColor]; 255 | infoLabel.textAlignment = NSTextAlignmentCenter; 256 | infoLabel.backgroundColor = [UIColor clearColor]; 257 | infoLabel.font = [UIFont boldSystemFontOfSize:20]; 258 | infoLabel.text = @"拖动滑块完成验证>>>"; 259 | infoLabel.adjustsFontSizeToFitWidth = true; 260 | //[self addSubview:infoLabel]; 261 | FBShimmeringView *shimmer = [[FBShimmeringView alloc] initWithFrame:bounds]; 262 | shimmer.shimmering = true; 263 | shimmer.backgroundColor = [UIColor orangeColor]; 264 | shimmer.shimmeringBeginFadeDuration = 1.5; 265 | shimmer.shimmeringOpacity = 0.3; 266 | shimmer.contentView = infoLabel; 267 | [self addSubview:shimmer]; 268 | [self addSubview:_slider]; 269 | self.shimmer = shimmer; 270 | 271 | // Set the slider action methods 272 | [_slider addTarget:self 273 | action:@selector(sliderUp:) 274 | forControlEvents:UIControlEventTouchUpInside]; 275 | [_slider addTarget:self 276 | action:@selector(sliderUp:) 277 | forControlEvents:UIControlEventTouchUpOutside]; 278 | [_slider addTarget:self 279 | action:@selector(sliderDown:) 280 | forControlEvents:UIControlEventTouchDown]; 281 | [_slider addTarget:self 282 | action:@selector(sliderChanged:) 283 | forControlEvents:UIControlEventValueChanged]; 284 | } 285 | 286 | - (void)resetSlider { 287 | [_slider setValue:0.0 animated: YES]; 288 | self.shimmer.alpha = 1.0; 289 | if (_event) { 290 | _event(0, false); 291 | } 292 | } 293 | 294 | // UISlider actions 295 | - (void) sliderUp:(UISlider *)sender { 296 | 297 | if (_sliding) { 298 | _sliding = NO; 299 | if (_event) { 300 | _event(sender.value, true); 301 | } 302 | } 303 | } 304 | 305 | - (void) sliderDown:(UISlider *)sender { 306 | 307 | if (!_sliding) { 308 | //[_label setAnimated:NO]; 309 | } 310 | _sliding = YES; 311 | } 312 | 313 | - (void) sliderChanged:(UISlider *)sender { 314 | 315 | self.shimmer.alpha = MAX(0.0, 1.0 - (_slider.value * 3.5)); 316 | if (_event) { 317 | _event(sender.value, false); 318 | } 319 | } 320 | 321 | - (UIImage *) thumbWithColor:(UIColor*)color { 322 | CGFloat scale = [UIScreen mainScreen].scale; 323 | if (scale<1.0) {scale = 1.0;} 324 | 325 | CGSize size = CGSizeMake(68.0*scale, 44.0*scale); 326 | CGFloat radius = 10.0*scale; 327 | // create a new bitmap image context 328 | UIGraphicsBeginImageContext(size); 329 | 330 | // get context 331 | CGContextRef context = UIGraphicsGetCurrentContext(); 332 | 333 | // push context to make it current 334 | // (need to do this manually because we are not drawing in a UIView) 335 | UIGraphicsPushContext(context); 336 | 337 | [color setFill]; 338 | [[[UIColor blackColor] colorWithAlphaComponent:0.8] setStroke]; 339 | 340 | CGFloat radiusp = radius+0.5; 341 | CGFloat wid1 = size.width-0.5; 342 | CGFloat hei1 = size.height-0.5; 343 | CGFloat wid2 = size.width-radiusp; 344 | CGFloat hei2 = size.height-radiusp; 345 | 346 | // Path 347 | CGContextMoveToPoint(context, 0.5, radiusp); 348 | CGContextAddArcToPoint(context, 0.5, 0.5, radiusp, 0.5, radius); 349 | CGContextAddLineToPoint(context, wid2, 0.5); 350 | CGContextAddArcToPoint(context, wid1, 0.5, wid1, radiusp, radius); 351 | CGContextAddLineToPoint(context, wid1, hei2); 352 | CGContextAddArcToPoint(context, wid1, hei1, wid2, hei1, radius); 353 | CGContextAddLineToPoint(context, radius, hei1); 354 | CGContextAddArcToPoint(context, 0.5, hei1, 0.5, hei2, radius); 355 | CGContextClosePath(context); 356 | CGContextDrawPath(context, kCGPathFillStroke); 357 | 358 | 359 | // Arrow 360 | [[[UIColor blueColor] colorWithAlphaComponent:0.6] setFill]; 361 | [[[UIColor blackColor] colorWithAlphaComponent:0.3] setStroke]; 362 | 363 | CGFloat points[8]= { (19.0*scale)+0.5, 364 | (16.0*scale)+0.5, 365 | (36.0*scale)+0.5, 366 | (10.0*scale)+0.5, 367 | (52.0*scale)+0.5, 368 | (22.0*scale)+0.5, 369 | (34.0*scale)+0.5, 370 | (28.0*scale)+0.5 }; 371 | 372 | CGContextMoveToPoint(context, points[0], points[1]); 373 | CGContextAddLineToPoint(context, points[2], points[1]); 374 | CGContextAddLineToPoint(context, points[2], points[3]); 375 | CGContextAddLineToPoint(context, points[4], points[5]); 376 | CGContextAddLineToPoint(context, points[2], points[6]); 377 | CGContextAddLineToPoint(context, points[2], points[7]); 378 | CGContextAddLineToPoint(context, points[0], points[7]); 379 | CGContextClosePath(context); 380 | CGContextDrawPath(context, kCGPathFillStroke); 381 | 382 | 383 | // Light 384 | [[[UIColor whiteColor] colorWithAlphaComponent:0.2] setFill]; 385 | 386 | CGFloat mid = lround(size.height/2.0)+0.5; 387 | CGContextMoveToPoint(context, 0.5, radiusp); 388 | CGContextAddArcToPoint(context, 0.5, 0.5, radiusp, 0.5, radius); 389 | CGContextAddLineToPoint(context, wid2, 0.5); 390 | CGContextAddArcToPoint(context, wid1, 0.5, wid1, radiusp, radius); 391 | CGContextAddLineToPoint(context, wid1, mid); 392 | CGContextAddLineToPoint(context, 0.5, mid); 393 | CGContextClosePath(context); 394 | CGContextDrawPath(context, kCGPathFill); 395 | 396 | // pop context 397 | UIGraphicsPopContext(); 398 | 399 | // get a UIImage from the image context 400 | UIImage *outputImage = [[UIImage alloc] initWithCGImage:UIGraphicsGetImageFromCurrentImageContext().CGImage scale:scale orientation:UIImageOrientationUp]; 401 | //write (debug) 402 | //[UIImagePNGRepresentation(outputImage) writeToFile:@"/Users/mathieu/Desktop/test.png" atomically:YES]; 403 | 404 | // clean up drawing environment 405 | UIGraphicsEndImageContext(); 406 | 407 | return outputImage; 408 | } 409 | 410 | - (UIImage *) clearPixel { 411 | CGRect rect = CGRectMake(0.0, 0.0, 1.0, 1.0); 412 | UIGraphicsBeginImageContext(rect.size); 413 | CGContextRef context = UIGraphicsGetCurrentContext(); 414 | CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); 415 | CGContextFillRect(context, rect); 416 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 417 | UIGraphicsEndImageContext(); 418 | return image; 419 | } 420 | 421 | /** @name Drawing methods */ 422 | #pragma mark - Drawing methods 423 | #define kLineWidth 5.0 424 | #define kThumbRadius 12.0 425 | - (CGFloat)sliderRadius { 426 | CGFloat radius = MIN(self.bounds.size.width/2, self.bounds.size.height/2); 427 | radius -= MAX(kLineWidth, kThumbRadius); 428 | return radius; 429 | } 430 | - (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context { 431 | UIGraphicsPushContext(context); 432 | CGContextBeginPath(context); 433 | 434 | CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y); 435 | CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO); 436 | 437 | CGContextFillPath(context); 438 | UIGraphicsPopContext(); 439 | } 440 | 441 | - (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context { 442 | UIGraphicsPushContext(context); 443 | CGContextBeginPath(context); 444 | 445 | // float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI); 446 | float angleFromTrack = 0; 447 | CGFloat startAngle = -M_PI_2; 448 | CGFloat endAngle = startAngle + angleFromTrack; 449 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO); 450 | 451 | CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context); 452 | 453 | CGContextStrokePath(context); 454 | UIGraphicsPopContext(); 455 | 456 | return arcEndPoint; 457 | } 458 | 459 | - (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context { 460 | UIGraphicsPushContext(context); 461 | 462 | // float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI); 463 | float angleFromTrack = 0; 464 | CGFloat startAngle = -M_PI_2; 465 | CGFloat endAngle = startAngle + angleFromTrack; 466 | CGContextMoveToPoint(context, center.x, center.y); 467 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO); 468 | 469 | CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context); 470 | 471 | CGContextClosePath(context); 472 | CGContextFillPath(context); 473 | UIGraphicsPopContext(); 474 | 475 | return arcEndPoint; 476 | } 477 | 478 | /** @name Thumb management methods */ 479 | #pragma mark - Thumb management methods 480 | - (BOOL)isPointInThumb:(CGPoint)point { 481 | CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2); 482 | return CGRectContainsPoint(thumbTouchRect, point); 483 | } 484 | 485 | /** @name UIGestureRecognizer management methods */ 486 | #pragma mark - UIGestureRecognizer management methods 487 | - (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer { 488 | CGPoint tapLocation = [panGestureRecognizer locationInView:self]; 489 | switch (panGestureRecognizer.state) { 490 | case UIGestureRecognizerStateChanged: { 491 | 492 | self.thumbCenterPoint = tapLocation; 493 | self.value = tapLocation.x/CGRectGetMaxX(self.bounds); 494 | break; 495 | } 496 | case UIGestureRecognizerStateEnded: 497 | if (!self.isContinuous) { 498 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 499 | } 500 | if ([self isPointInThumb:tapLocation]) { 501 | [self sendActionsForControlEvents:UIControlEventTouchUpInside]; 502 | } 503 | else { 504 | [self sendActionsForControlEvents:UIControlEventTouchUpOutside]; 505 | } 506 | break; 507 | default: 508 | break; 509 | } 510 | } 511 | - (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer { 512 | if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) { 513 | CGPoint tapLocation = [tapGestureRecognizer locationInView:self]; 514 | self.sliderAbel = [self isPointInThumb:tapLocation]; 515 | } 516 | } 517 | 518 | /** @name Touches Methods */ 519 | #pragma mark - Touches Methods 520 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 521 | [super touchesBegan:touches withEvent:event]; 522 | 523 | UITouch *touch = [touches anyObject]; 524 | CGPoint touchLocation = [touch locationInView:self]; 525 | if ([self isPointInThumb:touchLocation]) { 526 | [self sendActionsForControlEvents:UIControlEventTouchDown]; 527 | } 528 | } 529 | 530 | - (void)handleSliderValueChangedEvent:(NHSliderEvent)event { 531 | self.event = [event copy]; 532 | } 533 | 534 | 535 | @end 536 | 537 | #pragma mark -- state view -- 538 | 539 | @interface UIImage (NHHelper) 540 | 541 | - (UIImage *) imageWithTintColor:(UIColor *)tintColor; 542 | 543 | @end 544 | 545 | @implementation UIImage (NHHelper) 546 | 547 | - (UIImage *) imageWithTintColor:(UIColor *)tintColor { 548 | //We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen. 549 | UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); 550 | [tintColor setFill]; 551 | CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); 552 | UIRectFill(bounds); 553 | 554 | //Draw the tinted image in context 555 | [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; 556 | 557 | UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); 558 | UIGraphicsEndImageContext(); 559 | 560 | return tintedImage; 561 | } 562 | 563 | @end 564 | 565 | typedef void(^NHStateOnceMoreEvent)(void); 566 | 567 | @interface NHGraphicState : UIView 568 | 569 | @property (nonatomic, strong) UIImageView *iconView; 570 | @property (nonatomic, strong) UILabel *titleLabel,*subLabel; 571 | @property (nonatomic, strong) UIButton *moreBtn; 572 | @property (nonatomic, strong) UIActivityIndicatorView *indicatorView; 573 | 574 | @property (nonatomic, copy) NHStateOnceMoreEvent event; 575 | 576 | - (void)loadingState:(BOOL)load; 577 | 578 | - (void)handleStateOnceMoreEvent:(NHStateOnceMoreEvent)event; 579 | 580 | @end 581 | 582 | @implementation NHGraphicState 583 | 584 | - (id)initWithFrame:(CGRect)frame { 585 | self = [super initWithFrame:frame]; 586 | if (self) { 587 | [self __initSetup]; 588 | } 589 | return self; 590 | } 591 | 592 | - (id)initWithCoder:(NSCoder *)aDecoder { 593 | self = [super initWithCoder:aDecoder]; 594 | if (self) { 595 | [self __initSetup]; 596 | } 597 | return self; 598 | } 599 | 600 | - (void)__initSetup { 601 | CGFloat m_icon_size = PBSLIDER_SIZE; 602 | CGSize size = self.bounds.size; 603 | CGRect bounds = CGRectMake((size.width-m_icon_size)*0.5, m_icon_size, m_icon_size, m_icon_size); 604 | UIImageView *imgv = [[UIImageView alloc] initWithFrame:bounds]; 605 | [self addSubview:imgv]; 606 | self.iconView = imgv; 607 | 608 | bounds = CGRectMake(0, m_icon_size*2, size.width, 30); 609 | UILabel *label = [[UILabel alloc] initWithFrame:bounds]; 610 | label.font = [UIFont boldSystemFontOfSize:20]; 611 | label.textAlignment = NSTextAlignmentCenter; 612 | label.textColor = [UIColor whiteColor]; 613 | [self addSubview:label]; 614 | self.titleLabel = label; 615 | 616 | bounds.origin.y += 30; 617 | label = [[UILabel alloc] initWithFrame:bounds]; 618 | label.font = [UIFont boldSystemFontOfSize:15]; 619 | label.textAlignment = NSTextAlignmentCenter; 620 | label.textColor = [UIColor whiteColor]; 621 | [self addSubview:label]; 622 | self.subLabel = label; 623 | 624 | CGFloat m_btn_size = m_icon_size * 2; 625 | bounds = CGRectMake((size.width-m_btn_size)*0.5, m_icon_size*3.5, m_btn_size, 30); 626 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 627 | btn.frame = bounds; 628 | [btn setTitle:@"再试一次" forState:UIControlStateNormal]; 629 | [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 630 | [btn addTarget:self action:@selector(onceMoreEvent) forControlEvents:UIControlEventTouchUpInside]; 631 | [self addSubview:btn]; 632 | self.moreBtn = btn; 633 | btn.hidden = true; 634 | 635 | UIActivityIndicatorView *actor = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 636 | actor.hidesWhenStopped = true; 637 | [self addSubview:actor]; 638 | self.indicatorView = actor; 639 | CGPoint mCenter = CGPointMake(CGRectGetWidth(self.bounds)*0.5, CGRectGetHeight(self.bounds)*0.5); 640 | actor.center = mCenter; 641 | } 642 | 643 | - (void)loadingState:(BOOL)load { 644 | self.iconView.hidden = load; 645 | self.titleLabel.hidden = load; 646 | self.subLabel.hidden = load; 647 | self.moreBtn.hidden = load; 648 | if (load) { 649 | [self.indicatorView startAnimating]; 650 | self.backgroundColor = [UIColor lightGrayColor]; 651 | }else{ 652 | [self.indicatorView stopAnimating]; 653 | } 654 | } 655 | 656 | - (void)handleStateOnceMoreEvent:(NHStateOnceMoreEvent)event { 657 | self.event = [event copy]; 658 | } 659 | 660 | - (void)onceMoreEvent { 661 | if (_event) { 662 | _event(); 663 | } 664 | } 665 | 666 | @end 667 | 668 | #pragma mark -- Custom Graphic -- 669 | 670 | struct PBShapeBumper { 671 | BOOL arrow_left; 672 | BOOL arrow_up; 673 | BOOL m_h_isBump; 674 | BOOL m_v_isBump; 675 | CGPoint m_center; 676 | int m_ball_radius; 677 | int m_rect_radius; 678 | }; 679 | 680 | typedef struct PBShapeBumper PBShapeBumper; 681 | 682 | @interface NHGraphCoder () 683 | 684 | @property (nonatomic, strong) UIImage *img, *innerImg, *flagImg; 685 | @property (nonatomic, copy) NHGraphEvent event; 686 | @property (nonatomic) PBShapeBumper shapeBumper; 687 | @property (nonatomic, strong) UIBezierPath *shapePath; 688 | @property (nonatomic, strong) UIImageView *mFlag; 689 | 690 | @property (nonatomic, strong) NHGraphicSlider *slider; 691 | @property (nonatomic, strong) NHGraphicState *stater; 692 | 693 | @property (nonatomic, assign) BOOL endDetect; 694 | 695 | @end 696 | 697 | static NHGraphCoder *instance = nil; 698 | 699 | @implementation NHGraphCoder 700 | 701 | - (void)dealloc { 702 | _img = nil; 703 | _event = nil; 704 | } 705 | 706 | //+ (NHGraphCoder *)shared { 707 | // static dispatch_once_t onceToken; 708 | // dispatch_once(&onceToken, ^{ 709 | // if (instance == nil) { 710 | // instance = [[NHGraphCoder alloc] init]; 711 | // } 712 | // }); 713 | // return instance; 714 | //} 715 | 716 | - (void)setFrame:(CGRect)frame { 717 | [super setFrame:frame]; 718 | } 719 | 720 | - (void)setBounds:(CGRect)bounds { 721 | [super setBounds:bounds]; 722 | } 723 | 724 | + (NHGraphCoder *)codeWithImage:(UIImage *)img { 725 | return [[NHGraphCoder alloc] initWithImage:img]; 726 | } 727 | 728 | - (void)handleGraphicCoderVerifyEvent:(NHGraphEvent)event { 729 | self.event = [event copy]; 730 | } 731 | 732 | //- (id)init { 733 | // self = [super init]; 734 | // if (self) { 735 | // } 736 | // return self; 737 | //} 738 | // 739 | //- (id)initWithFrame:(CGRect)frame { 740 | // self = [super initWithFrame:frame]; 741 | // if (self) { 742 | // [self __initSetup]; 743 | // } 744 | // return self; 745 | //} 746 | // 747 | //- (id)initWithCoder:(NSCoder *)aDecoder { 748 | // self = [super initWithCoder:aDecoder]; 749 | // if (self) { 750 | // [self __initSetup]; 751 | // } 752 | // return self; 753 | //} 754 | 755 | - (id)initWithImage:(UIImage *)img { 756 | 757 | NHGraphCoder *coder = [NHGraphCoder new]; 758 | CGSize tmpSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 759 | CGRect bounds = (CGRect){ 760 | .origin = CGPointZero, 761 | .size = tmpSize 762 | }; 763 | img = ((img != nil)?img:[UIImage imageNamed:@"coder_default.jpg"]); 764 | [coder setFrame:bounds]; 765 | [coder __initSetupImg:img]; 766 | return coder; 767 | } 768 | 769 | - (void)setImg:(UIImage *)img { 770 | if (_img) { 771 | _img = nil; 772 | } 773 | _img = img; 774 | [self updateInnerImg:img]; 775 | } 776 | 777 | - (void)__initSetupImg:(UIImage *)img { 778 | self.img = img; 779 | [self __initSetup]; 780 | } 781 | 782 | - (void)updateInnerImg:(UIImage *)img { 783 | /* Don't resize if we already meet the required destination size. */ 784 | CGSize dstSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 785 | if (CGSizeEqualToSize(img.size, dstSize)) { 786 | self.innerImg = img; 787 | return ; 788 | } 789 | CGSize originSize = img.size; 790 | BOOL keepAspect = false; 791 | CGRect scaledImageRect = CGRectZero; 792 | 793 | CGFloat aspectWidth = dstSize.width / originSize.width; 794 | CGFloat aspectHeight = dstSize.height / originSize.height; 795 | CGFloat aspectRatio = keepAspect?MIN(aspectWidth, aspectHeight):MAX(aspectWidth, aspectHeight); 796 | 797 | scaledImageRect.size.width = originSize.width * aspectRatio; 798 | scaledImageRect.size.height = originSize.height * aspectRatio; 799 | scaledImageRect.origin.x = (dstSize.width - scaledImageRect.size.width) / 2.0f; 800 | scaledImageRect.origin.y = (dstSize.height - scaledImageRect.size.height) / 2.0f; 801 | 802 | UIGraphicsBeginImageContextWithOptions( dstSize, NO, 0 ); 803 | [img drawInRect:scaledImageRect]; 804 | UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); 805 | UIGraphicsEndImageContext(); 806 | 807 | self.innerImg = scaledImage; 808 | } 809 | 810 | - (void)__initSetup { 811 | 812 | //[self addSubview:self.mFlag]; 813 | 814 | [self setNeedsDisplay]; 815 | // 816 | CGFloat m_btn_size = PBSLIDER_SIZE * 0.5; 817 | CGRect bounds = CGRectMake(PBCONTENT_SIZE-m_btn_size, 0, m_btn_size, m_btn_size); 818 | // UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 819 | // btn.frame = bounds; 820 | // // [btn setTitle:@"refresh" forState:UIControlStateNormal]; 821 | // // [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 822 | // [btn setImage:img forState:UIControlStateNormal]; 823 | // [btn addTarget:self action:@selector(refreshState) forControlEvents:UIControlEventTouchUpInside]; 824 | // [self addSubview:btn]; 825 | 826 | UIImageView *v_img = [[UIImageView alloc] init]; 827 | [self addSubview:v_img]; 828 | self.mFlag = v_img; 829 | 830 | __weak typeof(self) weakSelf = self; 831 | bounds = CGRectMake(0, PBCONTENT_SIZE-PBSLIDER_SIZE, PBCONTENT_SIZE, PBSLIDER_SIZE); 832 | NHGraphicSlider *slider = [[NHGraphicSlider alloc] initWithFrame:bounds]; 833 | slider.backgroundColor = [UIColor lightGrayColor]; 834 | [slider handleSliderValueChangedEvent:^(CGFloat p, BOOL end) { 835 | __strong typeof(weakSelf) strongSelf = weakSelf; 836 | [strongSelf sliderValueChanged:p]; 837 | if (end) { 838 | [strongSelf endDetectSliderValue:p]; 839 | } 840 | }]; 841 | [self addSubview:slider]; 842 | self.slider = slider; 843 | 844 | bounds = self.bounds; 845 | bounds.size.height -= PBSLIDER_SIZE; 846 | NHGraphicState *stater = [[NHGraphicState alloc] initWithFrame:bounds]; 847 | [stater handleStateOnceMoreEvent:^{ 848 | __strong typeof(weakSelf) strongSelf = weakSelf; 849 | [strongSelf refreshState]; 850 | }]; 851 | [self addSubview:stater]; 852 | self.stater = stater; 853 | stater.hidden = true; 854 | } 855 | 856 | - (CAShapeLayer *)getBorderLayer { 857 | CGRect bounds = CGRectMake(0, 0, PBTile_SIZE, PBTile_SIZE); 858 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 859 | // shapeLayer.backgroundColor = [UIColor yellowColor].CGColor; 860 | shapeLayer.frame = bounds; 861 | [shapeLayer setFillRule:kCAFillRuleEvenOdd]; 862 | shapeLayer.fillColor = [[UIColor clearColor] CGColor]; 863 | shapeLayer.path = self.shapePath.CGPath; 864 | shapeLayer.shadowOffset = CGSizeMake(2, 2); 865 | shapeLayer.shadowPath = self.shapePath.CGPath; 866 | shapeLayer.shadowColor = [UIColor yellowColor].CGColor; 867 | shapeLayer.strokeColor = [UIColor yellowColor].CGColor; 868 | shapeLayer.lineWidth = 2; 869 | return shapeLayer; 870 | } 871 | 872 | - (void)updateFlag { 873 | CGRect bounds = CGRectMake(0, self.shapeBumper.m_center.y-PBTile_SIZE*0.5, PBTile_SIZE, PBTile_SIZE); 874 | self.mFlag.image = self.flagImg; 875 | self.mFlag.frame = bounds; 876 | [self.mFlag.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 877 | CAShapeLayer *borderLayer = [self getBorderLayer]; 878 | [self.mFlag.layer addSublayer:borderLayer]; 879 | CAShapeLayer *shapeLayer = [self getShapeLayer]; 880 | self.mFlag.layer.mask = shapeLayer; 881 | 882 | } 883 | 884 | - (void)setShapeBumper:(PBShapeBumper)shapeBumper { 885 | _shapeBumper = shapeBumper; 886 | if (_shapePath) { 887 | _shapePath = nil; 888 | } 889 | self.shapePath = [self copyShapePath]; 890 | self.flagImg = [self getThumbiaFlagByPath:self.shapePath]; 891 | [self updateFlag]; 892 | } 893 | 894 | - (UIBezierPath *)copyShapePath { 895 | PBShapeBumper *tmpBumper = malloc(sizeof(PBShapeBumper)); 896 | tmpBumper->arrow_left = self.shapeBumper.arrow_left; 897 | tmpBumper->arrow_up = self.shapeBumper.arrow_up; 898 | tmpBumper->m_h_isBump = self.shapeBumper.m_h_isBump; 899 | tmpBumper->m_v_isBump = self.shapeBumper.m_v_isBump; 900 | tmpBumper->m_ball_radius = self.shapeBumper.m_ball_radius; 901 | tmpBumper->m_rect_radius = self.shapeBumper.m_rect_radius; 902 | tmpBumper->m_center = CGPointMake(50, 50); 903 | return [self getPathForBumper:*tmpBumper]; 904 | } 905 | 906 | - (CAShapeLayer *)getShapeLayer { 907 | CGRect bounds = CGRectMake(0, 0, PBTile_SIZE, PBTile_SIZE); 908 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 909 | // shapeLayer.backgroundColor = [UIColor yellowColor].CGColor; 910 | shapeLayer.frame = bounds; 911 | [shapeLayer setFillRule:kCAFillRuleEvenOdd]; 912 | shapeLayer.fillColor = [[UIColor yellowColor] CGColor]; 913 | shapeLayer.path = self.shapePath.CGPath; 914 | 915 | return shapeLayer; 916 | } 917 | 918 | - (UIImage *)getShapeImage { 919 | // UIBezierPath *tmpPath = [self copyShapePath]; 920 | // return [self getThumbiaFlagByPath:tmpPath]; 921 | return self.flagImg; 922 | } 923 | 924 | - (UIBezierPath *)getShapePath { 925 | return [self shapePath]; 926 | } 927 | 928 | - (UIImage *)getThumbiaFlagByPath:(UIBezierPath *)path{ 929 | 930 | //destnation bounds 931 | //CGPoint startPoint = [self getStartPointBy:self.shapeBumper]; 932 | int x_start = self.shapeBumper.m_center.x - PBTile_SIZE * 0.5; 933 | int y_start = self.shapeBumper.m_center.y - PBTile_SIZE * 0.5; 934 | int m_width = PBTile_SIZE; 935 | UIImage *img = self.innerImg; 936 | CGFloat m_scale = img.scale; 937 | //NSLog(@"x:%d-y:%d-size:%d",x_start,y_start,m_width); 938 | 939 | CGFloat scale = MAX(m_scale, 1.0f); 940 | CGRect scaledBounds = CGRectMake(x_start * scale, y_start * scale, m_width * scale, m_width * scale); 941 | CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], scaledBounds); 942 | UIImage *croppedImage = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; 943 | CGImageRelease(imageRef); 944 | 945 | return croppedImage; 946 | } 947 | 948 | - (void)resetStateForDetect { 949 | [self refreshState]; 950 | } 951 | 952 | - (void)refreshState { 953 | NSLog(@"refresh state"); 954 | self.endDetect = false; 955 | self.slider.userInteractionEnabled = !self.endDetect; 956 | self.mFlag.hidden = self.endDetect; 957 | [self.stater loadingState:false]; 958 | self.stater.hidden = !self.endDetect; 959 | [self setNeedsDisplay]; 960 | } 961 | 962 | - (void)sliderValueChanged:(CGFloat)value { 963 | if (value < 0 || value > 1) { 964 | return; 965 | } 966 | CGRect bounds = CGRectMake((PBCONTENT_SIZE-PBTile_SIZE-PBSLIDER_SIZE)*value, self.shapeBumper.m_center.y-PBTile_SIZE*0.5, PBTile_SIZE, PBTile_SIZE); 967 | self.mFlag.frame = bounds; 968 | } 969 | //滑动slider结束后调用 970 | - (void)endDetectSliderValue:(CGFloat)value { 971 | CGFloat tmp_x = (PBCONTENT_SIZE-PBTile_SIZE-PBSLIDER_SIZE)*value; 972 | tmp_x = self.mFlag.center.x; 973 | const int error_offset = 5; 974 | BOOL success = (fabs(tmp_x - self.shapeBumper.m_center.x) <= error_offset); 975 | self.endDetect = true; 976 | [self setNeedsDisplay]; 977 | self.slider.userInteractionEnabled = !self.endDetect; 978 | self.mFlag.hidden = self.endDetect; 979 | [self.slider resetSlider]; 980 | [self endVerify:success]; 981 | } 982 | 983 | - (void)endVerify:(BOOL)success { 984 | // UIImage *m_img;UIColor *m_bg_color; 985 | // NSString *m_title,*m_sub_title; 986 | // if (success) { 987 | // m_img = [UIImage imageNamed:@"success"]; 988 | // m_img = [m_img imageWithTintColor:[UIColor whiteColor]]; 989 | // m_bg_color = [UIColor colorWithRed:80/255.0 green:95/255.0 blue:235/255.0 alpha:0.6]; 990 | // m_title = @"验证成功"; 991 | // }else{ 992 | // m_img = [UIImage imageNamed:@"error"]; 993 | // m_img = [m_img imageWithTintColor:[UIColor whiteColor]]; 994 | // m_bg_color = [UIColor colorWithRed:197/255.0 green:100/255.0 blue:66/255.0 alpha:0.6]; 995 | // m_title = @"验证失败"; 996 | // m_sub_title = @"拖动滑块将悬浮图像正确拼合"; 997 | // } 998 | // self.stater.iconView.image = m_img; 999 | // self.stater.backgroundColor = m_bg_color; 1000 | // self.stater.titleLabel.text = m_title; 1001 | // self.stater.subLabel.text = m_sub_title; 1002 | // self.stater.hidden = false; 1003 | // self.stater.moreBtn.hidden = success; 1004 | 1005 | if (_event) { 1006 | _event(self, success); 1007 | } 1008 | } 1009 | 1010 | - (int)randomFrom:(int)min to:(int)max { 1011 | return arc4random() % (max-min) + min; 1012 | } 1013 | 1014 | - (CGPoint)getStartPointBy:(PBShapeBumper)bumper { 1015 | int m_rect_center_y = bumper.m_center.y; 1016 | int m_rect_center_x = bumper.m_center.x; 1017 | BOOL arrow_left = bumper.arrow_left; 1018 | BOOL arrow_up = bumper.arrow_up; 1019 | BOOL m_h_isBump = bumper.m_h_isBump; 1020 | BOOL m_v_isBump = bumper.m_v_isBump; 1021 | int m_ball_radius = bumper.m_ball_radius; 1022 | int m_rect_radius = bumper.m_rect_radius; 1023 | //destnation bounds 1024 | CGFloat x_start, y_start; 1025 | if (arrow_left && m_h_isBump) { 1026 | //左凸 1027 | m_rect_center_x += m_ball_radius * 1.5 * 0.5; 1028 | }else if (!arrow_left && m_h_isBump){ 1029 | //右凸 1030 | m_rect_center_x -= m_ball_radius * 1.5 * 0.5; 1031 | } 1032 | if (arrow_up && m_v_isBump) { 1033 | //上凸 1034 | m_rect_center_y += m_ball_radius * 1.5 * 0.5; 1035 | }else if (!arrow_up && m_v_isBump){ 1036 | //下凸 1037 | m_rect_center_y -= m_ball_radius * 1.5 * 0.5; 1038 | } 1039 | x_start = m_rect_center_x-m_rect_radius; 1040 | y_start = m_rect_center_y-m_rect_radius; 1041 | 1042 | return CGPointMake(x_start, y_start); 1043 | } 1044 | 1045 | - (UIBezierPath *)graphWithBallTheta:(CGFloat)theta_rad shapeBump:(PBShapeBumper)bumper{ 1046 | 1047 | // int m_rect_center_y = bumper.m_center.y; 1048 | // int m_rect_center_x = bumper.m_center.x; 1049 | BOOL arrow_left = bumper.arrow_left; 1050 | BOOL arrow_up = bumper.arrow_up; 1051 | BOOL m_h_isBump = bumper.m_h_isBump; 1052 | BOOL m_v_isBump = bumper.m_v_isBump; 1053 | int m_ball_radius = bumper.m_ball_radius; 1054 | int m_rect_radius = bumper.m_rect_radius; 1055 | int m_rectangle_size = m_rect_radius * 2; 1056 | //destnation bounds 1057 | CGPoint startPoint = [self getStartPointBy:bumper]; 1058 | CGFloat x_start = startPoint.x; 1059 | CGFloat y_start = startPoint.y; 1060 | int m_small_rect_center_x = x_start + m_rect_radius; 1061 | int m_small_rect_center_y = y_start + m_rect_radius; 1062 | NSLog(@"start:%@--ball:%d*rect:%d",NSStringFromCGPoint(startPoint),m_ball_radius,m_rect_radius); 1063 | 1064 | UIBezierPath *path = [UIBezierPath bezierPath]; 1065 | // CGFloat theta_rad = acos(0.5); 1066 | //NSLog(@"theta :%f",theta_rad); 1067 | CGFloat xuan_half = m_ball_radius*sin(theta_rad); 1068 | CGFloat m_tmp_len = m_rect_radius-xuan_half; 1069 | 1070 | //begin path 1071 | //先移动到左上角 逆时针绘制 1072 | CGPoint tmp_p = CGPointMake(x_start, y_start); 1073 | [path moveToPoint:tmp_p]; 1074 | //left 1075 | tmp_p = CGPointMake(x_start, y_start+m_tmp_len); 1076 | [path addLineToPoint:tmp_p]; 1077 | if (arrow_left) { 1078 | if (m_h_isBump) { 1079 | //是凸出 1080 | CGPoint tmp_center = CGPointMake(x_start-m_ball_radius*0.5, m_small_rect_center_y); 1081 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI*2-theta_rad) endAngle:theta_rad clockwise:false]; 1082 | }else{ 1083 | //凹陷 1084 | CGPoint tmp_center = CGPointMake(x_start+m_ball_radius*0.5, m_small_rect_center_y); 1085 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI+theta_rad) endAngle:(M_PI-theta_rad) clockwise:true]; 1086 | } 1087 | } 1088 | tmp_p.y += m_tmp_len + xuan_half*2; 1089 | [path addLineToPoint:tmp_p]; 1090 | 1091 | //bottom 1092 | CGFloat theta_rad_left = M_PI_2 - theta_rad; 1093 | tmp_p.x += m_tmp_len; 1094 | [path addLineToPoint:tmp_p]; 1095 | if (!arrow_up) { 1096 | if (m_v_isBump) { 1097 | //凸出 1098 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_rectangle_size+m_ball_radius*0.5); 1099 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI+theta_rad_left) endAngle:(M_PI*2-theta_rad_left) clockwise:false]; 1100 | }else{ 1101 | //凹陷 1102 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_rectangle_size-m_ball_radius*0.5); 1103 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI-theta_rad_left) endAngle:(M_PI*2+theta_rad_left) clockwise:true]; 1104 | } 1105 | } 1106 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start+m_rectangle_size); 1107 | [path addLineToPoint:tmp_p]; 1108 | //right 1109 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start+m_rectangle_size-m_tmp_len); 1110 | [path addLineToPoint:tmp_p]; 1111 | if (!arrow_left) { 1112 | if (m_h_isBump) { 1113 | //凸出 1114 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x+m_rect_radius+m_ball_radius*0.5, m_small_rect_center_y); 1115 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI-theta_rad) endAngle:(M_PI+theta_rad) clockwise:false]; 1116 | }else{ 1117 | //凹陷 1118 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x+m_rect_radius-m_ball_radius*0.5, m_small_rect_center_y); 1119 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:theta_rad endAngle:(M_PI*2-theta_rad) clockwise:true]; 1120 | } 1121 | } 1122 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start); 1123 | [path addLineToPoint:tmp_p]; 1124 | //up 1125 | tmp_p = CGPointMake(x_start+m_rectangle_size-m_tmp_len, y_start); 1126 | [path addLineToPoint:tmp_p]; 1127 | if (arrow_up) { 1128 | if (m_v_isBump) { 1129 | //凸出 1130 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start-m_ball_radius*0.5); 1131 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(theta_rad_left) endAngle:(M_PI-theta_rad_left) clockwise:false]; 1132 | }else{ 1133 | //凹陷 1134 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_ball_radius*0.5); 1135 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(-theta_rad_left) endAngle:(M_PI+theta_rad_left) clockwise:true]; 1136 | } 1137 | } 1138 | tmp_p = CGPointMake(x_start, y_start); 1139 | [path addLineToPoint:tmp_p]; 1140 | //test 1141 | 1142 | [path closePath]; 1143 | 1144 | return path; 1145 | } 1146 | 1147 | - (UIBezierPath *)generateRandomPath { 1148 | 1149 | int m_tmp_direction_h = arc4random() % 2; 1150 | BOOL arrow_left = m_tmp_direction_h == 0; 1151 | int m_h_Bump = arc4random() % 2; 1152 | BOOL m_h_isBump = (m_h_Bump == 0); 1153 | int m_tmp_direction_v = arc4random() % 2; 1154 | BOOL arrow_up = m_tmp_direction_v == 0; 1155 | BOOL m_v_isBump = !m_h_isBump; 1156 | 1157 | int m_radius_scale = PB_BALL_RADIUS_SCALE; 1158 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1159 | // int m_ball_radius = PBTile_SIZE/((1+m_ball_cross_scale)+m_radius_scale); 1160 | int m_ball_radius = PBTile_SIZE/(m_radius_scale*2); 1161 | int m_rectangle_size = m_ball_radius*m_radius_scale; 1162 | int m_rect_radius = m_rectangle_size * 0.5; 1163 | 1164 | int min = PBTile_SIZE; 1165 | int max = PBCONTENT_SIZE-min; 1166 | int m_rect_center_y = [self randomFrom:min to:max]; 1167 | int m_rect_center_x = [self randomFrom:min to:max]; 1168 | NSLog(@"random path center x:%d---y:%d",m_rect_center_x,m_rect_center_y); 1169 | PBShapeBumper bumper = { 1170 | .arrow_up = arrow_up, 1171 | .arrow_left = arrow_left, 1172 | .m_h_isBump = m_h_isBump, 1173 | .m_v_isBump = m_v_isBump, 1174 | .m_ball_radius = m_ball_radius, 1175 | .m_rect_radius = m_rect_radius, 1176 | .m_center = CGPointMake(m_rect_center_x, m_rect_center_y) 1177 | }; 1178 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1179 | self.shapeBumper = bumper; 1180 | 1181 | return m_path; 1182 | } 1183 | 1184 | - (UIBezierPath *)getPathForBumper:(PBShapeBumper)bumper { 1185 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1186 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1187 | 1188 | return m_path; 1189 | } 1190 | 1191 | - (UIBezierPath *)path4Size:(CGSize)size { 1192 | //bezier path 1193 | 1194 | int m_tmp_direction_h = arc4random() % 2; 1195 | BOOL arrow_left = m_tmp_direction_h == 0; 1196 | int m_h_Bump = arc4random() % 2; 1197 | BOOL m_h_isBump = (m_h_Bump == 0); 1198 | int m_tmp_direction_v = arc4random() % 2; 1199 | BOOL arrow_up = m_tmp_direction_v == 0; 1200 | BOOL m_v_isBump = !m_h_isBump; 1201 | 1202 | int m_radius_scale = 4; 1203 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1204 | int m_avaliable_size = size.width; 1205 | int m_ball_radius = m_avaliable_size/((1+m_ball_cross_scale)+m_radius_scale); 1206 | int m_rectangle_size = m_ball_radius*m_radius_scale; 1207 | int m_rect_radius = m_rectangle_size * 0.5; 1208 | int m_rect_center_y = m_avaliable_size * 0.5; 1209 | int m_rect_center_x = m_avaliable_size * 0.5; 1210 | 1211 | PBShapeBumper bumper = { 1212 | .arrow_up = arrow_up, 1213 | .arrow_left = arrow_left, 1214 | .m_h_isBump = m_h_isBump, 1215 | .m_v_isBump = m_v_isBump, 1216 | .m_ball_radius = m_ball_radius, 1217 | .m_rect_radius = m_rect_radius, 1218 | .m_center = CGPointMake(m_rect_center_x, m_rect_center_y) 1219 | }; 1220 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1221 | 1222 | return m_path; 1223 | } 1224 | 1225 | // Only override drawRect: if you perform custom drawing. 1226 | // An empty implementation adversely affects performance during animation. 1227 | - (void)drawRect:(CGRect)rect { 1228 | // Drawing code 1229 | [self.innerImg drawInRect:rect]; 1230 | 1231 | UIColor *maskColor = [UIColor colorWithWhite:0 alpha:0.7]; 1232 | // maskColor = [UIColor blackColor]; 1233 | //绘制路径 1234 | if (!self.endDetect) { 1235 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 1236 | UIBezierPath *path = [self generateRandomPath]; 1237 | CGContextAddPath(ctx, path.CGPath); 1238 | CGContextSetFillColorWithColor(ctx, maskColor.CGColor); 1239 | CGContextFillPath(ctx); 1240 | } 1241 | } 1242 | 1243 | #pragma mark -- Net Graphic Code -- 1244 | + (NHGraphCoder *)codeWithURL:(NSString *)url { 1245 | return [[NHGraphCoder alloc] initWithURL:url]; 1246 | } 1247 | 1248 | - (id)initWithURL:(NSString *)url { 1249 | NHGraphCoder *coder = [NHGraphCoder new]; 1250 | CGSize tmpSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 1251 | CGRect bounds = (CGRect){ 1252 | .origin = CGPointZero, 1253 | .size = tmpSize 1254 | }; 1255 | [coder setBounds:bounds]; 1256 | // coder.backgroundColor = [UIColor lightGrayColor]; 1257 | if (url == nil) { 1258 | UIImage *img = [UIImage imageNamed:@"coder_default.jpg"]; 1259 | [coder __initSetupImg:img]; 1260 | }else{ 1261 | coder.endDetect = true; 1262 | [coder __initSetup]; 1263 | coder.mFlag.hidden = true; 1264 | coder.slider.userInteractionEnabled = false; 1265 | coder.stater.hidden = false; 1266 | [coder.stater loadingState:true]; 1267 | __weak typeof(coder) weakCoder = coder; 1268 | [[JGAFImageCache sharedInstance] imageForURL:url completion:^(UIImage * _Nullable image) { 1269 | __strong typeof(weakCoder) strongCoder = weakCoder; 1270 | if (image == nil) { 1271 | NSLog(@"下载图片失败!将使用默认图片本地验证"); 1272 | image = [UIImage imageNamed:@"coder_default.jpg"]; 1273 | } 1274 | [strongCoder setImg:image]; 1275 | [strongCoder resetStateForDetect]; 1276 | }]; 1277 | } 1278 | return coder; 1279 | } 1280 | 1281 | @end 1282 | -------------------------------------------------------------------------------- /yanZhengCode/Resources/4.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wsl2ls/YanZhengCode/75b6fc24af7f9bc12ba3a5b22b59a27cdee3ad39/yanZhengCode/Resources/4.JPG -------------------------------------------------------------------------------- /yanZhengCode/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /yanZhengCode/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "CaptchaView.h" 11 | #import "NHGraphCoder.h" 12 | 13 | #define dataStr @"89504E470D0A1A0A0000000D494844520000005A000000320806000000DC0FCC3500000A754944415478DAED998957145716C6F307CC9C93334B124F26C6498C4B0C51A332C6054570478D5BDC22D2B2854541E20682BB44148C10346A44625C70898A062411017103456491455110B0D99A86EA7DA9EAEE6FDE7B0E9E710459CA8C33A6EE39757AA9EAAAAEDFBBEFBBDF7DF51AA4F8AFC46B120209B4045A0A09B4045A022D85045A022DC5EF13B4AA59833AB9020DB54A988C66F0BC403E37A2B2BC0675350AF29D891D6736F1D0EB0C10C87E097407C266B3A1EE910287F79CC39E8863880D3F82C8D038ACF1DD09BF399BE03F6723427C76627D402CC2FC63104ABE0F5FB1077BB71FC3F1B8F3484BCA424D55BD04BABDB05A6DA87DD480E9C39660EC478B31618027260EF08263EF4518DD8B6EAEECFDB88F3DD8AB535F37729C3BC6F65B0CE70F6598D0DF03319B0EFDBE41D36CB55AADED1E575D518B8890FDF84A1681D59E51D810B80B5FAFDC8750BF684CB5F78543CF85709DB00AABBCA2584607B945C07D5A28E6392DC77CE7E558B6682B4A0A1EC062B1FC7E4053B845770B117B301A172EA7C064367648978BF2CA5090731725F9E5B857F410F78A1FE26AEA2DACF28862A003166CC1CF273270E3722172AEDD21AF05484BCEC6992317716CFF79E45E2F86416F7A354153A874FBF730994D389D72122E6E4EE4F54487CE63112CACF0592D56763E9A9966338F86BA26C47DF3131C3E5808994B08327FBD8966A58A15489EEC57715A34D637A3BE4609A58283D1F08A825656D7931B363F055EA3D320F6876F30C4E52396D162C2A033E25C423AD3E269FFF043D2F10C7004F47F0E6ECBB52D16EBAB039A6A6E6D5915D68F74C78F81DB61369A9EBA599D5E07BF500FF41DF32EAEE75E11752D81647AE62F39983CC89B1441197E883DC31C8AD859683693CD642333C3C68A716B03D772EC4B014D2F6CD0E810E71B8E2FDF7286F2517D2B3761C6A7D3FA63E4CC21287D502CFA7A3957EF60F6C840A6D3516BE3515126177DCEE6260BF26FF1C8CDE651562210D9B13CA798DBC8805BA1D7934D47DFDBC4816ED146AEB6119A46EE19B740F70B8280EC9F52E1D36D2C320E24B67A0EA3D1881EC3DE805BD03C343689CFBEBCEC12C8268730D0D485941494BF808CB6206AA31EB39CD598335E83DD910634355ADBB49F4A8580EB9966A49C3593222D741D34BDB85AD18CBCF357707CCDB7C83E91FAD4B461234BF4AFAAB00C9B1C3DB16D6A40ABB68D7E77BFE21E7A3ABC8D88EF36B539F53A1A74E06F679560C9BCCD1845FCF4D2059B917FA394154371A0AD387AC088A90E2AD875E31020D3E2CE6DA14DD065A5021B98E98E1A6C58A1EB3A6893DE88BCE42BD83AC10F7E6F8FC35EF7F568782887CD6A7B02B049DE801F96462078E05C54E6DF6BF506B43A2DF61CDE854F26F5C5B9D433A2A58A3A919CAB4558E9BE1D8E7D16C18378E7EBE979D010B721268C461B2EFD6AC6DC096A0CEEC1C1F3730DD27F31B77A2C4FF4BCB88047B0BF0E1FFE9583D71C4DD741537B252F29C7C1C06DF07D7B3CC95A2FDC3A77890C8081EDD736AB911C7508A143BE40465C223BBE35D0F7CA4BE11A38072E3267DC2ECA110D5AA73520EB523E82BD76B08C96B9ACC185C46B50367022678A0D85B93C7C176831EA23155CA76A907CDAD4C6603F3E76C5975AF47B83C3575E3A711AADE33448DB770AA1F65F600DD92EEE3D091D014C176D2E1D3C87D0A1AE38B3793F83DE961B49387B08F653ED10B26D39E4F58F4483D6A874B89E9687E58BB7B156DC6BC65AFC7C2C83796731B2447F9B9FC363B9B70EA3ECD424B3354822A08DC667E5D0682005398B2799AC455F92D1D15F1BC48116781E85A9D9F89AC847C0FB53703C6C17945575B89598CEE4E2C7659168AE51B47983544FE72F998911330621E96222F406FD0B014D33DA6FCE468C215EDAEBB3B5483E718981160471ED7601711D419E5A221D2ACC9DA8466AB289CCA0D6405B7139CD8C4943D5E8FD270E69E7CDE25D47F59DFBD8316339DCFE300C7B64EB5070210BFEDD2722724A20CBF8E7FDB6C56D78AF76438352FC6A1AB392A476DCC82C84F78C75CC75CC750CC2AF895709E826D1E72E2FE3219BA181DD5B1C164DD3E25A06DFEA712693152989267CF03AC740538B270AB4D54275BA8215BC80F75C881E2F40C8A0F9D8397B25EA1F543FF7B75AD20DEE3E180D87594370ECEC61E20A78F1A04921E69AD448397D05FE733731E9904D09C1D98434B65E2D463AA893A03EDA6396866974A0BB0E39D75B075D27B760DB3A3D03ED43349D6AB6E8864551558B78FFAD5842B2D8EB2F8ED8B5301435A515CFBD29BAEF6ACE658C5F380ACB36F8A1F2D1C317D620D5CB1B9178240DAE13831968DFCF3722F9642614754DCCD38BC9685AFCE64F52C369A09A58363D2ACB5B2FF0B410CE72D6A0CF9F39249D328B6F585A2CDC019F702CFEE3706C76F26619DE1E649ACD2BC303F1E1981E484E3BFBC22053CD97573730D0B34606B0B5696AEFD293B2454B07B57747E38C7019A682FD7B1CD605E9406FF3F1F6B81BD4EBACE0380B297E8FB3D9B1BF8AC9C60B69C1CB738A11F55910C2862E424E62060433DFEEE09C4C4EC0A7D30762499817CAABEEB79B491D0DFAA8AAF27E0DE2634E63BEF30A8CE9EB8665AE5B9FD83B31D2515F6BC1A1EF0D98324285017FE3B054A64106F1D577F204DC220E232DC584E8703D269381E8F726874FBA73D8FB8D81796AD1A0A9374E894E80C7EB0E488949004F9A85F66E866AF1E8D9F6E83FBE17D2AEA5C2CC9B9F6C2D4BA806A3812D3459AC964EC1A12B7165C595F86E6B02C6D9B9B362B8CA3312D99905501337D291070A6D05CDD6ACCB3C9C07710CE4F03E2A2C98AC81DB740D663AA931CE5E85B18354F024CD092D805346AA588BDEF2FFBB0C9A3799703BE932D60D77C38EE94190973EECD0D48E3FF13DFA39BF87155B029057940B79DD233CAC2E4705D9CA482B7EF77E09F24B6E13DDAEE83418AD5A8FBCEC526C0F8D83CB601F4C1AE88DB5FE310C342D92620AA1C05B515723E0D84103962CD2B2A2E83D4FCB7CF5E6603D4E1E36A1E40E8F8BC4CA9D3A622499CEB32647F4EA9DA2B206E1CE3EF0796B2CF3D2ED49065B2B3618603FC50E23660EC6966FD723FEF83E1C3CB91F7B0E7D8B6DDF6DC6A6E83084C7AE47E4DE70D4D4C93B0D9A7AE8DCAC62F640D6A99F8C48870C9161F128B879974C61A1CBD2D1A2C3141CD764C1BD120105A4E0DD2D16D0A8B03E05F4F1AC7D7629B54BA0D58DCD38B5712F96BE3B0927427741ABE4DAD5594EC511805BD17BF43B184E1A94B1F347B285FE3E8EDD31745A7F067FB04B3FF41AF50E568707A159D5D4C942480A7323C79E70AFF088C4E44FBE642B78713B7FC283D22AE6AF5FD4D3218B607B06EE6FB2F05F947E933525BB89955356D7B59B79F4CFA5133D1E38B137930DBB713DF1F7E16FB266A517016F3FF56306F9FD11DDF0F1B80F70ABE02678A1F3BE9A3E9AA2CF01A337FE8888E0FD3875E8026A880B1193CD2FE5090BFDB35585F711332F181B1C1633E01DF94D4DBD9C69337519CB36F8222C7215228854441F882292B185BD8F89DF817D4777E3E2D50B50A93BBF0044F55FAF3332F9F85F8C4E81D6ABB4381E1A8BD5FD3F47EEB94C56103B025AD1A460454FB008FFFADCC83E3771CA274E84B6E08DCD0ABCAAD161D0B4DDD6ABB55011E36FD4E8BB64955EF6F4FDBFC9682924D012682924D0126809B414126809B414126809B4045A0A09B4045A0A1AFF049DE45661B57926D90000000049454E44AE426082" 14 | 15 | 16 | @interface ViewController () 17 | { 18 | CaptchaView * _captchaView1; 19 | CaptchaView * _captchaView2; 20 | } 21 | 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | //字符串验证码界面 30 | _captchaView1 = [[CaptchaView alloc] initWithFrame:CGRectMake(20, 40, 150, 40) WithType:DefaultType]; 31 | [self.view addSubview:_captchaView1]; 32 | 33 | //算数验证码 34 | _captchaView2 = [[CaptchaView alloc] initWithFrame:CGRectMake(20, 90, 150, 40) WithType:CountType]; 35 | [self.view addSubview:_captchaView2]; 36 | 37 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(190, 40, 100, 40)]; 38 | label.text = @"点击图片换验证码"; 39 | label.font = [UIFont systemFontOfSize:12]; 40 | label.textColor = [UIColor grayColor]; 41 | [self.view addSubview:label]; 42 | 43 | 44 | //图片验证码 45 | UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 150, 90, 40)]; 46 | [self.view addSubview:imageView]; 47 | NSData * data = [self transStrHexToData:dataStr]; 48 | UIImage * image = [UIImage imageWithData:data]; 49 | imageView.image = image; 50 | NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); 51 | 52 | 53 | //滑块验证码 54 | //网络图片 55 | // NSString *url = @"http://pic.pimg.tw/loloto/1357207442-1350656755_l.jpg?v=1357207447"; 56 | // NHGraphCoder *coder = [NHGraphCoder codeWithURL:url]; 57 | 58 | //本地图片 59 | UIImage * image1 = [UIImage imageNamed:@"4.jpg"]; 60 | NHGraphCoder *coder = [NHGraphCoder codeWithImage:image1]; 61 | coder.center = CGPointMake(self.view.frame.size.width*0.5, self.view.frame.size.height*0.5 + 30); 62 | [coder handleGraphicCoderVerifyEvent:^(NHGraphCoder * _Nonnull cd, BOOL success) { 63 | 64 | NSString * resultStr ; 65 | if (success == 1) { 66 | resultStr = @"验证成功"; 67 | }else{ 68 | resultStr = @"验证失败"; 69 | CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"]; 70 | anim.repeatCount = 10; 71 | anim.values = @[@-20, @20, @-20]; 72 | [coder.layer addAnimation:anim forKey:nil]; 73 | [coder resetStateForDetect]; 74 | } 75 | 76 | UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"提示" message:resultStr preferredStyle:UIAlertControllerStyleAlert]; 77 | [self presentViewController:alertController animated:NO completion:nil]; 78 | [UIView animateWithDuration:2 animations:^{ 79 | [alertController dismissViewControllerAnimated:NO completion:nil]; 80 | }]; 81 | 82 | }]; 83 | [self.view addSubview:coder]; 84 | } 85 | 86 | /// 将服务器端传过来的十六进制的字符串转化为NSData 87 | - (NSData *)transStrHexToData:(NSString *)strHex 88 | { 89 | // bytes索引 90 | NSUInteger j = 0; 91 | 92 | NSInteger len = strHex.length; 93 | 94 | if (len % 2 == 1) { 95 | /// 若不能被2整除则说明16进制的字符串不满足图片图。特此说明,假如只是单纯的把十六进制转换为NSData就把这个if干掉即可, 96 | return nil; 97 | } 98 | 99 | // 动态分配内存 100 | Byte *bytes = (Byte *)malloc((len / 2 + 1) * sizeof(Byte)); 101 | 102 | // 初始化内存 其中memset的作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 103 | memset(bytes, '\0', (len / 2 + 1) * sizeof(Byte)); 104 | 105 | // for循环里面其实就是把16进制的字符串转化为字节数组的过程 106 | for (NSUInteger i = 0; i < strHex.length; i += 2) { 107 | 108 | // 一字节byte是8位(比特)bit 一位就代表一个0或者1(即二进制) 每8位(bit)组成一个字节(Byte) 所以每一次取2为字符组合成一个字节 其实就是2个16进制的字符其实就是8位(bit)即一个字节byte 109 | NSString *str = [strHex substringWithRange:NSMakeRange(i, 2)]; 110 | 111 | // 将16进制字符串转化为十进制 112 | unsigned long uint_ch = strtoul([str UTF8String], 0, 16); 113 | 114 | bytes[j] = uint_ch; 115 | 116 | /// 自增 117 | j ++; 118 | } 119 | 120 | // 将字节数组转化为NSData 121 | NSData *data = [[NSData alloc] initWithBytes:bytes length:len / 2]; 122 | 123 | // 释放内存 124 | free(bytes); 125 | 126 | return data; 127 | 128 | } 129 | 130 | 131 | 132 | 133 | - (void)didReceiveMemoryWarning { 134 | [super didReceiveMemoryWarning]; 135 | // Dispose of any resources that can be recreated. 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /yanZhengCode/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // yanZhengCode 4 | // 5 | // Created by 王双龙 on 16/8/3. 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 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmering-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmering.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014-present, Facebook, Inc. 3 | All rights reserved. 4 | 5 | This source code is licensed under the BSD-style license found in the 6 | LICENSE file in the root directory of this source tree. An additional grant 7 | of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | typedef NS_ENUM(NSInteger, FBShimmerDirection) { 14 | //! Shimmer animation goes from left to right 15 | FBShimmerDirectionRight, 16 | //! Shimmer animation goes from right to left 17 | FBShimmerDirectionLeft, 18 | //! Shimmer animation goes from below to above 19 | FBShimmerDirectionUp, 20 | //! Shimmer animation goes from above to below 21 | FBShimmerDirectionDown, 22 | }; 23 | 24 | static const float FBShimmerDefaultBeginTime = CGFLOAT_MAX; 25 | 26 | @protocol FBShimmering 27 | 28 | //! @abstract Set this to YES to start shimming and NO to stop. Defaults to NO. 29 | @property (assign, nonatomic, readwrite, getter = isShimmering) BOOL shimmering; 30 | 31 | //! @abstract The time interval between shimmerings in seconds. Defaults to 0.4. 32 | @property (assign, nonatomic, readwrite) CFTimeInterval shimmeringPauseDuration; 33 | 34 | //! @abstract The opacity of the content while it is shimmering. Defaults to 0.5. 35 | @property (assign, nonatomic, readwrite) CGFloat shimmeringAnimationOpacity; 36 | 37 | //! @abstract The opacity of the content before it is shimmering. Defaults to 1.0. 38 | @property (assign, nonatomic, readwrite) CGFloat shimmeringOpacity; 39 | 40 | //! @abstract The speed of shimmering, in points per second. Defaults to 230. 41 | @property (assign, nonatomic, readwrite) CGFloat shimmeringSpeed; 42 | 43 | //! @abstract The highlight length of shimmering. Range of [0,1], defaults to 1.0. 44 | @property (assign, nonatomic, readwrite) CGFloat shimmeringHighlightLength; 45 | 46 | //! @abstract Same as "shimmeringHighlightLength", just for downward compatibility. @deprecated 47 | @property (assign, nonatomic, readwrite, getter = shimmeringHighlightLength, setter = setShimmeringHighlightLength:) CGFloat shimmeringHighlightWidth DEPRECATED_MSG_ATTRIBUTE("Use shimmeringHighlightLength"); 48 | 49 | //! @abstract The direction of shimmering animation. Defaults to FBShimmerDirectionRight. 50 | @property (assign, nonatomic, readwrite) FBShimmerDirection shimmeringDirection; 51 | 52 | //! @abstract The duration of the fade used when shimmer begins. Defaults to 0.1. 53 | @property (assign, nonatomic, readwrite) CFTimeInterval shimmeringBeginFadeDuration; 54 | 55 | //! @abstract The duration of the fade used when shimmer ends. Defaults to 0.3. 56 | @property (assign, nonatomic, readwrite) CFTimeInterval shimmeringEndFadeDuration; 57 | 58 | /** 59 | @abstract The absolute CoreAnimation media time when the shimmer will fade in. 60 | @discussion Only valid after setting {@ref shimmering} to NO. 61 | */ 62 | @property (assign, nonatomic, readonly) CFTimeInterval shimmeringFadeTime; 63 | 64 | /** 65 | @abstract The absolute CoreAnimation media time when the shimmer will begin. 66 | @discussion Only valid after setting {@ref shimmering} to YES. 67 | */ 68 | @property (assign, nonatomic) CFTimeInterval shimmeringBeginTime; 69 | 70 | @end 71 | 72 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmeringLayer.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014-present, Facebook, Inc. 3 | All rights reserved. 4 | 5 | This source code is licensed under the BSD-style license found in the 6 | LICENSE file in the root directory of this source tree. An additional grant 7 | of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "FBShimmering.h" 13 | 14 | /** 15 | @abstract Lightweight, generic shimmering layer. 16 | */ 17 | @interface FBShimmeringLayer : CALayer 18 | 19 | //! @abstract The content layer to be shimmered. 20 | @property (strong, nonatomic) CALayer *contentLayer; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmeringLayer.m: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014-present, Facebook, Inc. 3 | All rights reserved. 4 | 5 | This source code is licensed under the BSD-style license found in the 6 | LICENSE file in the root directory of this source tree. An additional grant 7 | of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "FBShimmeringLayer.h" 11 | 12 | #import 13 | #import 14 | #import 15 | 16 | #import 17 | #import 18 | 19 | #if !__has_feature(objc_arc) 20 | #error This file must be compiled with ARC. Convert your project to ARC or specify the -fobjc-arc flag. 21 | #endif 22 | 23 | #if TARGET_IPHONE_SIMULATOR 24 | UIKIT_EXTERN float UIAnimationDragCoefficient(void); // UIKit private drag coeffient, use judiciously 25 | #endif 26 | 27 | static CGFloat FBShimmeringLayerDragCoefficient(void) 28 | { 29 | #if TARGET_IPHONE_SIMULATOR 30 | return UIAnimationDragCoefficient(); 31 | #else 32 | return 1.0; 33 | #endif 34 | } 35 | 36 | static void FBShimmeringLayerAnimationApplyDragCoefficient(CAAnimation *animation) 37 | { 38 | CGFloat k = FBShimmeringLayerDragCoefficient(); 39 | 40 | if (k != 0 && k != 1) { 41 | animation.speed = 1 / k; 42 | } 43 | } 44 | 45 | // animations keys 46 | static NSString *const kFBShimmerSlideAnimationKey = @"slide"; 47 | static NSString *const kFBFadeAnimationKey = @"fade"; 48 | static NSString *const kFBEndFadeAnimationKey = @"fade-end"; 49 | 50 | static CABasicAnimation *fade_animation(CALayer *layer, CGFloat opacity, CFTimeInterval duration) 51 | { 52 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; 53 | animation.fromValue = @([(layer.presentationLayer ?: layer) opacity]); 54 | animation.toValue = @(opacity); 55 | animation.fillMode = kCAFillModeBoth; 56 | animation.removedOnCompletion = NO; 57 | animation.duration = duration; 58 | FBShimmeringLayerAnimationApplyDragCoefficient(animation); 59 | return animation; 60 | } 61 | 62 | static CABasicAnimation *shimmer_slide_animation(CFTimeInterval duration, FBShimmerDirection direction) 63 | { 64 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 65 | animation.toValue = [NSValue valueWithCGPoint:CGPointZero]; 66 | animation.duration = duration; 67 | animation.repeatCount = HUGE_VALF; 68 | FBShimmeringLayerAnimationApplyDragCoefficient(animation); 69 | if (direction == FBShimmerDirectionLeft || 70 | direction == FBShimmerDirectionUp) { 71 | animation.speed = -fabsf(animation.speed); 72 | } 73 | return animation; 74 | } 75 | 76 | // take a shimmer slide animation and turns into repeating 77 | static CAAnimation *shimmer_slide_repeat(CAAnimation *a, CFTimeInterval duration, FBShimmerDirection direction) 78 | { 79 | CAAnimation *anim = [a copy]; 80 | anim.repeatCount = HUGE_VALF; 81 | anim.duration = duration; 82 | anim.speed = (direction == FBShimmerDirectionRight || direction == FBShimmerDirectionDown) ? fabsf(anim.speed) : -fabsf(anim.speed); 83 | return anim; 84 | } 85 | 86 | // take a shimmer slide animation and turns into finish 87 | static CAAnimation *shimmer_slide_finish(CAAnimation *a) 88 | { 89 | CAAnimation *anim = [a copy]; 90 | anim.repeatCount = 0; 91 | return anim; 92 | } 93 | 94 | @interface FBShimmeringMaskLayer : CAGradientLayer 95 | @property (readonly, nonatomic) CALayer *fadeLayer; 96 | @end 97 | 98 | @implementation FBShimmeringMaskLayer 99 | 100 | - (instancetype)init 101 | { 102 | self = [super init]; 103 | if (nil != self) { 104 | _fadeLayer = [[CALayer alloc] init]; 105 | _fadeLayer.backgroundColor = [UIColor whiteColor].CGColor; 106 | [self addSublayer:_fadeLayer]; 107 | } 108 | return self; 109 | } 110 | 111 | - (void)layoutSublayers 112 | { 113 | [super layoutSublayers]; 114 | CGRect r = self.bounds; 115 | _fadeLayer.bounds = r; 116 | _fadeLayer.position = CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r)); 117 | } 118 | 119 | @end 120 | 121 | @interface FBShimmeringLayer () 122 | @property (strong, nonatomic) FBShimmeringMaskLayer *maskLayer; 123 | @end 124 | 125 | @implementation FBShimmeringLayer 126 | { 127 | CALayer *_contentLayer; 128 | FBShimmeringMaskLayer *_maskLayer; 129 | } 130 | 131 | #pragma mark - Lifecycle 132 | 133 | @synthesize shimmering = _shimmering; 134 | @synthesize shimmeringPauseDuration = _shimmeringPauseDuration; 135 | @synthesize shimmeringAnimationOpacity = _shimmeringAnimationOpacity; 136 | @synthesize shimmeringOpacity = _shimmeringOpacity; 137 | @synthesize shimmeringSpeed = _shimmeringSpeed; 138 | @synthesize shimmeringHighlightLength = _shimmeringHighlightLength; 139 | @synthesize shimmeringDirection = _shimmeringDirection; 140 | @synthesize shimmeringFadeTime = _shimmeringFadeTime; 141 | @synthesize shimmeringBeginFadeDuration = _shimmeringBeginFadeDuration; 142 | @synthesize shimmeringEndFadeDuration = _shimmeringEndFadeDuration; 143 | @synthesize shimmeringBeginTime = _shimmeringBeginTime; 144 | @dynamic shimmeringHighlightWidth; 145 | 146 | - (instancetype)init 147 | { 148 | self = [super init]; 149 | if (nil != self) { 150 | // default configuration 151 | _shimmeringPauseDuration = 0.4; 152 | _shimmeringSpeed = 230.0; 153 | _shimmeringHighlightLength = 1.0; 154 | _shimmeringAnimationOpacity = 0.5; 155 | _shimmeringOpacity = 1.0; 156 | _shimmeringDirection = FBShimmerDirectionRight; 157 | _shimmeringBeginFadeDuration = 0.1; 158 | _shimmeringEndFadeDuration = 0.3; 159 | _shimmeringBeginTime = FBShimmerDefaultBeginTime; 160 | } 161 | return self; 162 | } 163 | 164 | #pragma mark - Properties 165 | 166 | - (void)setContentLayer:(CALayer *)contentLayer 167 | { 168 | // reset mask 169 | self.maskLayer = nil; 170 | 171 | // note content layer and add for display 172 | _contentLayer = contentLayer; 173 | self.sublayers = contentLayer ? @[contentLayer] : nil; 174 | 175 | // update shimmering animation 176 | [self _updateShimmering]; 177 | } 178 | 179 | - (void)setShimmering:(BOOL)shimmering 180 | { 181 | if (shimmering != _shimmering) { 182 | _shimmering = shimmering; 183 | [self _updateShimmering]; 184 | } 185 | } 186 | 187 | - (void)setShimmeringSpeed:(CGFloat)speed 188 | { 189 | if (speed != _shimmeringSpeed) { 190 | _shimmeringSpeed = speed; 191 | [self _updateShimmering]; 192 | } 193 | } 194 | 195 | - (void)setShimmeringHighlightLength:(CGFloat)length 196 | { 197 | if (length != _shimmeringHighlightLength) { 198 | _shimmeringHighlightLength = length; 199 | [self _updateShimmering]; 200 | } 201 | } 202 | 203 | - (void)setShimmeringDirection:(FBShimmerDirection)direction 204 | { 205 | if (direction != _shimmeringDirection) { 206 | _shimmeringDirection = direction; 207 | [self _updateShimmering]; 208 | } 209 | } 210 | 211 | - (void)setShimmeringPauseDuration:(CFTimeInterval)duration 212 | { 213 | if (duration != _shimmeringPauseDuration) { 214 | _shimmeringPauseDuration = duration; 215 | [self _updateShimmering]; 216 | } 217 | } 218 | 219 | - (void)setShimmeringAnimationOpacity:(CGFloat)shimmeringAnimationOpacity 220 | { 221 | if (shimmeringAnimationOpacity != _shimmeringAnimationOpacity) { 222 | _shimmeringAnimationOpacity = shimmeringAnimationOpacity; 223 | [self _updateMaskColors]; 224 | } 225 | } 226 | 227 | - (void)setShimmeringOpacity:(CGFloat)shimmeringOpacity 228 | { 229 | if (shimmeringOpacity != _shimmeringOpacity) { 230 | _shimmeringOpacity = shimmeringOpacity; 231 | [self _updateMaskColors]; 232 | } 233 | } 234 | 235 | - (void)setShimmeringBeginTime:(CFTimeInterval)beginTime 236 | { 237 | if (beginTime != _shimmeringBeginTime) { 238 | _shimmeringBeginTime = beginTime; 239 | [self _updateShimmering]; 240 | } 241 | } 242 | 243 | - (void)layoutSublayers 244 | { 245 | [super layoutSublayers]; 246 | CGRect r = self.bounds; 247 | _contentLayer.anchorPoint = CGPointMake(0.5, 0.5); 248 | _contentLayer.bounds = r; 249 | _contentLayer.position = CGPointMake(CGRectGetMidX(r), CGRectGetMidY(r)); 250 | 251 | if (nil != _maskLayer) { 252 | [self _updateMaskLayout]; 253 | } 254 | } 255 | 256 | - (void)setBounds:(CGRect)bounds 257 | { 258 | CGRect oldBounds = self.bounds; 259 | [super setBounds:bounds]; 260 | 261 | if (!CGRectEqualToRect(oldBounds, bounds)) { 262 | [self _updateShimmering]; 263 | } 264 | } 265 | 266 | #pragma mark - Internal 267 | 268 | - (void)_clearMask 269 | { 270 | if (nil == _maskLayer) { 271 | return; 272 | } 273 | 274 | BOOL disableActions = [CATransaction disableActions]; 275 | [CATransaction setDisableActions:YES]; 276 | 277 | self.maskLayer = nil; 278 | _contentLayer.mask = nil; 279 | 280 | [CATransaction setDisableActions:disableActions]; 281 | } 282 | 283 | - (void)_createMaskIfNeeded 284 | { 285 | if (_shimmering && !_maskLayer) { 286 | _maskLayer = [FBShimmeringMaskLayer layer]; 287 | _maskLayer.delegate = self; 288 | _contentLayer.mask = _maskLayer; 289 | [self _updateMaskColors]; 290 | [self _updateMaskLayout]; 291 | } 292 | } 293 | 294 | - (void)_updateMaskColors 295 | { 296 | if (nil == _maskLayer) { 297 | return; 298 | } 299 | 300 | // We create a gradient to be used as a mask. 301 | // In a mask, the colors do not matter, it's the alpha that decides the degree of masking. 302 | UIColor *maskedColor = [UIColor colorWithWhite:1.0 alpha:_shimmeringOpacity]; 303 | UIColor *unmaskedColor = [UIColor colorWithWhite:1.0 alpha:_shimmeringAnimationOpacity]; 304 | 305 | // Create a gradient from masked to unmasked to masked. 306 | _maskLayer.colors = @[(__bridge id)maskedColor.CGColor, (__bridge id)unmaskedColor.CGColor, (__bridge id)maskedColor.CGColor]; 307 | } 308 | 309 | - (void)_updateMaskLayout 310 | { 311 | // Everything outside the mask layer is hidden, so we need to create a mask long enough for the shimmered layer to be always covered by the mask. 312 | CGFloat length = 0.0f; 313 | if (_shimmeringDirection == FBShimmerDirectionDown || 314 | _shimmeringDirection == FBShimmerDirectionUp) { 315 | length = CGRectGetHeight(_contentLayer.bounds); 316 | } else { 317 | length = CGRectGetWidth(_contentLayer.bounds); 318 | } 319 | if (0 == length) { 320 | return; 321 | } 322 | 323 | // extra distance for the gradient to travel during the pause. 324 | CGFloat extraDistance = length + _shimmeringSpeed * _shimmeringPauseDuration; 325 | 326 | // compute how far the shimmering goes 327 | CGFloat fullShimmerLength = length * 3.0f + extraDistance; 328 | CGFloat travelDistance = length * 2.0f + extraDistance; 329 | 330 | // position the gradient for the desired width 331 | CGFloat highlightOutsideLength = (1.0 - _shimmeringHighlightLength) / 2.0; 332 | _maskLayer.locations = @[@(highlightOutsideLength), 333 | @(0.5), 334 | @(1.0 - highlightOutsideLength)]; 335 | 336 | CGFloat startPoint = (length + extraDistance) / fullShimmerLength; 337 | CGFloat endPoint = travelDistance / fullShimmerLength; 338 | 339 | // position for the start of the animation 340 | _maskLayer.anchorPoint = CGPointZero; 341 | if (_shimmeringDirection == FBShimmerDirectionDown || 342 | _shimmeringDirection == FBShimmerDirectionUp) { 343 | _maskLayer.startPoint = CGPointMake(0.0, startPoint); 344 | _maskLayer.endPoint = CGPointMake(0.0, endPoint); 345 | _maskLayer.position = CGPointMake(0.0, -travelDistance); 346 | _maskLayer.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(_contentLayer.bounds), fullShimmerLength); 347 | } else { 348 | _maskLayer.startPoint = CGPointMake(startPoint, 0.0); 349 | _maskLayer.endPoint = CGPointMake(endPoint, 0.0); 350 | _maskLayer.position = CGPointMake(-travelDistance, 0.0); 351 | _maskLayer.bounds = CGRectMake(0.0, 0.0, fullShimmerLength, CGRectGetHeight(_contentLayer.bounds)); 352 | } 353 | } 354 | 355 | - (void)_updateShimmering 356 | { 357 | // create mask if needed 358 | [self _createMaskIfNeeded]; 359 | 360 | // if not shimmering and no mask, noop 361 | if (!_shimmering && !_maskLayer) { 362 | return; 363 | } 364 | 365 | // ensure layout 366 | [self layoutIfNeeded]; 367 | 368 | BOOL disableActions = [CATransaction disableActions]; 369 | if (!_shimmering) { 370 | if (disableActions) { 371 | // simply remove mask 372 | [self _clearMask]; 373 | } else { 374 | // end slide 375 | CFTimeInterval slideEndTime = 0; 376 | 377 | CAAnimation *slideAnimation = [_maskLayer animationForKey:kFBShimmerSlideAnimationKey]; 378 | if (slideAnimation != nil) { 379 | 380 | // determine total time sliding 381 | CFTimeInterval now = CACurrentMediaTime(); 382 | CFTimeInterval slideTotalDuration = now - slideAnimation.beginTime; 383 | 384 | // determine time offset into current slide 385 | CFTimeInterval slideTimeOffset = fmod(slideTotalDuration, slideAnimation.duration); 386 | 387 | // transition to non-repeating slide 388 | CAAnimation *finishAnimation = shimmer_slide_finish(slideAnimation); 389 | 390 | // adjust begin time to now - offset 391 | finishAnimation.beginTime = now - slideTimeOffset; 392 | 393 | // note slide end time and begin 394 | slideEndTime = finishAnimation.beginTime + slideAnimation.duration; 395 | [_maskLayer addAnimation:finishAnimation forKey:kFBShimmerSlideAnimationKey]; 396 | } 397 | 398 | // fade in text at slideEndTime 399 | CABasicAnimation *fadeInAnimation = fade_animation(_maskLayer.fadeLayer, 1.0, _shimmeringEndFadeDuration); 400 | fadeInAnimation.delegate = self; 401 | [fadeInAnimation setValue:@YES forKey:kFBEndFadeAnimationKey]; 402 | fadeInAnimation.beginTime = slideEndTime; 403 | [_maskLayer.fadeLayer addAnimation:fadeInAnimation forKey:kFBFadeAnimationKey]; 404 | 405 | // expose end time for synchronization 406 | _shimmeringFadeTime = slideEndTime; 407 | } 408 | } else { 409 | // fade out text, optionally animated 410 | CABasicAnimation *fadeOutAnimation = nil; 411 | if (_shimmeringBeginFadeDuration > 0.0 && !disableActions) { 412 | fadeOutAnimation = fade_animation(_maskLayer.fadeLayer, 0.0, _shimmeringBeginFadeDuration); 413 | [_maskLayer.fadeLayer addAnimation:fadeOutAnimation forKey:kFBFadeAnimationKey]; 414 | } else { 415 | BOOL innerDisableActions = [CATransaction disableActions]; 416 | [CATransaction setDisableActions:YES]; 417 | 418 | _maskLayer.fadeLayer.opacity = 0.0; 419 | [_maskLayer.fadeLayer removeAllAnimations]; 420 | 421 | [CATransaction setDisableActions:innerDisableActions]; 422 | } 423 | 424 | // begin slide animation 425 | CAAnimation *slideAnimation = [_maskLayer animationForKey:kFBShimmerSlideAnimationKey]; 426 | 427 | // compute shimmer duration 428 | CGFloat length = 0.0f; 429 | if (_shimmeringDirection == FBShimmerDirectionDown || 430 | _shimmeringDirection == FBShimmerDirectionUp) { 431 | length = CGRectGetHeight(_contentLayer.bounds); 432 | } else { 433 | length = CGRectGetWidth(_contentLayer.bounds); 434 | } 435 | CFTimeInterval animationDuration = (length / _shimmeringSpeed) + _shimmeringPauseDuration; 436 | 437 | if (slideAnimation != nil) { 438 | // ensure existing slide animation repeats 439 | [_maskLayer addAnimation:shimmer_slide_repeat(slideAnimation, animationDuration, _shimmeringDirection) forKey:kFBShimmerSlideAnimationKey]; 440 | } else { 441 | // add slide animation 442 | slideAnimation = shimmer_slide_animation(animationDuration, _shimmeringDirection); 443 | slideAnimation.fillMode = kCAFillModeForwards; 444 | slideAnimation.removedOnCompletion = NO; 445 | if (_shimmeringBeginTime == FBShimmerDefaultBeginTime) { 446 | _shimmeringBeginTime = CACurrentMediaTime() + fadeOutAnimation.duration; 447 | } 448 | slideAnimation.beginTime = _shimmeringBeginTime; 449 | 450 | [_maskLayer addAnimation:slideAnimation forKey:kFBShimmerSlideAnimationKey]; 451 | } 452 | } 453 | } 454 | 455 | #pragma mark - CALayerDelegate 456 | 457 | - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event 458 | { 459 | // no associated actions 460 | return (id)kCFNull; 461 | } 462 | 463 | #pragma mark - CAAnimationDelegate 464 | 465 | - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 466 | { 467 | if (flag && [[anim valueForKey:kFBEndFadeAnimationKey] boolValue]) { 468 | [_maskLayer.fadeLayer removeAnimationForKey:kFBFadeAnimationKey]; 469 | 470 | [self _clearMask]; 471 | } 472 | } 473 | 474 | @end 475 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmeringView.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014-present, Facebook, Inc. 3 | All rights reserved. 4 | 5 | This source code is licensed under the BSD-style license found in the 6 | LICENSE file in the root directory of this source tree. An additional grant 7 | of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "FBShimmering.h" 13 | 14 | /** 15 | @abstract Lightweight, generic shimmering view. 16 | */ 17 | @interface FBShimmeringView : UIView 18 | 19 | //! @abstract The content view to be shimmered. 20 | @property (strong, nonatomic) UIView *contentView; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/FBShimmering/FBShimmeringView.m: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2014-present, Facebook, Inc. 3 | All rights reserved. 4 | 5 | This source code is licensed under the BSD-style license found in the 6 | LICENSE file in the root directory of this source tree. An additional grant 7 | of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "FBShimmeringView.h" 11 | 12 | #import "FBShimmeringLayer.h" 13 | 14 | #if !__has_feature(objc_arc) 15 | #error This file must be compiled with ARC. Convert your project to ARC or specify the -fobjc-arc flag. 16 | #endif 17 | 18 | @implementation FBShimmeringView 19 | 20 | + (Class)layerClass 21 | { 22 | return [FBShimmeringLayer class]; 23 | } 24 | 25 | #define __layer ((FBShimmeringLayer *)self.layer) 26 | 27 | #define LAYER_ACCESSOR(accessor, ctype) \ 28 | - (ctype)accessor { \ 29 | return [__layer accessor]; \ 30 | } 31 | 32 | #define LAYER_MUTATOR(mutator, ctype) \ 33 | - (void)mutator (ctype)value { \ 34 | [__layer mutator value]; \ 35 | } 36 | 37 | #define LAYER_RW_PROPERTY(accessor, mutator, ctype) \ 38 | LAYER_ACCESSOR (accessor, ctype) \ 39 | LAYER_MUTATOR (mutator, ctype) 40 | 41 | LAYER_RW_PROPERTY(isShimmering, setShimmering:, BOOL) 42 | LAYER_RW_PROPERTY(shimmeringPauseDuration, setShimmeringPauseDuration:, CFTimeInterval) 43 | LAYER_RW_PROPERTY(shimmeringAnimationOpacity, setShimmeringAnimationOpacity:, CGFloat) 44 | LAYER_RW_PROPERTY(shimmeringOpacity, setShimmeringOpacity:, CGFloat) 45 | LAYER_RW_PROPERTY(shimmeringSpeed, setShimmeringSpeed:, CGFloat) 46 | LAYER_RW_PROPERTY(shimmeringHighlightLength, setShimmeringHighlightLength:, CGFloat) 47 | LAYER_RW_PROPERTY(shimmeringDirection, setShimmeringDirection:, FBShimmerDirection) 48 | LAYER_ACCESSOR(shimmeringFadeTime, CFTimeInterval) 49 | LAYER_RW_PROPERTY(shimmeringBeginFadeDuration, setShimmeringBeginFadeDuration:, CFTimeInterval) 50 | LAYER_RW_PROPERTY(shimmeringEndFadeDuration, setShimmeringEndFadeDuration:, CFTimeInterval) 51 | LAYER_RW_PROPERTY(shimmeringBeginTime, setShimmeringBeginTime:, CFTimeInterval) 52 | 53 | - (void)setContentView:(UIView *)contentView 54 | { 55 | if (contentView != _contentView) { 56 | _contentView = contentView; 57 | [self addSubview:contentView]; 58 | __layer.contentLayer = contentView.layer; 59 | } 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/JGAFImageCache/JGAFImageCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGAFImageCache.h 3 | // JGAFImageCache 4 | // 5 | // Created by Jamin Guy on 3/28/13. 6 | // Copyright (c) 2013 Jamin Guy. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface JGAFImageCache : NSObject 13 | 14 | #ifndef JGAFImageCache_LOGGING_ENABLED 15 | #define JGAFImageCache_LOGGING_ENABLED 0 16 | #endif 17 | 18 | // When the app enters the background extra time will be requested within which to 19 | // delete files from disk that are older than this number in seconds. 20 | // Default is 7 days: -604800 21 | #define JGAFImageCache_DEFAULT_EXPIRATION_INTERVAL -604800 22 | @property (assign, nonatomic) NSTimeInterval fileExpirationInterval; 23 | 24 | // If the http response status code is not in the 400-499 range a retry can be initiated 25 | // Default: 0 26 | @property (assign, nonatomic) NSInteger maxNumberOfRetries; 27 | // Number of seconds to wait between retries 28 | // Default 0.0 29 | @property (assign, nonatomic) NSTimeInterval retryDelay; 30 | 31 | + (nonnull JGAFImageCache *)sharedInstance; 32 | - (void)imageForURL:(nonnull NSString *)url completion:(void (^ __nullable)( UIImage * __nullable image))completion; 33 | - (void)clearAllData; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /yanZhengCode/vendor/JGAFImageCache/JGAFImageCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGAFImageCache.m 3 | // JGAFImageCache 4 | // 5 | // Created by Jamin Guy on 3/28/13. 6 | // Copyright (c) 2013 Jamin Guy. All rights reserved. 7 | // 8 | 9 | #import "JGAFImageCache.h" 10 | 11 | #import 12 | #import 13 | 14 | #pragma mark - NSString (JGAFSHA1) 15 | 16 | @interface NSString (JGAFSHA1) 17 | 18 | - (NSString*)jgaf_sha1; 19 | 20 | @end 21 | 22 | @implementation NSString (JGAFSHA1) 23 | 24 | /* See here for more info: Getting MD5 and SHA-1 http://stackoverflow.com/a/6006747/648774 */ 25 | 26 | - (NSString *)jgaf_sha1 { 27 | NSMutableString *mutableSHA1 = nil; 28 | NSData *data = [self dataUsingEncoding: NSUTF8StringEncoding]; /* A different encoding can be used here. */ 29 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 30 | if(CC_SHA1(data.bytes, (CC_LONG)data.length, digest)) { 31 | mutableSHA1 = [[NSMutableString alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; 32 | for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { 33 | [mutableSHA1 appendFormat:@"%02x", digest[i]]; 34 | } 35 | } 36 | return mutableSHA1; 37 | } 38 | 39 | @end 40 | 41 | 42 | #pragma mark - JGAFImageCache 43 | 44 | @interface JGAFImageCache () 45 | 46 | @property (strong, nonatomic) NSCache *imageCache; 47 | @property (strong, nonatomic) NSURLSession *urlSession; 48 | 49 | @end 50 | 51 | @implementation JGAFImageCache 52 | 53 | + (JGAFImageCache *)sharedInstance { 54 | static id sharedID; 55 | static dispatch_once_t onceToken; 56 | dispatch_once(&onceToken, ^{ 57 | sharedID = [[self alloc] init]; 58 | }); 59 | return sharedID; 60 | } 61 | 62 | - (id)init { 63 | self = [super init]; 64 | if(self) { 65 | _fileExpirationInterval = JGAFImageCache_DEFAULT_EXPIRATION_INTERVAL; 66 | _imageCache = [[NSCache alloc] init]; 67 | _maxNumberOfRetries = 0; 68 | _retryDelay = 0.0; 69 | 70 | NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration]; 71 | NSURLSession *urlSession = [NSURLSession sessionWithConfiguration:sessionConfiguration]; 72 | _urlSession = urlSession; 73 | 74 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; 75 | __weak JGAFImageCache *weakSelf = self; 76 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { 77 | [weakSelf.imageCache removeAllObjects]; 78 | }]; 79 | } 80 | return self; 81 | } 82 | 83 | - (void)dealloc { 84 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 85 | } 86 | 87 | - (void)applicationDidEnterBackground:(NSNotification *)notification { 88 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid; 89 | backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 90 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 91 | }]; 92 | 93 | if(backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 94 | __weak JGAFImageCache *weakSelf = self; 95 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 96 | NSDate *maxAge = [NSDate dateWithTimeIntervalSinceNow:weakSelf.fileExpirationInterval]; 97 | [[self class] removeAllFilesOlderThanDate:maxAge]; 98 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 99 | }); 100 | } 101 | } 102 | 103 | - (void)imageForURL:(NSString *)url completion:(void (^)(UIImage *image))completion { 104 | NSAssert(url.length > 0, @"url cannot be nil"); 105 | __weak JGAFImageCache *weakSelf = self; 106 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 107 | NSString *sha1 = [url jgaf_sha1]; 108 | UIImage *image = [weakSelf.imageCache objectForKey:sha1]; 109 | if(image == nil) { 110 | image = [weakSelf imageFromDiskForKey:sha1]; 111 | } 112 | 113 | if(image == nil) { 114 | [weakSelf loadRemoteImageForURL:url key:sha1 retryCount:0 completion:completion]; 115 | } 116 | else if(completion) { 117 | dispatch_async(dispatch_get_main_queue(), ^{ 118 | completion(image); 119 | }); 120 | } 121 | }); 122 | } 123 | 124 | - (UIImage *)imageFromDiskForKey:(NSString *)key { 125 | UIImage *image = nil; 126 | @try { 127 | NSString *filePath = [[self class] filePathForKey:key]; 128 | NSFileManager *fileManager = [[self class] sharedFileManager]; 129 | if([fileManager fileExistsAtPath:filePath]) { 130 | // Update the file modification date for use by the removeAllFilesOlderThanDate: method 131 | [fileManager setAttributes:@{NSFileModificationDate:[NSDate date]} ofItemAtPath:filePath error:NULL]; 132 | image = [[self class] imageWithData:[fileManager contentsAtPath:filePath]]; 133 | if (image) { 134 | [_imageCache setObject:image forKey:key]; 135 | } 136 | } 137 | } 138 | @catch(NSException *exception) { 139 | #if JGAFImageCache_LOGGING_ENABLED 140 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, exception); 141 | #endif 142 | } 143 | return image; 144 | } 145 | 146 | - (void)loadRemoteImageForURL:(NSString *)url key:(NSString *)key retryCount:(NSInteger)retryCount completion:(void (^)(UIImage *image))completion { 147 | if (url.length > 0) { 148 | NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; 149 | __weak JGAFImageCache *weakSelf = self; 150 | NSURLSessionDataTask *task = [self.urlSession dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 151 | if (error == nil) { 152 | NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; 153 | NSInteger httpStatusCode = httpResponse.statusCode; 154 | switch (httpStatusCode) { 155 | case 200: { 156 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 157 | UIImage *image = nil; 158 | if(data.length) { 159 | @try { 160 | image = [[weakSelf class] imageWithData:data]; 161 | } 162 | @catch(NSException *exception) { 163 | #if JGAFImageCache_LOGGING_ENABLED 164 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, exception); 165 | #endif 166 | } 167 | } 168 | 169 | if(image) { 170 | [[weakSelf class] saveImageToDiskForKey:image key:key]; 171 | [weakSelf.imageCache setObject:image forKey:key]; 172 | 173 | } 174 | 175 | if(completion) { 176 | dispatch_async(dispatch_get_main_queue(), ^{ 177 | completion(image); 178 | }); 179 | } 180 | }); 181 | } break; 182 | 183 | default: { 184 | NSLog(@"%s [Line %d] failed: %@", __PRETTY_FUNCTION__, __LINE__, url); 185 | if((retryCount >= weakSelf.maxNumberOfRetries) || (httpStatusCode >= 400 && httpStatusCode <= 499)) { 186 | //out of retries or got a 400 level error so don't retry 187 | if(completion) { 188 | dispatch_async(dispatch_get_main_queue(), ^{ 189 | completion(nil); 190 | }); 191 | } 192 | } 193 | else { 194 | // try again 195 | NSInteger nextRetryCount = retryCount + 1; 196 | double delayInSeconds = self.retryDelay; 197 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 198 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 199 | [self loadRemoteImageForURL:url key:key retryCount:nextRetryCount completion:completion]; 200 | }); 201 | 202 | #if JGAFImageCache_LOGGING_ENABLED 203 | NSLog(@"%s [Line %d] retrying(%d)", __PRETTY_FUNCTION__, __LINE__, (int)nextRetryCount); 204 | #endif 205 | } 206 | 207 | #if JGAFImageCache_LOGGING_ENABLED 208 | NSLog(@"%s [Line %d] statusCode(%d) %@", __PRETTY_FUNCTION__, __LINE__, (int)httpStatusCode, response); 209 | #endif 210 | } break; 211 | } 212 | } 213 | else { 214 | #if JGAFImageCache_LOGGING_ENABLED 215 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, error); 216 | #endif 217 | } 218 | }]; 219 | [task resume]; 220 | } 221 | else if(completion) { 222 | completion(nil); 223 | } 224 | } 225 | 226 | - (void)clearAllData { 227 | [self.imageCache removeAllObjects]; 228 | 229 | UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid; 230 | backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 231 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 232 | }]; 233 | 234 | if(backgroundTaskIdentifier != UIBackgroundTaskInvalid) { 235 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 236 | [[self class] removeAllFilesOlderThanDate:[NSDate date]]; 237 | [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier]; 238 | }); 239 | } 240 | } 241 | 242 | #pragma mark - Class Methods 243 | 244 | #pragma mark - Images 245 | 246 | + (UIImage *)imageWithData:(NSData *)data { 247 | @synchronized(self) { 248 | return [[UIImage alloc] initWithData:data]; 249 | } 250 | } 251 | 252 | #pragma mark - Files 253 | 254 | + (NSFileManager *)sharedFileManager { 255 | static id sharedFileManagerID; 256 | static dispatch_once_t onceToken; 257 | dispatch_once(&onceToken, ^{ 258 | sharedFileManagerID = [[NSFileManager alloc] init]; 259 | }); 260 | return sharedFileManagerID; 261 | } 262 | 263 | + (NSString *)cacheDirectoryPath { 264 | static NSString *cacheDirectoryPath; 265 | static dispatch_once_t onceToken; 266 | dispatch_once(&onceToken, ^{ 267 | NSArray *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 268 | cacheDirectoryPath = [[[caches objectAtIndex:0] stringByAppendingPathComponent:@"JGAFImageCache"] copy]; 269 | NSFileManager *fileManager = [self sharedFileManager]; 270 | if([fileManager fileExistsAtPath:cacheDirectoryPath isDirectory:NULL] == NO) { 271 | [fileManager createDirectoryAtPath:cacheDirectoryPath withIntermediateDirectories:YES attributes:nil error:NULL]; 272 | } 273 | }); 274 | return cacheDirectoryPath; 275 | } 276 | 277 | + (NSString *)filePathForKey:(NSString *)key { 278 | return [[self cacheDirectoryPath] stringByAppendingPathComponent:key]; 279 | } 280 | 281 | + (NSOperationQueue *)saveOperationQueue { 282 | static NSOperationQueue *operationQueue; 283 | static dispatch_once_t onceToken; 284 | dispatch_once(&onceToken, ^{ 285 | operationQueue = [[NSOperationQueue alloc] init]; 286 | operationQueue.maxConcurrentOperationCount = 1; 287 | }); 288 | return operationQueue; 289 | } 290 | 291 | + (void)saveImageToDiskForKey:(UIImage *)image key:(NSString *)key { 292 | [[[self class] saveOperationQueue] addOperationWithBlock:^{ 293 | @try { 294 | NSString *filePath = [self filePathForKey:key]; 295 | NSData *imageData = UIImagePNGRepresentation(image); 296 | if(imageData.length < [[self class] freeDiskSpace]) { 297 | [[self sharedFileManager] createFileAtPath:filePath contents:imageData attributes:nil]; 298 | } 299 | } 300 | @catch(NSException *exception) { 301 | #if JGAFImageCache_LOGGING_ENABLED 302 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, exception); 303 | #endif 304 | } 305 | }]; 306 | } 307 | 308 | + (unsigned long long)freeDiskSpace { 309 | unsigned long long totalFreeSpace = 0; 310 | NSError *error = nil; 311 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 312 | NSDictionary *dictionary = [[[self class] sharedFileManager] attributesOfFileSystemForPath:[paths lastObject] error: &error]; 313 | if (dictionary) { 314 | NSNumber *freeFileSystemSizeInBytes = [dictionary objectForKey:NSFileSystemFreeSize]; 315 | totalFreeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue]; 316 | } 317 | #if JGAFImageCache_LOGGING_ENABLED 318 | else if(error) { 319 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, error); 320 | } 321 | #endif 322 | return totalFreeSpace; 323 | } 324 | 325 | + (void)removeAllFilesOlderThanDate:(NSDate *)date { 326 | NSFileManager *fileManager = [self sharedFileManager]; 327 | NSString *cachePath = [self cacheDirectoryPath]; 328 | NSDirectoryEnumerator *directoryEnumerator = [fileManager enumeratorAtPath:cachePath]; 329 | NSString *file; 330 | while (file = [directoryEnumerator nextObject]) { 331 | NSError *error = nil; 332 | NSString *filepath = [cachePath stringByAppendingPathComponent:file]; 333 | NSDate *modifiedDate = [[fileManager attributesOfItemAtPath:filepath error:&error] fileModificationDate]; 334 | if(error == nil) { 335 | if ([modifiedDate compare:date] == NSOrderedAscending) { 336 | [fileManager removeItemAtPath:filepath error:&error]; 337 | } 338 | } 339 | #if JGAFImageCache_LOGGING_ENABLED 340 | if(error != nil) { 341 | NSLog(@"%s [Line %d] %@", __PRETTY_FUNCTION__, __LINE__, error); 342 | } 343 | #endif 344 | } 345 | } 346 | 347 | #pragma mark - Internet 348 | 349 | + (NSString *)escapedPathForURL:(NSURL *)url { 350 | NSString *escapedPath = @""; 351 | if(url.path.length) { 352 | escapedPath = [self stringByEscapingSpaces:url.path]; 353 | } 354 | 355 | if(url.query.length) { 356 | escapedPath = [escapedPath stringByAppendingFormat:@"?%@", url.query]; 357 | } 358 | 359 | if(url.fragment.length) { 360 | escapedPath = [escapedPath stringByAppendingFormat:@"#%@", url.fragment]; 361 | } 362 | 363 | return escapedPath.length > 0 ? escapedPath : nil; 364 | } 365 | 366 | + (NSString *)stringByEscapingSpaces:(NSString *)string { 367 | // () 368 | CFStringRef escaped = 369 | CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, 370 | (CFStringRef)string, 371 | NULL, 372 | (CFStringRef)@" ", 373 | kCFStringEncodingUTF8); 374 | return (__bridge_transfer NSString *)escaped; 375 | } 376 | 377 | @end 378 | -------------------------------------------------------------------------------- /yanZhengCodeTests/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /yanZhengCodeTests/yanZhengCodeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // yanZhengCodeTests.m 3 | // yanZhengCodeTests 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface yanZhengCodeTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation yanZhengCodeTests 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 | -------------------------------------------------------------------------------- /yanZhengCodeUITests/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /yanZhengCodeUITests/yanZhengCodeUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // yanZhengCodeUITests.m 3 | // yanZhengCodeUITests 4 | // 5 | // Created by 王双龙 on 16/8/3. 6 | // Copyright © 2016年 王双龙. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface yanZhengCodeUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation yanZhengCodeUITests 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 | --------------------------------------------------------------------------------