├── LICENSE ├── NHGraphCoderPro.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── nanhujiaju.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── nanhujiaju.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── NHGraphCoderPro.xcscheme │ └── xcschememanagement.plist ├── NHGraphCoderPro ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Classes │ ├── NHGraphCoder.h │ ├── NHGraphCoder.m │ ├── Resources │ │ ├── coder_default.jpg │ │ ├── error.png │ │ ├── error@2x.png │ │ ├── error@3x.png │ │ ├── success.png │ │ ├── success@2x.png │ │ └── success@3x.png │ └── vendor │ │ ├── FBShimmering │ │ ├── FBShimmering-Prefix.pch │ │ ├── FBShimmering.h │ │ ├── FBShimmeringLayer.h │ │ ├── FBShimmeringLayer.m │ │ ├── FBShimmeringView.h │ │ └── FBShimmeringView.m │ │ └── JGAFImageCache │ │ ├── JGAFImageCache.h │ │ └── JGAFImageCache.m ├── Info.plist ├── ViewController.h ├── ViewController.m ├── main.m ├── test_1.jpg ├── test_2.jpg ├── test_3.jpg └── test_5.jpg ├── NHGraphCoderProTests ├── Info.plist └── NHGraphCoderProTests.m ├── NHGraphCoderProUITests ├── Info.plist └── NHGraphCoderProUITests.m └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 iFindTA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 60632FA41CFFDF4300B5FDA2 /* JGAFImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 60632FA31CFFDF4300B5FDA2 /* JGAFImageCache.m */; }; 11 | 60632FA61CFFE6D600B5FDA2 /* coder_default.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 60632FA51CFFE6D600B5FDA2 /* coder_default.jpg */; }; 12 | 609F35AE1CFEA2B100A04125 /* NHGraphCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 609F35AB1CFEA2B100A04125 /* NHGraphCoder.m */; }; 13 | 609F35BC1CFEBA2800A04125 /* FBShimmeringLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 609F35B91CFEBA2800A04125 /* FBShimmeringLayer.m */; }; 14 | 609F35BD1CFEBA2800A04125 /* FBShimmeringView.m in Sources */ = {isa = PBXBuildFile; fileRef = 609F35BB1CFEBA2800A04125 /* FBShimmeringView.m */; }; 15 | 609F35C41CFED2A200A04125 /* error.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35BE1CFED2A200A04125 /* error.png */; }; 16 | 609F35C51CFED2A200A04125 /* error@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35BF1CFED2A200A04125 /* error@2x.png */; }; 17 | 609F35C61CFED2A200A04125 /* error@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35C01CFED2A200A04125 /* error@3x.png */; }; 18 | 609F35C71CFED2A200A04125 /* success.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35C11CFED2A200A04125 /* success.png */; }; 19 | 609F35C81CFED2A200A04125 /* success@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35C21CFED2A200A04125 /* success@2x.png */; }; 20 | 609F35C91CFED2A200A04125 /* success@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 609F35C31CFED2A200A04125 /* success@3x.png */; }; 21 | 60B9C9C91CFC88FB00B26609 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 60B9C9C81CFC88FB00B26609 /* main.m */; }; 22 | 60B9C9CC1CFC88FB00B26609 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 60B9C9CB1CFC88FB00B26609 /* AppDelegate.m */; }; 23 | 60B9C9CF1CFC88FB00B26609 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 60B9C9CE1CFC88FB00B26609 /* ViewController.m */; }; 24 | 60B9C9D21CFC88FB00B26609 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 60B9C9D01CFC88FB00B26609 /* Main.storyboard */; }; 25 | 60B9C9D41CFC88FB00B26609 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 60B9C9D31CFC88FB00B26609 /* Assets.xcassets */; }; 26 | 60B9C9D71CFC88FB00B26609 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 60B9C9D51CFC88FB00B26609 /* LaunchScreen.storyboard */; }; 27 | 60B9C9E21CFC88FC00B26609 /* NHGraphCoderProTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60B9C9E11CFC88FC00B26609 /* NHGraphCoderProTests.m */; }; 28 | 60B9C9ED1CFC88FC00B26609 /* NHGraphCoderProUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 60B9C9EC1CFC88FC00B26609 /* NHGraphCoderProUITests.m */; }; 29 | 60B9C9FF1CFC8B9E00B26609 /* test_1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 60B9C9FD1CFC8B9E00B26609 /* test_1.jpg */; }; 30 | 60B9CA001CFC8B9E00B26609 /* test_2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 60B9C9FE1CFC8B9E00B26609 /* test_2.jpg */; }; 31 | 60B9CA041CFC997B00B26609 /* test_3.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 60B9CA011CFC997B00B26609 /* test_3.jpg */; }; 32 | 60B9CA061CFC997B00B26609 /* test_5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 60B9CA031CFC997B00B26609 /* test_5.jpg */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | 60B9C9DE1CFC88FC00B26609 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 60B9C9BC1CFC88FB00B26609 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 60B9C9C31CFC88FB00B26609; 41 | remoteInfo = NHGraphCoderPro; 42 | }; 43 | 60B9C9E91CFC88FC00B26609 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 60B9C9BC1CFC88FB00B26609 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 60B9C9C31CFC88FB00B26609; 48 | remoteInfo = NHGraphCoderPro; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 60632FA21CFFDF4300B5FDA2 /* JGAFImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JGAFImageCache.h; sourceTree = ""; }; 54 | 60632FA31CFFDF4300B5FDA2 /* JGAFImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JGAFImageCache.m; sourceTree = ""; }; 55 | 60632FA51CFFE6D600B5FDA2 /* coder_default.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = coder_default.jpg; sourceTree = ""; }; 56 | 609F35AA1CFEA2B100A04125 /* NHGraphCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NHGraphCoder.h; sourceTree = ""; }; 57 | 609F35AB1CFEA2B100A04125 /* NHGraphCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NHGraphCoder.m; sourceTree = ""; }; 58 | 609F35B61CFEBA2800A04125 /* FBShimmering-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FBShimmering-Prefix.pch"; sourceTree = ""; }; 59 | 609F35B71CFEBA2800A04125 /* FBShimmering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmering.h; sourceTree = ""; }; 60 | 609F35B81CFEBA2800A04125 /* FBShimmeringLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmeringLayer.h; sourceTree = ""; }; 61 | 609F35B91CFEBA2800A04125 /* FBShimmeringLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBShimmeringLayer.m; sourceTree = ""; }; 62 | 609F35BA1CFEBA2800A04125 /* FBShimmeringView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBShimmeringView.h; sourceTree = ""; }; 63 | 609F35BB1CFEBA2800A04125 /* FBShimmeringView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBShimmeringView.m; sourceTree = ""; }; 64 | 609F35BE1CFED2A200A04125 /* error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = error.png; sourceTree = ""; }; 65 | 609F35BF1CFED2A200A04125 /* error@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "error@2x.png"; sourceTree = ""; }; 66 | 609F35C01CFED2A200A04125 /* error@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "error@3x.png"; sourceTree = ""; }; 67 | 609F35C11CFED2A200A04125 /* success.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = success.png; sourceTree = ""; }; 68 | 609F35C21CFED2A200A04125 /* success@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "success@2x.png"; sourceTree = ""; }; 69 | 609F35C31CFED2A200A04125 /* success@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "success@3x.png"; sourceTree = ""; }; 70 | 60B9C9C41CFC88FB00B26609 /* NHGraphCoderPro.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NHGraphCoderPro.app; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | 60B9C9C81CFC88FB00B26609 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 72 | 60B9C9CA1CFC88FB00B26609 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 73 | 60B9C9CB1CFC88FB00B26609 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 74 | 60B9C9CD1CFC88FB00B26609 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 75 | 60B9C9CE1CFC88FB00B26609 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 76 | 60B9C9D11CFC88FB00B26609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 77 | 60B9C9D31CFC88FB00B26609 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 78 | 60B9C9D61CFC88FB00B26609 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 79 | 60B9C9D81CFC88FB00B26609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80 | 60B9C9DD1CFC88FC00B26609 /* NHGraphCoderProTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NHGraphCoderProTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 60B9C9E11CFC88FC00B26609 /* NHGraphCoderProTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NHGraphCoderProTests.m; sourceTree = ""; }; 82 | 60B9C9E31CFC88FC00B26609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83 | 60B9C9E81CFC88FC00B26609 /* NHGraphCoderProUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NHGraphCoderProUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 84 | 60B9C9EC1CFC88FC00B26609 /* NHGraphCoderProUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NHGraphCoderProUITests.m; sourceTree = ""; }; 85 | 60B9C9EE1CFC88FC00B26609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 86 | 60B9C9FD1CFC8B9E00B26609 /* test_1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_1.jpg; sourceTree = ""; }; 87 | 60B9C9FE1CFC8B9E00B26609 /* test_2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_2.jpg; sourceTree = ""; }; 88 | 60B9CA011CFC997B00B26609 /* test_3.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_3.jpg; sourceTree = ""; }; 89 | 60B9CA031CFC997B00B26609 /* test_5.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = test_5.jpg; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 60B9C9C11CFC88FB00B26609 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | 60B9C9DA1CFC88FC00B26609 /* Frameworks */ = { 101 | isa = PBXFrameworksBuildPhase; 102 | buildActionMask = 2147483647; 103 | files = ( 104 | ); 105 | runOnlyForDeploymentPostprocessing = 0; 106 | }; 107 | 60B9C9E51CFC88FC00B26609 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXFrameworksBuildPhase section */ 115 | 116 | /* Begin PBXGroup section */ 117 | 60632FA11CFFDF4300B5FDA2 /* JGAFImageCache */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 60632FA21CFFDF4300B5FDA2 /* JGAFImageCache.h */, 121 | 60632FA31CFFDF4300B5FDA2 /* JGAFImageCache.m */, 122 | ); 123 | path = JGAFImageCache; 124 | sourceTree = ""; 125 | }; 126 | 609F35A91CFEA2B100A04125 /* Classes */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 609F35AC1CFEA2B100A04125 /* Resources */, 130 | 609F35B01CFEAF3200A04125 /* vendor */, 131 | 609F35AA1CFEA2B100A04125 /* NHGraphCoder.h */, 132 | 609F35AB1CFEA2B100A04125 /* NHGraphCoder.m */, 133 | ); 134 | path = Classes; 135 | sourceTree = ""; 136 | }; 137 | 609F35AC1CFEA2B100A04125 /* Resources */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 60632FA51CFFE6D600B5FDA2 /* coder_default.jpg */, 141 | 609F35BE1CFED2A200A04125 /* error.png */, 142 | 609F35BF1CFED2A200A04125 /* error@2x.png */, 143 | 609F35C01CFED2A200A04125 /* error@3x.png */, 144 | 609F35C11CFED2A200A04125 /* success.png */, 145 | 609F35C21CFED2A200A04125 /* success@2x.png */, 146 | 609F35C31CFED2A200A04125 /* success@3x.png */, 147 | ); 148 | path = Resources; 149 | sourceTree = ""; 150 | }; 151 | 609F35B01CFEAF3200A04125 /* vendor */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 60632FA11CFFDF4300B5FDA2 /* JGAFImageCache */, 155 | 609F35B51CFEBA2800A04125 /* FBShimmering */, 156 | ); 157 | path = vendor; 158 | sourceTree = ""; 159 | }; 160 | 609F35B51CFEBA2800A04125 /* FBShimmering */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 609F35B61CFEBA2800A04125 /* FBShimmering-Prefix.pch */, 164 | 609F35B71CFEBA2800A04125 /* FBShimmering.h */, 165 | 609F35B81CFEBA2800A04125 /* FBShimmeringLayer.h */, 166 | 609F35B91CFEBA2800A04125 /* FBShimmeringLayer.m */, 167 | 609F35BA1CFEBA2800A04125 /* FBShimmeringView.h */, 168 | 609F35BB1CFEBA2800A04125 /* FBShimmeringView.m */, 169 | ); 170 | path = FBShimmering; 171 | sourceTree = ""; 172 | }; 173 | 60B9C9BB1CFC88FB00B26609 = { 174 | isa = PBXGroup; 175 | children = ( 176 | 60B9C9C61CFC88FB00B26609 /* NHGraphCoderPro */, 177 | 60B9C9E01CFC88FC00B26609 /* NHGraphCoderProTests */, 178 | 60B9C9EB1CFC88FC00B26609 /* NHGraphCoderProUITests */, 179 | 60B9C9C51CFC88FB00B26609 /* Products */, 180 | ); 181 | sourceTree = ""; 182 | }; 183 | 60B9C9C51CFC88FB00B26609 /* Products */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 60B9C9C41CFC88FB00B26609 /* NHGraphCoderPro.app */, 187 | 60B9C9DD1CFC88FC00B26609 /* NHGraphCoderProTests.xctest */, 188 | 60B9C9E81CFC88FC00B26609 /* NHGraphCoderProUITests.xctest */, 189 | ); 190 | name = Products; 191 | sourceTree = ""; 192 | }; 193 | 60B9C9C61CFC88FB00B26609 /* NHGraphCoderPro */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 609F35A91CFEA2B100A04125 /* Classes */, 197 | 60B9C9CA1CFC88FB00B26609 /* AppDelegate.h */, 198 | 60B9C9CB1CFC88FB00B26609 /* AppDelegate.m */, 199 | 60B9C9CD1CFC88FB00B26609 /* ViewController.h */, 200 | 60B9C9CE1CFC88FB00B26609 /* ViewController.m */, 201 | 60B9C9D01CFC88FB00B26609 /* Main.storyboard */, 202 | 60B9C9D31CFC88FB00B26609 /* Assets.xcassets */, 203 | 60B9C9D51CFC88FB00B26609 /* LaunchScreen.storyboard */, 204 | 60B9C9D81CFC88FB00B26609 /* Info.plist */, 205 | 60B9C9C71CFC88FB00B26609 /* Supporting Files */, 206 | ); 207 | path = NHGraphCoderPro; 208 | sourceTree = ""; 209 | }; 210 | 60B9C9C71CFC88FB00B26609 /* Supporting Files */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | 60B9C9FD1CFC8B9E00B26609 /* test_1.jpg */, 214 | 60B9C9FE1CFC8B9E00B26609 /* test_2.jpg */, 215 | 60B9CA011CFC997B00B26609 /* test_3.jpg */, 216 | 60B9CA031CFC997B00B26609 /* test_5.jpg */, 217 | 60B9C9C81CFC88FB00B26609 /* main.m */, 218 | ); 219 | name = "Supporting Files"; 220 | sourceTree = ""; 221 | }; 222 | 60B9C9E01CFC88FC00B26609 /* NHGraphCoderProTests */ = { 223 | isa = PBXGroup; 224 | children = ( 225 | 60B9C9E11CFC88FC00B26609 /* NHGraphCoderProTests.m */, 226 | 60B9C9E31CFC88FC00B26609 /* Info.plist */, 227 | ); 228 | path = NHGraphCoderProTests; 229 | sourceTree = ""; 230 | }; 231 | 60B9C9EB1CFC88FC00B26609 /* NHGraphCoderProUITests */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | 60B9C9EC1CFC88FC00B26609 /* NHGraphCoderProUITests.m */, 235 | 60B9C9EE1CFC88FC00B26609 /* Info.plist */, 236 | ); 237 | path = NHGraphCoderProUITests; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXGroup section */ 241 | 242 | /* Begin PBXNativeTarget section */ 243 | 60B9C9C31CFC88FB00B26609 /* NHGraphCoderPro */ = { 244 | isa = PBXNativeTarget; 245 | buildConfigurationList = 60B9C9F11CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderPro" */; 246 | buildPhases = ( 247 | 60B9C9C01CFC88FB00B26609 /* Sources */, 248 | 60B9C9C11CFC88FB00B26609 /* Frameworks */, 249 | 60B9C9C21CFC88FB00B26609 /* Resources */, 250 | ); 251 | buildRules = ( 252 | ); 253 | dependencies = ( 254 | ); 255 | name = NHGraphCoderPro; 256 | productName = NHGraphCoderPro; 257 | productReference = 60B9C9C41CFC88FB00B26609 /* NHGraphCoderPro.app */; 258 | productType = "com.apple.product-type.application"; 259 | }; 260 | 60B9C9DC1CFC88FC00B26609 /* NHGraphCoderProTests */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = 60B9C9F41CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderProTests" */; 263 | buildPhases = ( 264 | 60B9C9D91CFC88FC00B26609 /* Sources */, 265 | 60B9C9DA1CFC88FC00B26609 /* Frameworks */, 266 | 60B9C9DB1CFC88FC00B26609 /* Resources */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | 60B9C9DF1CFC88FC00B26609 /* PBXTargetDependency */, 272 | ); 273 | name = NHGraphCoderProTests; 274 | productName = NHGraphCoderProTests; 275 | productReference = 60B9C9DD1CFC88FC00B26609 /* NHGraphCoderProTests.xctest */; 276 | productType = "com.apple.product-type.bundle.unit-test"; 277 | }; 278 | 60B9C9E71CFC88FC00B26609 /* NHGraphCoderProUITests */ = { 279 | isa = PBXNativeTarget; 280 | buildConfigurationList = 60B9C9F71CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderProUITests" */; 281 | buildPhases = ( 282 | 60B9C9E41CFC88FC00B26609 /* Sources */, 283 | 60B9C9E51CFC88FC00B26609 /* Frameworks */, 284 | 60B9C9E61CFC88FC00B26609 /* Resources */, 285 | ); 286 | buildRules = ( 287 | ); 288 | dependencies = ( 289 | 60B9C9EA1CFC88FC00B26609 /* PBXTargetDependency */, 290 | ); 291 | name = NHGraphCoderProUITests; 292 | productName = NHGraphCoderProUITests; 293 | productReference = 60B9C9E81CFC88FC00B26609 /* NHGraphCoderProUITests.xctest */; 294 | productType = "com.apple.product-type.bundle.ui-testing"; 295 | }; 296 | /* End PBXNativeTarget section */ 297 | 298 | /* Begin PBXProject section */ 299 | 60B9C9BC1CFC88FB00B26609 /* Project object */ = { 300 | isa = PBXProject; 301 | attributes = { 302 | LastUpgradeCheck = 0730; 303 | ORGANIZATIONNAME = "hu jiaju"; 304 | TargetAttributes = { 305 | 60B9C9C31CFC88FB00B26609 = { 306 | CreatedOnToolsVersion = 7.3.1; 307 | DevelopmentTeam = 4WJ4F55RGP; 308 | }; 309 | 60B9C9DC1CFC88FC00B26609 = { 310 | CreatedOnToolsVersion = 7.3.1; 311 | DevelopmentTeam = 4WJ4F55RGP; 312 | TestTargetID = 60B9C9C31CFC88FB00B26609; 313 | }; 314 | 60B9C9E71CFC88FC00B26609 = { 315 | CreatedOnToolsVersion = 7.3.1; 316 | DevelopmentTeam = 4WJ4F55RGP; 317 | TestTargetID = 60B9C9C31CFC88FB00B26609; 318 | }; 319 | }; 320 | }; 321 | buildConfigurationList = 60B9C9BF1CFC88FB00B26609 /* Build configuration list for PBXProject "NHGraphCoderPro" */; 322 | compatibilityVersion = "Xcode 3.2"; 323 | developmentRegion = English; 324 | hasScannedForEncodings = 0; 325 | knownRegions = ( 326 | en, 327 | Base, 328 | ); 329 | mainGroup = 60B9C9BB1CFC88FB00B26609; 330 | productRefGroup = 60B9C9C51CFC88FB00B26609 /* Products */; 331 | projectDirPath = ""; 332 | projectRoot = ""; 333 | targets = ( 334 | 60B9C9C31CFC88FB00B26609 /* NHGraphCoderPro */, 335 | 60B9C9DC1CFC88FC00B26609 /* NHGraphCoderProTests */, 336 | 60B9C9E71CFC88FC00B26609 /* NHGraphCoderProUITests */, 337 | ); 338 | }; 339 | /* End PBXProject section */ 340 | 341 | /* Begin PBXResourcesBuildPhase section */ 342 | 60B9C9C21CFC88FB00B26609 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | 609F35C81CFED2A200A04125 /* success@2x.png in Resources */, 347 | 609F35C71CFED2A200A04125 /* success.png in Resources */, 348 | 609F35C61CFED2A200A04125 /* error@3x.png in Resources */, 349 | 60B9C9D71CFC88FB00B26609 /* LaunchScreen.storyboard in Resources */, 350 | 60B9C9FF1CFC8B9E00B26609 /* test_1.jpg in Resources */, 351 | 60B9C9D41CFC88FB00B26609 /* Assets.xcassets in Resources */, 352 | 60B9CA041CFC997B00B26609 /* test_3.jpg in Resources */, 353 | 60B9CA001CFC8B9E00B26609 /* test_2.jpg in Resources */, 354 | 60B9CA061CFC997B00B26609 /* test_5.jpg in Resources */, 355 | 60632FA61CFFE6D600B5FDA2 /* coder_default.jpg in Resources */, 356 | 609F35C41CFED2A200A04125 /* error.png in Resources */, 357 | 609F35C91CFED2A200A04125 /* success@3x.png in Resources */, 358 | 609F35C51CFED2A200A04125 /* error@2x.png in Resources */, 359 | 60B9C9D21CFC88FB00B26609 /* Main.storyboard in Resources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | 60B9C9DB1CFC88FC00B26609 /* Resources */ = { 364 | isa = PBXResourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | ); 368 | runOnlyForDeploymentPostprocessing = 0; 369 | }; 370 | 60B9C9E61CFC88FC00B26609 /* Resources */ = { 371 | isa = PBXResourcesBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | runOnlyForDeploymentPostprocessing = 0; 376 | }; 377 | /* End PBXResourcesBuildPhase section */ 378 | 379 | /* Begin PBXSourcesBuildPhase section */ 380 | 60B9C9C01CFC88FB00B26609 /* Sources */ = { 381 | isa = PBXSourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | 609F35BC1CFEBA2800A04125 /* FBShimmeringLayer.m in Sources */, 385 | 60B9C9CF1CFC88FB00B26609 /* ViewController.m in Sources */, 386 | 609F35BD1CFEBA2800A04125 /* FBShimmeringView.m in Sources */, 387 | 60B9C9CC1CFC88FB00B26609 /* AppDelegate.m in Sources */, 388 | 609F35AE1CFEA2B100A04125 /* NHGraphCoder.m in Sources */, 389 | 60B9C9C91CFC88FB00B26609 /* main.m in Sources */, 390 | 60632FA41CFFDF4300B5FDA2 /* JGAFImageCache.m in Sources */, 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | 60B9C9D91CFC88FC00B26609 /* Sources */ = { 395 | isa = PBXSourcesBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | 60B9C9E21CFC88FC00B26609 /* NHGraphCoderProTests.m in Sources */, 399 | ); 400 | runOnlyForDeploymentPostprocessing = 0; 401 | }; 402 | 60B9C9E41CFC88FC00B26609 /* Sources */ = { 403 | isa = PBXSourcesBuildPhase; 404 | buildActionMask = 2147483647; 405 | files = ( 406 | 60B9C9ED1CFC88FC00B26609 /* NHGraphCoderProUITests.m in Sources */, 407 | ); 408 | runOnlyForDeploymentPostprocessing = 0; 409 | }; 410 | /* End PBXSourcesBuildPhase section */ 411 | 412 | /* Begin PBXTargetDependency section */ 413 | 60B9C9DF1CFC88FC00B26609 /* PBXTargetDependency */ = { 414 | isa = PBXTargetDependency; 415 | target = 60B9C9C31CFC88FB00B26609 /* NHGraphCoderPro */; 416 | targetProxy = 60B9C9DE1CFC88FC00B26609 /* PBXContainerItemProxy */; 417 | }; 418 | 60B9C9EA1CFC88FC00B26609 /* PBXTargetDependency */ = { 419 | isa = PBXTargetDependency; 420 | target = 60B9C9C31CFC88FB00B26609 /* NHGraphCoderPro */; 421 | targetProxy = 60B9C9E91CFC88FC00B26609 /* PBXContainerItemProxy */; 422 | }; 423 | /* End PBXTargetDependency section */ 424 | 425 | /* Begin PBXVariantGroup section */ 426 | 60B9C9D01CFC88FB00B26609 /* Main.storyboard */ = { 427 | isa = PBXVariantGroup; 428 | children = ( 429 | 60B9C9D11CFC88FB00B26609 /* Base */, 430 | ); 431 | name = Main.storyboard; 432 | sourceTree = ""; 433 | }; 434 | 60B9C9D51CFC88FB00B26609 /* LaunchScreen.storyboard */ = { 435 | isa = PBXVariantGroup; 436 | children = ( 437 | 60B9C9D61CFC88FB00B26609 /* Base */, 438 | ); 439 | name = LaunchScreen.storyboard; 440 | sourceTree = ""; 441 | }; 442 | /* End PBXVariantGroup section */ 443 | 444 | /* Begin XCBuildConfiguration section */ 445 | 60B9C9EF1CFC88FC00B26609 /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ALWAYS_SEARCH_USER_PATHS = NO; 449 | CLANG_ANALYZER_NONNULL = YES; 450 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 451 | CLANG_CXX_LIBRARY = "libc++"; 452 | CLANG_ENABLE_MODULES = YES; 453 | CLANG_ENABLE_OBJC_ARC = YES; 454 | CLANG_WARN_BOOL_CONVERSION = YES; 455 | CLANG_WARN_CONSTANT_CONVERSION = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_EMPTY_BODY = YES; 458 | CLANG_WARN_ENUM_CONVERSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_UNREACHABLE_CODE = YES; 462 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 463 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 464 | COPY_PHASE_STRIP = NO; 465 | DEBUG_INFORMATION_FORMAT = dwarf; 466 | ENABLE_STRICT_OBJC_MSGSEND = YES; 467 | ENABLE_TESTABILITY = YES; 468 | GCC_C_LANGUAGE_STANDARD = gnu99; 469 | GCC_DYNAMIC_NO_PIC = NO; 470 | GCC_NO_COMMON_BLOCKS = YES; 471 | GCC_OPTIMIZATION_LEVEL = 0; 472 | GCC_PREPROCESSOR_DEFINITIONS = ( 473 | "DEBUG=1", 474 | "$(inherited)", 475 | ); 476 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_UNDECLARED_SELECTOR = YES; 479 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 480 | GCC_WARN_UNUSED_FUNCTION = YES; 481 | GCC_WARN_UNUSED_VARIABLE = YES; 482 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 483 | MTL_ENABLE_DEBUG_INFO = YES; 484 | ONLY_ACTIVE_ARCH = YES; 485 | SDKROOT = iphoneos; 486 | TARGETED_DEVICE_FAMILY = "1,2"; 487 | }; 488 | name = Debug; 489 | }; 490 | 60B9C9F01CFC88FC00B26609 /* Release */ = { 491 | isa = XCBuildConfiguration; 492 | buildSettings = { 493 | ALWAYS_SEARCH_USER_PATHS = NO; 494 | CLANG_ANALYZER_NONNULL = YES; 495 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 496 | CLANG_CXX_LIBRARY = "libc++"; 497 | CLANG_ENABLE_MODULES = YES; 498 | CLANG_ENABLE_OBJC_ARC = YES; 499 | CLANG_WARN_BOOL_CONVERSION = YES; 500 | CLANG_WARN_CONSTANT_CONVERSION = YES; 501 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 502 | CLANG_WARN_EMPTY_BODY = YES; 503 | CLANG_WARN_ENUM_CONVERSION = YES; 504 | CLANG_WARN_INT_CONVERSION = YES; 505 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 506 | CLANG_WARN_UNREACHABLE_CODE = YES; 507 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 508 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 509 | COPY_PHASE_STRIP = NO; 510 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 511 | ENABLE_NS_ASSERTIONS = NO; 512 | ENABLE_STRICT_OBJC_MSGSEND = YES; 513 | GCC_C_LANGUAGE_STANDARD = gnu99; 514 | GCC_NO_COMMON_BLOCKS = YES; 515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 517 | GCC_WARN_UNDECLARED_SELECTOR = YES; 518 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 519 | GCC_WARN_UNUSED_FUNCTION = YES; 520 | GCC_WARN_UNUSED_VARIABLE = YES; 521 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 522 | MTL_ENABLE_DEBUG_INFO = NO; 523 | SDKROOT = iphoneos; 524 | TARGETED_DEVICE_FAMILY = "1,2"; 525 | VALIDATE_PRODUCT = YES; 526 | }; 527 | name = Release; 528 | }; 529 | 60B9C9F21CFC88FC00B26609 /* Debug */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 533 | CODE_SIGN_IDENTITY = "iPhone Developer"; 534 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 535 | INFOPLIST_FILE = NHGraphCoderPro/Info.plist; 536 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 537 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 538 | PRODUCT_BUNDLE_IDENTIFIER = com.nanhu.processa; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | PROVISIONING_PROFILE = ""; 541 | }; 542 | name = Debug; 543 | }; 544 | 60B9C9F31CFC88FC00B26609 /* Release */ = { 545 | isa = XCBuildConfiguration; 546 | buildSettings = { 547 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 548 | CODE_SIGN_IDENTITY = "iPhone Developer"; 549 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 550 | INFOPLIST_FILE = NHGraphCoderPro/Info.plist; 551 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 552 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 553 | PRODUCT_BUNDLE_IDENTIFIER = com.nanhu.processa; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | PROVISIONING_PROFILE = ""; 556 | }; 557 | name = Release; 558 | }; 559 | 60B9C9F51CFC88FC00B26609 /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | BUNDLE_LOADER = "$(TEST_HOST)"; 563 | INFOPLIST_FILE = NHGraphCoderProTests/Info.plist; 564 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 565 | PRODUCT_BUNDLE_IDENTIFIER = com.youtu.acb.NHGraphCoderProTests; 566 | PRODUCT_NAME = "$(TARGET_NAME)"; 567 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NHGraphCoderPro.app/NHGraphCoderPro"; 568 | }; 569 | name = Debug; 570 | }; 571 | 60B9C9F61CFC88FC00B26609 /* Release */ = { 572 | isa = XCBuildConfiguration; 573 | buildSettings = { 574 | BUNDLE_LOADER = "$(TEST_HOST)"; 575 | INFOPLIST_FILE = NHGraphCoderProTests/Info.plist; 576 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 577 | PRODUCT_BUNDLE_IDENTIFIER = com.youtu.acb.NHGraphCoderProTests; 578 | PRODUCT_NAME = "$(TARGET_NAME)"; 579 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/NHGraphCoderPro.app/NHGraphCoderPro"; 580 | }; 581 | name = Release; 582 | }; 583 | 60B9C9F81CFC88FC00B26609 /* Debug */ = { 584 | isa = XCBuildConfiguration; 585 | buildSettings = { 586 | INFOPLIST_FILE = NHGraphCoderProUITests/Info.plist; 587 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 588 | PRODUCT_BUNDLE_IDENTIFIER = com.youtu.acb.NHGraphCoderProUITests; 589 | PRODUCT_NAME = "$(TARGET_NAME)"; 590 | TEST_TARGET_NAME = NHGraphCoderPro; 591 | }; 592 | name = Debug; 593 | }; 594 | 60B9C9F91CFC88FC00B26609 /* Release */ = { 595 | isa = XCBuildConfiguration; 596 | buildSettings = { 597 | INFOPLIST_FILE = NHGraphCoderProUITests/Info.plist; 598 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 599 | PRODUCT_BUNDLE_IDENTIFIER = com.youtu.acb.NHGraphCoderProUITests; 600 | PRODUCT_NAME = "$(TARGET_NAME)"; 601 | TEST_TARGET_NAME = NHGraphCoderPro; 602 | }; 603 | name = Release; 604 | }; 605 | /* End XCBuildConfiguration section */ 606 | 607 | /* Begin XCConfigurationList section */ 608 | 60B9C9BF1CFC88FB00B26609 /* Build configuration list for PBXProject "NHGraphCoderPro" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 60B9C9EF1CFC88FC00B26609 /* Debug */, 612 | 60B9C9F01CFC88FC00B26609 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | 60B9C9F11CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderPro" */ = { 618 | isa = XCConfigurationList; 619 | buildConfigurations = ( 620 | 60B9C9F21CFC88FC00B26609 /* Debug */, 621 | 60B9C9F31CFC88FC00B26609 /* Release */, 622 | ); 623 | defaultConfigurationIsVisible = 0; 624 | defaultConfigurationName = Release; 625 | }; 626 | 60B9C9F41CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderProTests" */ = { 627 | isa = XCConfigurationList; 628 | buildConfigurations = ( 629 | 60B9C9F51CFC88FC00B26609 /* Debug */, 630 | 60B9C9F61CFC88FC00B26609 /* Release */, 631 | ); 632 | defaultConfigurationIsVisible = 0; 633 | defaultConfigurationName = Release; 634 | }; 635 | 60B9C9F71CFC88FC00B26609 /* Build configuration list for PBXNativeTarget "NHGraphCoderProUITests" */ = { 636 | isa = XCConfigurationList; 637 | buildConfigurations = ( 638 | 60B9C9F81CFC88FC00B26609 /* Debug */, 639 | 60B9C9F91CFC88FC00B26609 /* Release */, 640 | ); 641 | defaultConfigurationIsVisible = 0; 642 | defaultConfigurationName = Release; 643 | }; 644 | /* End XCConfigurationList section */ 645 | }; 646 | rootObject = 60B9C9BC1CFC88FB00B26609 /* Project object */; 647 | } 648 | -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/project.xcworkspace/xcuserdata/nanhujiaju.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro.xcodeproj/project.xcworkspace/xcuserdata/nanhujiaju.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/xcuserdata/nanhujiaju.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 54 | 56 | 68 | 69 | 70 | 72 | 78 | 79 | 80 | 82 | 94 | 95 | 96 | 98 | 110 | 111 | 112 | 114 | 126 | 127 | 128 | 130 | 142 | 143 | 144 | 146 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/xcuserdata/nanhujiaju.xcuserdatad/xcschemes/NHGraphCoderPro.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 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | 102 | 108 | 109 | 110 | 111 | 113 | 114 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /NHGraphCoderPro.xcodeproj/xcuserdata/nanhujiaju.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | NHGraphCoderPro.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 60B9C9C31CFC88FB00B26609 16 | 17 | primary 18 | 19 | 20 | 60B9C9DC1CFC88FC00B26609 21 | 22 | primary 23 | 24 | 25 | 60B9C9E71CFC88FC00B26609 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /NHGraphCoderPro/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.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 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /NHGraphCoderPro/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.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 "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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/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 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /NHGraphCoderPro/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | UIImage *thumbImage = [self thumbWithColor:[UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:200.0/255.0 alpha:1.0]]; 228 | 229 | 230 | _slider = [[UISlider alloc] initWithFrame:self.bounds]; 231 | _slider.autoresizingMask = UIViewAutoresizingFlexibleWidth; 232 | CGPoint ctr = _slider.center; 233 | CGRect sliderFrame = _slider.frame; 234 | sliderFrame.size.width -= 4; //each "edge" of the track is 2 pixels wide 235 | _slider.frame = sliderFrame; 236 | _slider.center = ctr; 237 | _slider.backgroundColor = [UIColor clearColor]; 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 | [self addSubview:_slider]; 249 | 250 | CGSize thumbSize = thumbImage.size; 251 | CGFloat infoStart_x = thumbSize.width ; 252 | CGRect bounds = CGRectMake(infoStart_x,0,CGRectGetWidth(self.bounds)-infoStart_x,CGRectGetHeight(self.bounds)); 253 | UILabel *infoLabel = [[UILabel alloc] initWithFrame:bounds]; 254 | //infoLabel.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 255 | infoLabel.textColor = [UIColor whiteColor]; 256 | infoLabel.textAlignment = NSTextAlignmentCenter; 257 | infoLabel.backgroundColor = [UIColor clearColor]; 258 | infoLabel.font = [UIFont boldSystemFontOfSize:20]; 259 | infoLabel.text = @"拖动滑块完成验证>>>"; 260 | infoLabel.adjustsFontSizeToFitWidth = true; 261 | //[self addSubview:infoLabel]; 262 | FBShimmeringView *shimmer = [[FBShimmeringView alloc] initWithFrame:bounds]; 263 | shimmer.shimmering = true; 264 | shimmer.shimmeringBeginFadeDuration = 1.5; 265 | shimmer.shimmeringOpacity = 0.3; 266 | shimmer.contentView = infoLabel; 267 | [self addSubview:shimmer]; 268 | self.shimmer = shimmer; 269 | 270 | // Set the slider action methods 271 | [_slider addTarget:self 272 | action:@selector(sliderUp:) 273 | forControlEvents:UIControlEventTouchUpInside]; 274 | [_slider addTarget:self 275 | action:@selector(sliderUp:) 276 | forControlEvents:UIControlEventTouchUpOutside]; 277 | [_slider addTarget:self 278 | action:@selector(sliderDown:) 279 | forControlEvents:UIControlEventTouchDown]; 280 | [_slider addTarget:self 281 | action:@selector(sliderChanged:) 282 | forControlEvents:UIControlEventValueChanged]; 283 | } 284 | 285 | - (void)resetSlider { 286 | [_slider setValue:0.0 animated: YES]; 287 | self.shimmer.alpha = 1.0; 288 | if (_event) { 289 | _event(0, false); 290 | } 291 | } 292 | 293 | // UISlider actions 294 | - (void) sliderUp:(UISlider *)sender { 295 | 296 | if (_sliding) { 297 | _sliding = NO; 298 | if (_event) { 299 | _event(sender.value, true); 300 | } 301 | } 302 | } 303 | 304 | - (void) sliderDown:(UISlider *)sender { 305 | 306 | if (!_sliding) { 307 | //[_label setAnimated:NO]; 308 | } 309 | _sliding = YES; 310 | } 311 | 312 | - (void) sliderChanged:(UISlider *)sender { 313 | 314 | self.shimmer.alpha = MAX(0.0, 1.0 - (_slider.value * 3.5)); 315 | if (_event) { 316 | _event(sender.value, false); 317 | } 318 | } 319 | 320 | - (UIImage *) thumbWithColor:(UIColor*)color { 321 | CGFloat scale = [UIScreen mainScreen].scale; 322 | if (scale<1.0) {scale = 1.0;} 323 | 324 | CGSize size = CGSizeMake(68.0*scale, 44.0*scale); 325 | CGFloat radius = 10.0*scale; 326 | // create a new bitmap image context 327 | UIGraphicsBeginImageContext(size); 328 | 329 | // get context 330 | CGContextRef context = UIGraphicsGetCurrentContext(); 331 | 332 | // push context to make it current 333 | // (need to do this manually because we are not drawing in a UIView) 334 | UIGraphicsPushContext(context); 335 | 336 | [color setFill]; 337 | [[[UIColor blackColor] colorWithAlphaComponent:0.8] setStroke]; 338 | 339 | CGFloat radiusp = radius+0.5; 340 | CGFloat wid1 = size.width-0.5; 341 | CGFloat hei1 = size.height-0.5; 342 | CGFloat wid2 = size.width-radiusp; 343 | CGFloat hei2 = size.height-radiusp; 344 | 345 | // Path 346 | CGContextMoveToPoint(context, 0.5, radiusp); 347 | CGContextAddArcToPoint(context, 0.5, 0.5, radiusp, 0.5, radius); 348 | CGContextAddLineToPoint(context, wid2, 0.5); 349 | CGContextAddArcToPoint(context, wid1, 0.5, wid1, radiusp, radius); 350 | CGContextAddLineToPoint(context, wid1, hei2); 351 | CGContextAddArcToPoint(context, wid1, hei1, wid2, hei1, radius); 352 | CGContextAddLineToPoint(context, radius, hei1); 353 | CGContextAddArcToPoint(context, 0.5, hei1, 0.5, hei2, radius); 354 | CGContextClosePath(context); 355 | CGContextDrawPath(context, kCGPathFillStroke); 356 | 357 | 358 | // Arrow 359 | [[[UIColor blueColor] colorWithAlphaComponent:0.6] setFill]; 360 | [[[UIColor blackColor] colorWithAlphaComponent:0.3] setStroke]; 361 | 362 | CGFloat points[8]= { (19.0*scale)+0.5, 363 | (16.0*scale)+0.5, 364 | (36.0*scale)+0.5, 365 | (10.0*scale)+0.5, 366 | (52.0*scale)+0.5, 367 | (22.0*scale)+0.5, 368 | (34.0*scale)+0.5, 369 | (28.0*scale)+0.5 }; 370 | 371 | CGContextMoveToPoint(context, points[0], points[1]); 372 | CGContextAddLineToPoint(context, points[2], points[1]); 373 | CGContextAddLineToPoint(context, points[2], points[3]); 374 | CGContextAddLineToPoint(context, points[4], points[5]); 375 | CGContextAddLineToPoint(context, points[2], points[6]); 376 | CGContextAddLineToPoint(context, points[2], points[7]); 377 | CGContextAddLineToPoint(context, points[0], points[7]); 378 | CGContextClosePath(context); 379 | CGContextDrawPath(context, kCGPathFillStroke); 380 | 381 | 382 | // Light 383 | [[[UIColor whiteColor] colorWithAlphaComponent:0.2] setFill]; 384 | 385 | CGFloat mid = lround(size.height/2.0)+0.5; 386 | CGContextMoveToPoint(context, 0.5, radiusp); 387 | CGContextAddArcToPoint(context, 0.5, 0.5, radiusp, 0.5, radius); 388 | CGContextAddLineToPoint(context, wid2, 0.5); 389 | CGContextAddArcToPoint(context, wid1, 0.5, wid1, radiusp, radius); 390 | CGContextAddLineToPoint(context, wid1, mid); 391 | CGContextAddLineToPoint(context, 0.5, mid); 392 | CGContextClosePath(context); 393 | CGContextDrawPath(context, kCGPathFill); 394 | 395 | // pop context 396 | UIGraphicsPopContext(); 397 | 398 | // get a UIImage from the image context 399 | UIImage *outputImage = [[UIImage alloc] initWithCGImage:UIGraphicsGetImageFromCurrentImageContext().CGImage scale:scale orientation:UIImageOrientationUp]; 400 | //write (debug) 401 | //[UIImagePNGRepresentation(outputImage) writeToFile:@"/Users/mathieu/Desktop/test.png" atomically:YES]; 402 | 403 | // clean up drawing environment 404 | UIGraphicsEndImageContext(); 405 | 406 | return outputImage; 407 | } 408 | 409 | - (UIImage *) clearPixel { 410 | CGRect rect = CGRectMake(0.0, 0.0, 1.0, 1.0); 411 | UIGraphicsBeginImageContext(rect.size); 412 | CGContextRef context = UIGraphicsGetCurrentContext(); 413 | CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); 414 | CGContextFillRect(context, rect); 415 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 416 | UIGraphicsEndImageContext(); 417 | return image; 418 | } 419 | 420 | /** @name Drawing methods */ 421 | #pragma mark - Drawing methods 422 | #define kLineWidth 5.0 423 | #define kThumbRadius 12.0 424 | - (CGFloat)sliderRadius { 425 | CGFloat radius = MIN(self.bounds.size.width/2, self.bounds.size.height/2); 426 | radius -= MAX(kLineWidth, kThumbRadius); 427 | return radius; 428 | } 429 | - (void)drawThumbAtPoint:(CGPoint)sliderButtonCenterPoint inContext:(CGContextRef)context { 430 | UIGraphicsPushContext(context); 431 | CGContextBeginPath(context); 432 | 433 | CGContextMoveToPoint(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y); 434 | CGContextAddArc(context, sliderButtonCenterPoint.x, sliderButtonCenterPoint.y, kThumbRadius, 0.0, 2*M_PI, NO); 435 | 436 | CGContextFillPath(context); 437 | UIGraphicsPopContext(); 438 | } 439 | 440 | - (CGPoint)drawCircularTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context { 441 | UIGraphicsPushContext(context); 442 | CGContextBeginPath(context); 443 | 444 | // float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI); 445 | float angleFromTrack = 0; 446 | CGFloat startAngle = -M_PI_2; 447 | CGFloat endAngle = startAngle + angleFromTrack; 448 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO); 449 | 450 | CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context); 451 | 452 | CGContextStrokePath(context); 453 | UIGraphicsPopContext(); 454 | 455 | return arcEndPoint; 456 | } 457 | 458 | - (CGPoint)drawPieTrack:(float)track atPoint:(CGPoint)center withRadius:(CGFloat)radius inContext:(CGContextRef)context { 459 | UIGraphicsPushContext(context); 460 | 461 | // float angleFromTrack = translateValueFromSourceIntervalToDestinationInterval(track, self.minimumValue, self.maximumValue, 0, 2*M_PI); 462 | float angleFromTrack = 0; 463 | CGFloat startAngle = -M_PI_2; 464 | CGFloat endAngle = startAngle + angleFromTrack; 465 | CGContextMoveToPoint(context, center.x, center.y); 466 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, NO); 467 | 468 | CGPoint arcEndPoint = CGContextGetPathCurrentPoint(context); 469 | 470 | CGContextClosePath(context); 471 | CGContextFillPath(context); 472 | UIGraphicsPopContext(); 473 | 474 | return arcEndPoint; 475 | } 476 | 477 | /** @name Thumb management methods */ 478 | #pragma mark - Thumb management methods 479 | - (BOOL)isPointInThumb:(CGPoint)point { 480 | CGRect thumbTouchRect = CGRectMake(self.thumbCenterPoint.x - kThumbRadius, self.thumbCenterPoint.y - kThumbRadius, kThumbRadius*2, kThumbRadius*2); 481 | return CGRectContainsPoint(thumbTouchRect, point); 482 | } 483 | 484 | /** @name UIGestureRecognizer management methods */ 485 | #pragma mark - UIGestureRecognizer management methods 486 | - (void)panGestureHappened:(UIPanGestureRecognizer *)panGestureRecognizer { 487 | CGPoint tapLocation = [panGestureRecognizer locationInView:self]; 488 | switch (panGestureRecognizer.state) { 489 | case UIGestureRecognizerStateChanged: { 490 | 491 | self.thumbCenterPoint = tapLocation; 492 | self.value = tapLocation.x/CGRectGetMaxX(self.bounds); 493 | break; 494 | } 495 | case UIGestureRecognizerStateEnded: 496 | if (!self.isContinuous) { 497 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 498 | } 499 | if ([self isPointInThumb:tapLocation]) { 500 | [self sendActionsForControlEvents:UIControlEventTouchUpInside]; 501 | } 502 | else { 503 | [self sendActionsForControlEvents:UIControlEventTouchUpOutside]; 504 | } 505 | break; 506 | default: 507 | break; 508 | } 509 | } 510 | - (void)tapGestureHappened:(UITapGestureRecognizer *)tapGestureRecognizer { 511 | if (tapGestureRecognizer.state == UIGestureRecognizerStateEnded) { 512 | CGPoint tapLocation = [tapGestureRecognizer locationInView:self]; 513 | self.sliderAbel = [self isPointInThumb:tapLocation]; 514 | } 515 | } 516 | 517 | /** @name Touches Methods */ 518 | #pragma mark - Touches Methods 519 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 520 | [super touchesBegan:touches withEvent:event]; 521 | 522 | UITouch *touch = [touches anyObject]; 523 | CGPoint touchLocation = [touch locationInView:self]; 524 | if ([self isPointInThumb:touchLocation]) { 525 | [self sendActionsForControlEvents:UIControlEventTouchDown]; 526 | } 527 | } 528 | 529 | - (void)handleSliderValueChangedEvent:(NHSliderEvent)event { 530 | self.event = [event copy]; 531 | } 532 | 533 | 534 | @end 535 | 536 | #pragma mark -- state view -- 537 | 538 | @interface UIImage (NHHelper) 539 | 540 | - (UIImage *) imageWithTintColor:(UIColor *)tintColor; 541 | 542 | @end 543 | 544 | @implementation UIImage (NHHelper) 545 | 546 | - (UIImage *) imageWithTintColor:(UIColor *)tintColor { 547 | //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. 548 | UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); 549 | [tintColor setFill]; 550 | CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); 551 | UIRectFill(bounds); 552 | 553 | //Draw the tinted image in context 554 | [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; 555 | 556 | UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); 557 | UIGraphicsEndImageContext(); 558 | 559 | return tintedImage; 560 | } 561 | 562 | @end 563 | 564 | typedef void(^NHStateOnceMoreEvent)(void); 565 | 566 | @interface NHGraphicState : UIView 567 | 568 | @property (nonatomic, strong) UIImageView *iconView; 569 | @property (nonatomic, strong) UILabel *titleLabel,*subLabel; 570 | @property (nonatomic, strong) UIButton *moreBtn; 571 | @property (nonatomic, strong) UIActivityIndicatorView *indicatorView; 572 | 573 | @property (nonatomic, copy) NHStateOnceMoreEvent event; 574 | 575 | - (void)loadingState:(BOOL)load; 576 | 577 | - (void)handleStateOnceMoreEvent:(NHStateOnceMoreEvent)event; 578 | 579 | @end 580 | 581 | @implementation NHGraphicState 582 | 583 | - (id)initWithFrame:(CGRect)frame { 584 | self = [super initWithFrame:frame]; 585 | if (self) { 586 | [self __initSetup]; 587 | } 588 | return self; 589 | } 590 | 591 | - (id)initWithCoder:(NSCoder *)aDecoder { 592 | self = [super initWithCoder:aDecoder]; 593 | if (self) { 594 | [self __initSetup]; 595 | } 596 | return self; 597 | } 598 | 599 | - (void)__initSetup { 600 | CGFloat m_icon_size = PBSLIDER_SIZE; 601 | CGSize size = self.bounds.size; 602 | CGRect bounds = CGRectMake((size.width-m_icon_size)*0.5, m_icon_size, m_icon_size, m_icon_size); 603 | UIImageView *imgv = [[UIImageView alloc] initWithFrame:bounds]; 604 | [self addSubview:imgv]; 605 | self.iconView = imgv; 606 | 607 | bounds = CGRectMake(0, m_icon_size*2, size.width, 30); 608 | UILabel *label = [[UILabel alloc] initWithFrame:bounds]; 609 | label.font = [UIFont boldSystemFontOfSize:20]; 610 | label.textAlignment = NSTextAlignmentCenter; 611 | label.textColor = [UIColor whiteColor]; 612 | [self addSubview:label]; 613 | self.titleLabel = label; 614 | 615 | bounds.origin.y += 30; 616 | label = [[UILabel alloc] initWithFrame:bounds]; 617 | label.font = [UIFont boldSystemFontOfSize:15]; 618 | label.textAlignment = NSTextAlignmentCenter; 619 | label.textColor = [UIColor whiteColor]; 620 | [self addSubview:label]; 621 | self.subLabel = label; 622 | 623 | CGFloat m_btn_size = m_icon_size * 2; 624 | bounds = CGRectMake((size.width-m_btn_size)*0.5, m_icon_size*3.5, m_btn_size, 30); 625 | UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 626 | btn.frame = bounds; 627 | [btn setTitle:@"再试一次" forState:UIControlStateNormal]; 628 | [btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 629 | [btn addTarget:self action:@selector(onceMoreEvent) forControlEvents:UIControlEventTouchUpInside]; 630 | [self addSubview:btn]; 631 | self.moreBtn = btn; 632 | btn.hidden = true; 633 | 634 | UIActivityIndicatorView *actor = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 635 | actor.hidesWhenStopped = true; 636 | [self addSubview:actor]; 637 | self.indicatorView = actor; 638 | CGPoint mCenter = CGPointMake(CGRectGetWidth(self.bounds)*0.5, CGRectGetHeight(self.bounds)*0.5); 639 | actor.center = mCenter; 640 | } 641 | 642 | - (void)loadingState:(BOOL)load { 643 | self.iconView.hidden = load; 644 | self.titleLabel.hidden = load; 645 | self.subLabel.hidden = load; 646 | self.moreBtn.hidden = load; 647 | if (load) { 648 | [self.indicatorView startAnimating]; 649 | self.backgroundColor = [UIColor lightGrayColor]; 650 | }else{ 651 | [self.indicatorView stopAnimating]; 652 | } 653 | } 654 | 655 | - (void)handleStateOnceMoreEvent:(NHStateOnceMoreEvent)event { 656 | self.event = [event copy]; 657 | } 658 | 659 | - (void)onceMoreEvent { 660 | if (_event) { 661 | _event(); 662 | } 663 | } 664 | 665 | @end 666 | 667 | #pragma mark -- Custom Graphic -- 668 | 669 | struct PBShapeBumper { 670 | BOOL arrow_left; 671 | BOOL arrow_up; 672 | BOOL m_h_isBump; 673 | BOOL m_v_isBump; 674 | CGPoint m_center; 675 | int m_ball_radius; 676 | int m_rect_radius; 677 | }; 678 | 679 | typedef struct PBShapeBumper PBShapeBumper; 680 | 681 | @interface NHGraphCoder () 682 | 683 | @property (nonatomic, strong) UIImage *img, *innerImg, *flagImg; 684 | @property (nonatomic, copy) NHGraphEvent event; 685 | @property (nonatomic) PBShapeBumper shapeBumper; 686 | @property (nonatomic, strong) UIBezierPath *shapePath; 687 | @property (nonatomic, strong) UIImageView *mFlag; 688 | 689 | @property (nonatomic, strong) NHGraphicSlider *slider; 690 | @property (nonatomic, strong) NHGraphicState *stater; 691 | 692 | @property (nonatomic, assign) BOOL endDetect; 693 | 694 | @end 695 | 696 | static NHGraphCoder *instance = nil; 697 | 698 | @implementation NHGraphCoder 699 | 700 | - (void)dealloc { 701 | _img = nil; 702 | _event = nil; 703 | } 704 | 705 | //+ (NHGraphCoder *)shared { 706 | // static dispatch_once_t onceToken; 707 | // dispatch_once(&onceToken, ^{ 708 | // if (instance == nil) { 709 | // instance = [[NHGraphCoder alloc] init]; 710 | // } 711 | // }); 712 | // return instance; 713 | //} 714 | 715 | - (void)setFrame:(CGRect)frame { 716 | [super setFrame:frame]; 717 | } 718 | 719 | - (void)setBounds:(CGRect)bounds { 720 | [super setBounds:bounds]; 721 | } 722 | 723 | + (NHGraphCoder *)codeWithImage:(UIImage *)img { 724 | return [[NHGraphCoder alloc] initWithImage:img]; 725 | } 726 | 727 | - (void)handleGraphicCoderVerifyEvent:(NHGraphEvent)event { 728 | self.event = [event copy]; 729 | } 730 | 731 | //- (id)init { 732 | // self = [super init]; 733 | // if (self) { 734 | // } 735 | // return self; 736 | //} 737 | // 738 | //- (id)initWithFrame:(CGRect)frame { 739 | // self = [super initWithFrame:frame]; 740 | // if (self) { 741 | // [self __initSetup]; 742 | // } 743 | // return self; 744 | //} 745 | // 746 | //- (id)initWithCoder:(NSCoder *)aDecoder { 747 | // self = [super initWithCoder:aDecoder]; 748 | // if (self) { 749 | // [self __initSetup]; 750 | // } 751 | // return self; 752 | //} 753 | 754 | - (id)initWithImage:(UIImage *)img { 755 | 756 | NHGraphCoder *coder = [NHGraphCoder new]; 757 | CGSize tmpSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 758 | CGRect bounds = (CGRect){ 759 | .origin = CGPointZero, 760 | .size = tmpSize 761 | }; 762 | img = ((img != nil)?img:[UIImage imageNamed:@"coder_default.jpg"]); 763 | [coder setFrame:bounds]; 764 | [coder __initSetupImg:img]; 765 | return coder; 766 | } 767 | 768 | - (void)setImg:(UIImage *)img { 769 | if (_img) { 770 | _img = nil; 771 | } 772 | _img = img; 773 | [self updateInnerImg:img]; 774 | } 775 | 776 | - (void)__initSetupImg:(UIImage *)img { 777 | self.img = img; 778 | [self __initSetup]; 779 | } 780 | 781 | - (void)updateInnerImg:(UIImage *)img { 782 | /* Don't resize if we already meet the required destination size. */ 783 | CGSize dstSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 784 | if (CGSizeEqualToSize(img.size, dstSize)) { 785 | self.innerImg = img; 786 | return ; 787 | } 788 | CGSize originSize = img.size; 789 | BOOL keepAspect = false; 790 | CGRect scaledImageRect = CGRectZero; 791 | 792 | CGFloat aspectWidth = dstSize.width / originSize.width; 793 | CGFloat aspectHeight = dstSize.height / originSize.height; 794 | CGFloat aspectRatio = keepAspect?MIN(aspectWidth, aspectHeight):MAX(aspectWidth, aspectHeight); 795 | 796 | scaledImageRect.size.width = originSize.width * aspectRatio; 797 | scaledImageRect.size.height = originSize.height * aspectRatio; 798 | scaledImageRect.origin.x = (dstSize.width - scaledImageRect.size.width) / 2.0f; 799 | scaledImageRect.origin.y = (dstSize.height - scaledImageRect.size.height) / 2.0f; 800 | 801 | UIGraphicsBeginImageContextWithOptions( dstSize, NO, 0 ); 802 | [img drawInRect:scaledImageRect]; 803 | UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext(); 804 | UIGraphicsEndImageContext(); 805 | 806 | self.innerImg = scaledImage; 807 | } 808 | 809 | - (void)__initSetup { 810 | 811 | //[self addSubview:self.mFlag]; 812 | 813 | [self setNeedsDisplay]; 814 | // 815 | CGFloat m_btn_size = PBSLIDER_SIZE * 0.5; 816 | CGRect bounds = CGRectMake(PBCONTENT_SIZE-m_btn_size, 0, m_btn_size, m_btn_size); 817 | // UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; 818 | // btn.frame = bounds; 819 | // // [btn setTitle:@"refresh" forState:UIControlStateNormal]; 820 | // // [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 821 | // [btn setImage:img forState:UIControlStateNormal]; 822 | // [btn addTarget:self action:@selector(refreshState) forControlEvents:UIControlEventTouchUpInside]; 823 | // [self addSubview:btn]; 824 | 825 | UIImageView *v_img = [[UIImageView alloc] init]; 826 | [self addSubview:v_img]; 827 | self.mFlag = v_img; 828 | 829 | __weak typeof(self) weakSelf = self; 830 | bounds = CGRectMake(0, PBCONTENT_SIZE-PBSLIDER_SIZE, PBCONTENT_SIZE, PBSLIDER_SIZE); 831 | NHGraphicSlider *slider = [[NHGraphicSlider alloc] initWithFrame:bounds]; 832 | slider.backgroundColor = [UIColor lightGrayColor]; 833 | [slider handleSliderValueChangedEvent:^(CGFloat p, BOOL end) { 834 | __strong typeof(weakSelf) strongSelf = weakSelf; 835 | [strongSelf sliderValueChanged:p]; 836 | if (end) { 837 | [strongSelf endDetectSliderValue:p]; 838 | } 839 | }]; 840 | [self addSubview:slider]; 841 | self.slider = slider; 842 | 843 | bounds = self.bounds; 844 | bounds.size.height -= PBSLIDER_SIZE; 845 | NHGraphicState *stater = [[NHGraphicState alloc] initWithFrame:bounds]; 846 | [stater handleStateOnceMoreEvent:^{ 847 | __strong typeof(weakSelf) strongSelf = weakSelf; 848 | [strongSelf refreshState]; 849 | }]; 850 | [self addSubview:stater]; 851 | self.stater = stater; 852 | stater.hidden = true; 853 | } 854 | 855 | - (CAShapeLayer *)getBorderLayer { 856 | CGRect bounds = CGRectMake(0, 0, PBTile_SIZE, PBTile_SIZE); 857 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 858 | // shapeLayer.backgroundColor = [UIColor yellowColor].CGColor; 859 | shapeLayer.frame = bounds; 860 | [shapeLayer setFillRule:kCAFillRuleEvenOdd]; 861 | shapeLayer.fillColor = [[UIColor clearColor] CGColor]; 862 | shapeLayer.path = self.shapePath.CGPath; 863 | shapeLayer.shadowOffset = CGSizeMake(2, 2); 864 | shapeLayer.shadowPath = self.shapePath.CGPath; 865 | shapeLayer.shadowColor = [UIColor yellowColor].CGColor; 866 | shapeLayer.strokeColor = [UIColor yellowColor].CGColor; 867 | shapeLayer.lineWidth = 2; 868 | return shapeLayer; 869 | } 870 | 871 | - (void)updateFlag { 872 | CGRect bounds = CGRectMake(0, self.shapeBumper.m_center.y-PBTile_SIZE*0.5, PBTile_SIZE, PBTile_SIZE); 873 | self.mFlag.image = self.flagImg; 874 | self.mFlag.frame = bounds; 875 | [self.mFlag.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)]; 876 | CAShapeLayer *borderLayer = [self getBorderLayer]; 877 | [self.mFlag.layer addSublayer:borderLayer]; 878 | CAShapeLayer *shapeLayer = [self getShapeLayer]; 879 | self.mFlag.layer.mask = shapeLayer; 880 | 881 | } 882 | 883 | - (void)setShapeBumper:(PBShapeBumper)shapeBumper { 884 | _shapeBumper = shapeBumper; 885 | if (_shapePath) { 886 | _shapePath = nil; 887 | } 888 | self.shapePath = [self copyShapePath]; 889 | self.flagImg = [self getThumbiaFlagByPath:self.shapePath]; 890 | [self updateFlag]; 891 | } 892 | 893 | - (UIBezierPath *)copyShapePath { 894 | PBShapeBumper *tmpBumper = malloc(sizeof(PBShapeBumper)); 895 | tmpBumper->arrow_left = self.shapeBumper.arrow_left; 896 | tmpBumper->arrow_up = self.shapeBumper.arrow_up; 897 | tmpBumper->m_h_isBump = self.shapeBumper.m_h_isBump; 898 | tmpBumper->m_v_isBump = self.shapeBumper.m_v_isBump; 899 | tmpBumper->m_ball_radius = self.shapeBumper.m_ball_radius; 900 | tmpBumper->m_rect_radius = self.shapeBumper.m_rect_radius; 901 | tmpBumper->m_center = CGPointMake(50, 50); 902 | return [self getPathForBumper:*tmpBumper]; 903 | } 904 | 905 | - (CAShapeLayer *)getShapeLayer { 906 | CGRect bounds = CGRectMake(0, 0, PBTile_SIZE, PBTile_SIZE); 907 | CAShapeLayer *shapeLayer = [CAShapeLayer layer]; 908 | // shapeLayer.backgroundColor = [UIColor yellowColor].CGColor; 909 | shapeLayer.frame = bounds; 910 | [shapeLayer setFillRule:kCAFillRuleEvenOdd]; 911 | shapeLayer.fillColor = [[UIColor yellowColor] CGColor]; 912 | shapeLayer.path = self.shapePath.CGPath; 913 | 914 | return shapeLayer; 915 | } 916 | 917 | - (UIImage *)getShapeImage { 918 | // UIBezierPath *tmpPath = [self copyShapePath]; 919 | // return [self getThumbiaFlagByPath:tmpPath]; 920 | return self.flagImg; 921 | } 922 | 923 | - (UIBezierPath *)getShapePath { 924 | return [self shapePath]; 925 | } 926 | 927 | - (UIImage *)getThumbiaFlagByPath:(UIBezierPath *)path{ 928 | 929 | //destnation bounds 930 | //CGPoint startPoint = [self getStartPointBy:self.shapeBumper]; 931 | int x_start = self.shapeBumper.m_center.x - PBTile_SIZE * 0.5; 932 | int y_start = self.shapeBumper.m_center.y - PBTile_SIZE * 0.5; 933 | int m_width = PBTile_SIZE; 934 | UIImage *img = self.innerImg; 935 | CGFloat m_scale = img.scale; 936 | //NSLog(@"x:%d-y:%d-size:%d",x_start,y_start,m_width); 937 | 938 | CGFloat scale = MAX(m_scale, 1.0f); 939 | CGRect scaledBounds = CGRectMake(x_start * scale, y_start * scale, m_width * scale, m_width * scale); 940 | CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], scaledBounds); 941 | UIImage *croppedImage = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; 942 | CGImageRelease(imageRef); 943 | 944 | return croppedImage; 945 | } 946 | 947 | - (void)resetStateForDetect { 948 | [self refreshState]; 949 | } 950 | 951 | - (void)refreshState { 952 | NSLog(@"refresh state"); 953 | self.endDetect = false; 954 | self.slider.userInteractionEnabled = !self.endDetect; 955 | self.mFlag.hidden = self.endDetect; 956 | [self.stater loadingState:false]; 957 | self.stater.hidden = !self.endDetect; 958 | [self setNeedsDisplay]; 959 | } 960 | 961 | - (void)sliderValueChanged:(CGFloat)value { 962 | if (value < 0 || value > 1) { 963 | return; 964 | } 965 | CGRect bounds = CGRectMake((PBCONTENT_SIZE-PBTile_SIZE-PBSLIDER_SIZE)*value, self.shapeBumper.m_center.y-PBTile_SIZE*0.5, PBTile_SIZE, PBTile_SIZE); 966 | self.mFlag.frame = bounds; 967 | } 968 | //滑动slider结束后调用 969 | - (void)endDetectSliderValue:(CGFloat)value { 970 | CGFloat tmp_x = (PBCONTENT_SIZE-PBTile_SIZE-PBSLIDER_SIZE)*value; 971 | tmp_x = self.mFlag.center.x; 972 | const int error_offset = 5; 973 | BOOL success = (fabs(tmp_x - self.shapeBumper.m_center.x) <= error_offset); 974 | self.endDetect = true; 975 | [self setNeedsDisplay]; 976 | self.slider.userInteractionEnabled = !self.endDetect; 977 | self.mFlag.hidden = self.endDetect; 978 | [self.slider resetSlider]; 979 | [self endVerify:success]; 980 | } 981 | 982 | - (void)endVerify:(BOOL)success { 983 | UIImage *m_img;UIColor *m_bg_color; 984 | NSString *m_title,*m_sub_title; 985 | if (success) { 986 | m_img = [UIImage imageNamed:@"success"]; 987 | m_img = [m_img imageWithTintColor:[UIColor whiteColor]]; 988 | m_bg_color = [UIColor colorWithRed:80/255.0 green:95/255.0 blue:235/255.0 alpha:0.6]; 989 | m_title = @"验证成功"; 990 | }else{ 991 | m_img = [UIImage imageNamed:@"error"]; 992 | m_img = [m_img imageWithTintColor:[UIColor whiteColor]]; 993 | m_bg_color = [UIColor colorWithRed:197/255.0 green:100/255.0 blue:66/255.0 alpha:0.6]; 994 | m_title = @"验证失败"; 995 | m_sub_title = @"拖动滑块将悬浮图像正确拼合"; 996 | } 997 | self.stater.iconView.image = m_img; 998 | self.stater.backgroundColor = m_bg_color; 999 | self.stater.titleLabel.text = m_title; 1000 | self.stater.subLabel.text = m_sub_title; 1001 | self.stater.hidden = false; 1002 | self.stater.moreBtn.hidden = success; 1003 | 1004 | if (_event) { 1005 | _event(self, success); 1006 | } 1007 | } 1008 | 1009 | - (int)randomFrom:(int)min to:(int)max { 1010 | return arc4random() % (max-min) + min; 1011 | } 1012 | 1013 | - (CGPoint)getStartPointBy:(PBShapeBumper)bumper { 1014 | int m_rect_center_y = bumper.m_center.y; 1015 | int m_rect_center_x = bumper.m_center.x; 1016 | BOOL arrow_left = bumper.arrow_left; 1017 | BOOL arrow_up = bumper.arrow_up; 1018 | BOOL m_h_isBump = bumper.m_h_isBump; 1019 | BOOL m_v_isBump = bumper.m_v_isBump; 1020 | int m_ball_radius = bumper.m_ball_radius; 1021 | int m_rect_radius = bumper.m_rect_radius; 1022 | //destnation bounds 1023 | CGFloat x_start, y_start; 1024 | if (arrow_left && m_h_isBump) { 1025 | //左凸 1026 | m_rect_center_x += m_ball_radius * 1.5 * 0.5; 1027 | }else if (!arrow_left && m_h_isBump){ 1028 | //右凸 1029 | m_rect_center_x -= m_ball_radius * 1.5 * 0.5; 1030 | } 1031 | if (arrow_up && m_v_isBump) { 1032 | //上凸 1033 | m_rect_center_y += m_ball_radius * 1.5 * 0.5; 1034 | }else if (!arrow_up && m_v_isBump){ 1035 | //下凸 1036 | m_rect_center_y -= m_ball_radius * 1.5 * 0.5; 1037 | } 1038 | x_start = m_rect_center_x-m_rect_radius; 1039 | y_start = m_rect_center_y-m_rect_radius; 1040 | 1041 | return CGPointMake(x_start, y_start); 1042 | } 1043 | 1044 | - (UIBezierPath *)graphWithBallTheta:(CGFloat)theta_rad shapeBump:(PBShapeBumper)bumper{ 1045 | 1046 | // int m_rect_center_y = bumper.m_center.y; 1047 | // int m_rect_center_x = bumper.m_center.x; 1048 | BOOL arrow_left = bumper.arrow_left; 1049 | BOOL arrow_up = bumper.arrow_up; 1050 | BOOL m_h_isBump = bumper.m_h_isBump; 1051 | BOOL m_v_isBump = bumper.m_v_isBump; 1052 | int m_ball_radius = bumper.m_ball_radius; 1053 | int m_rect_radius = bumper.m_rect_radius; 1054 | int m_rectangle_size = m_rect_radius * 2; 1055 | //destnation bounds 1056 | CGPoint startPoint = [self getStartPointBy:bumper]; 1057 | CGFloat x_start = startPoint.x; 1058 | CGFloat y_start = startPoint.y; 1059 | int m_small_rect_center_x = x_start + m_rect_radius; 1060 | int m_small_rect_center_y = y_start + m_rect_radius; 1061 | NSLog(@"start:%@--ball:%d*rect:%d",NSStringFromCGPoint(startPoint),m_ball_radius,m_rect_radius); 1062 | 1063 | UIBezierPath *path = [UIBezierPath bezierPath]; 1064 | // CGFloat theta_rad = acos(0.5); 1065 | //NSLog(@"theta :%f",theta_rad); 1066 | CGFloat xuan_half = m_ball_radius*sin(theta_rad); 1067 | CGFloat m_tmp_len = m_rect_radius-xuan_half; 1068 | 1069 | //begin path 1070 | //先移动到左上角 逆时针绘制 1071 | CGPoint tmp_p = CGPointMake(x_start, y_start); 1072 | [path moveToPoint:tmp_p]; 1073 | //left 1074 | tmp_p = CGPointMake(x_start, y_start+m_tmp_len); 1075 | [path addLineToPoint:tmp_p]; 1076 | if (arrow_left) { 1077 | if (m_h_isBump) { 1078 | //是凸出 1079 | CGPoint tmp_center = CGPointMake(x_start-m_ball_radius*0.5, m_small_rect_center_y); 1080 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI*2-theta_rad) endAngle:theta_rad clockwise:false]; 1081 | }else{ 1082 | //凹陷 1083 | CGPoint tmp_center = CGPointMake(x_start+m_ball_radius*0.5, m_small_rect_center_y); 1084 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI+theta_rad) endAngle:(M_PI-theta_rad) clockwise:true]; 1085 | } 1086 | } 1087 | tmp_p.y += m_tmp_len + xuan_half*2; 1088 | [path addLineToPoint:tmp_p]; 1089 | 1090 | //bottom 1091 | CGFloat theta_rad_left = M_PI_2 - theta_rad; 1092 | tmp_p.x += m_tmp_len; 1093 | [path addLineToPoint:tmp_p]; 1094 | if (!arrow_up) { 1095 | if (m_v_isBump) { 1096 | //凸出 1097 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_rectangle_size+m_ball_radius*0.5); 1098 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI+theta_rad_left) endAngle:(M_PI*2-theta_rad_left) clockwise:false]; 1099 | }else{ 1100 | //凹陷 1101 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_rectangle_size-m_ball_radius*0.5); 1102 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI-theta_rad_left) endAngle:(M_PI*2+theta_rad_left) clockwise:true]; 1103 | } 1104 | } 1105 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start+m_rectangle_size); 1106 | [path addLineToPoint:tmp_p]; 1107 | //right 1108 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start+m_rectangle_size-m_tmp_len); 1109 | [path addLineToPoint:tmp_p]; 1110 | if (!arrow_left) { 1111 | if (m_h_isBump) { 1112 | //凸出 1113 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x+m_rect_radius+m_ball_radius*0.5, m_small_rect_center_y); 1114 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(M_PI-theta_rad) endAngle:(M_PI+theta_rad) clockwise:false]; 1115 | }else{ 1116 | //凹陷 1117 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x+m_rect_radius-m_ball_radius*0.5, m_small_rect_center_y); 1118 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:theta_rad endAngle:(M_PI*2-theta_rad) clockwise:true]; 1119 | } 1120 | } 1121 | tmp_p = CGPointMake(x_start+m_rectangle_size, y_start); 1122 | [path addLineToPoint:tmp_p]; 1123 | //up 1124 | tmp_p = CGPointMake(x_start+m_rectangle_size-m_tmp_len, y_start); 1125 | [path addLineToPoint:tmp_p]; 1126 | if (arrow_up) { 1127 | if (m_v_isBump) { 1128 | //凸出 1129 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start-m_ball_radius*0.5); 1130 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(theta_rad_left) endAngle:(M_PI-theta_rad_left) clockwise:false]; 1131 | }else{ 1132 | //凹陷 1133 | CGPoint tmp_center = CGPointMake(m_small_rect_center_x, y_start+m_ball_radius*0.5); 1134 | [path addArcWithCenter:tmp_center radius:m_ball_radius startAngle:(-theta_rad_left) endAngle:(M_PI+theta_rad_left) clockwise:true]; 1135 | } 1136 | } 1137 | tmp_p = CGPointMake(x_start, y_start); 1138 | [path addLineToPoint:tmp_p]; 1139 | //test 1140 | 1141 | [path closePath]; 1142 | 1143 | return path; 1144 | } 1145 | 1146 | - (UIBezierPath *)generateRandomPath { 1147 | 1148 | int m_tmp_direction_h = arc4random() % 2; 1149 | BOOL arrow_left = m_tmp_direction_h == 0; 1150 | int m_h_Bump = arc4random() % 2; 1151 | BOOL m_h_isBump = (m_h_Bump == 0); 1152 | int m_tmp_direction_v = arc4random() % 2; 1153 | BOOL arrow_up = m_tmp_direction_v == 0; 1154 | BOOL m_v_isBump = !m_h_isBump; 1155 | 1156 | int m_radius_scale = PB_BALL_RADIUS_SCALE; 1157 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1158 | // int m_ball_radius = PBTile_SIZE/((1+m_ball_cross_scale)+m_radius_scale); 1159 | int m_ball_radius = PBTile_SIZE/(m_radius_scale*2); 1160 | int m_rectangle_size = m_ball_radius*m_radius_scale; 1161 | int m_rect_radius = m_rectangle_size * 0.5; 1162 | 1163 | int min = PBTile_SIZE; 1164 | int max = PBCONTENT_SIZE-min; 1165 | int m_rect_center_y = [self randomFrom:min to:max]; 1166 | int m_rect_center_x = [self randomFrom:min to:max]; 1167 | NSLog(@"random path center x:%d---y:%d",m_rect_center_x,m_rect_center_y); 1168 | PBShapeBumper bumper = { 1169 | .arrow_up = arrow_up, 1170 | .arrow_left = arrow_left, 1171 | .m_h_isBump = m_h_isBump, 1172 | .m_v_isBump = m_v_isBump, 1173 | .m_ball_radius = m_ball_radius, 1174 | .m_rect_radius = m_rect_radius, 1175 | .m_center = CGPointMake(m_rect_center_x, m_rect_center_y) 1176 | }; 1177 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1178 | self.shapeBumper = bumper; 1179 | 1180 | return m_path; 1181 | } 1182 | 1183 | - (UIBezierPath *)getPathForBumper:(PBShapeBumper)bumper { 1184 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1185 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1186 | 1187 | return m_path; 1188 | } 1189 | 1190 | - (UIBezierPath *)path4Size:(CGSize)size { 1191 | //bezier path 1192 | 1193 | int m_tmp_direction_h = arc4random() % 2; 1194 | BOOL arrow_left = m_tmp_direction_h == 0; 1195 | int m_h_Bump = arc4random() % 2; 1196 | BOOL m_h_isBump = (m_h_Bump == 0); 1197 | int m_tmp_direction_v = arc4random() % 2; 1198 | BOOL arrow_up = m_tmp_direction_v == 0; 1199 | BOOL m_v_isBump = !m_h_isBump; 1200 | 1201 | int m_radius_scale = 4; 1202 | float m_ball_cross_scale = 0.5;//球心外到边框距离倍数 1203 | int m_avaliable_size = size.width; 1204 | int m_ball_radius = m_avaliable_size/((1+m_ball_cross_scale)+m_radius_scale); 1205 | int m_rectangle_size = m_ball_radius*m_radius_scale; 1206 | int m_rect_radius = m_rectangle_size * 0.5; 1207 | int m_rect_center_y = m_avaliable_size * 0.5; 1208 | int m_rect_center_x = m_avaliable_size * 0.5; 1209 | 1210 | PBShapeBumper bumper = { 1211 | .arrow_up = arrow_up, 1212 | .arrow_left = arrow_left, 1213 | .m_h_isBump = m_h_isBump, 1214 | .m_v_isBump = m_v_isBump, 1215 | .m_ball_radius = m_ball_radius, 1216 | .m_rect_radius = m_rect_radius, 1217 | .m_center = CGPointMake(m_rect_center_x, m_rect_center_y) 1218 | }; 1219 | UIBezierPath *m_path = [self graphWithBallTheta:acos(m_ball_cross_scale) shapeBump:bumper]; 1220 | 1221 | return m_path; 1222 | } 1223 | 1224 | // Only override drawRect: if you perform custom drawing. 1225 | // An empty implementation adversely affects performance during animation. 1226 | - (void)drawRect:(CGRect)rect { 1227 | // Drawing code 1228 | [self.innerImg drawInRect:rect]; 1229 | 1230 | UIColor *maskColor = [UIColor colorWithWhite:0 alpha:0.7]; 1231 | // maskColor = [UIColor blackColor]; 1232 | //绘制路径 1233 | if (!self.endDetect) { 1234 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 1235 | UIBezierPath *path = [self generateRandomPath]; 1236 | CGContextAddPath(ctx, path.CGPath); 1237 | CGContextSetFillColorWithColor(ctx, maskColor.CGColor); 1238 | CGContextFillPath(ctx); 1239 | } 1240 | } 1241 | 1242 | #pragma mark -- Net Graphic Code -- 1243 | + (NHGraphCoder *)codeWithURL:(NSString *)url { 1244 | return [[NHGraphCoder alloc] initWithURL:url]; 1245 | } 1246 | 1247 | - (id)initWithURL:(NSString *)url { 1248 | NHGraphCoder *coder = [NHGraphCoder new]; 1249 | CGSize tmpSize = (CGSize){PBCONTENT_SIZE,PBCONTENT_SIZE}; 1250 | CGRect bounds = (CGRect){ 1251 | .origin = CGPointZero, 1252 | .size = tmpSize 1253 | }; 1254 | [coder setBounds:bounds]; 1255 | // coder.backgroundColor = [UIColor lightGrayColor]; 1256 | if (url == nil) { 1257 | UIImage *img = [UIImage imageNamed:@"coder_default.jpg"]; 1258 | [coder __initSetupImg:img]; 1259 | }else{ 1260 | coder.endDetect = true; 1261 | [coder __initSetup]; 1262 | coder.mFlag.hidden = true; 1263 | coder.slider.userInteractionEnabled = false; 1264 | coder.stater.hidden = false; 1265 | [coder.stater loadingState:true]; 1266 | __weak typeof(coder) weakCoder = coder; 1267 | [[JGAFImageCache sharedInstance] imageForURL:url completion:^(UIImage * _Nullable image) { 1268 | __strong typeof(weakCoder) strongCoder = weakCoder; 1269 | if (image == nil) { 1270 | NSLog(@"下载图片失败!将使用默认图片本地验证"); 1271 | image = [UIImage imageNamed:@"coder_default.jpg"]; 1272 | } 1273 | [strongCoder setImg:image]; 1274 | [strongCoder resetStateForDetect]; 1275 | }]; 1276 | } 1277 | return coder; 1278 | } 1279 | 1280 | @end 1281 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/coder_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/coder_default.jpg -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/error.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/error@2x.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/error@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/error@3x.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/success.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/success@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/success@2x.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/Resources/success@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/Classes/Resources/success@3x.png -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/Classes/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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | UILaunchStoryboardName 31 | LaunchScreen 32 | UIMainStoryboardFile 33 | Main 34 | UIRequiredDeviceCapabilities 35 | 36 | armv7 37 | 38 | UISupportedInterfaceOrientations 39 | 40 | UIInterfaceOrientationPortrait 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /NHGraphCoderPro/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.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 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /NHGraphCoderPro/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.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 "ViewController.h" 10 | #import "NHGraphCoder.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | self.view.backgroundColor = [UIColor cyanColor]; 23 | CGSize size = self.view.bounds.size; 24 | 25 | // //native image to detect 26 | // UIImage *img__ = [UIImage imageNamed:@"test_2.jpg"]; 27 | // NHGraphCoder *coder = [NHGraphCoder codeWithImage:img__]; 28 | // coder.center = CGPointMake(size.width*0.5, size.height*0.5); 29 | // [coder handleGraphicCoderVerifyEvent:^(NHGraphCoder * _Nonnull cd, BOOL success) { 30 | // NSLog(@"验证结果:%d",success); 31 | // }]; 32 | // [self.view addSubview:coder]; 33 | 34 | //network image to detect 35 | NSString *url = @"http://pic.pimg.tw/loloto/1357207442-1350656755_l.jpg?v=1357207447"; 36 | NHGraphCoder *coder = [NHGraphCoder codeWithURL:url]; 37 | coder.center = CGPointMake(size.width*0.5, size.height*0.5); 38 | [coder handleGraphicCoderVerifyEvent:^(NHGraphCoder * _Nonnull cd, BOOL success) { 39 | NSLog(@"验证结果:%d",success); 40 | }]; 41 | [self.view addSubview:coder]; 42 | } 43 | 44 | - (void)didReceiveMemoryWarning { 45 | [super didReceiveMemoryWarning]; 46 | // Dispose of any resources that can be recreated. 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /NHGraphCoderPro/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.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 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 | -------------------------------------------------------------------------------- /NHGraphCoderPro/test_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/test_1.jpg -------------------------------------------------------------------------------- /NHGraphCoderPro/test_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/test_2.jpg -------------------------------------------------------------------------------- /NHGraphCoderPro/test_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/test_3.jpg -------------------------------------------------------------------------------- /NHGraphCoderPro/test_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iFindTA/NHGraphCoderPro/815c7a1de0cf60644db9577d1e45419e9e4c2293/NHGraphCoderPro/test_5.jpg -------------------------------------------------------------------------------- /NHGraphCoderProTests/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 | -------------------------------------------------------------------------------- /NHGraphCoderProTests/NHGraphCoderProTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NHGraphCoderProTests.m 3 | // NHGraphCoderProTests 4 | // 5 | // Created by hu jiaju on 16/5/30. 6 | // Copyright © 2016年 hu jiaju. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NHGraphCoderProTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NHGraphCoderProTests 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 | -------------------------------------------------------------------------------- /NHGraphCoderProUITests/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 | -------------------------------------------------------------------------------- /NHGraphCoderProUITests/NHGraphCoderProUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NHGraphCoderProUITests.m 3 | // NHGraphCoderProUITests 4 | // 5 | // Created by hu jiaju on 16/5/30. 6 | // Copyright © 2016年 hu jiaju. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NHGraphCoderProUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NHGraphCoderProUITests 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NHGraphCoderPro 2 | ### 本地图形验证码 先上效果(v2.0 持续更新) 3 | ##### 类似于[极验验证码](http://geetest.com) 4 | 如果对你有帮助,请亮个 star! 5 | 6 | ![image](https://raw.githubusercontent.com/iFindTA/screenshots/master/graphiccode.gif) 7 | 8 | #### Dependency: 9 | - FBShimmering:facebook开源组件,[Git地址](https://github.com/facebook/Shimmer/) 10 | - JGAFImageCache:Git开源组件,[Git地址](https://github.com/jaminguy/JGAFImageCache) 11 | 12 | #### Features: 13 | ``` 14 | 1,支持本地图片作为验证码模板,无需后台支持 15 | 2,支持网络图片作为验证码模版(只需一个URL) 16 | 3,可统计验证成功、失败次数(自己计数) 17 | 4,不依赖于其他平台闭源SDK、framework 18 | ``` 19 | 20 | #### Usage: 21 | ###### 使用比较简单 22 | 创建: 23 | - 本地图片验证 24 | ```ObjectiveC 25 | //native image to detect 26 | CGSize size = self.view.bounds.size; 27 | UIImage *img__ = [UIImage imageNamed:@"test_4.jpg"]; 28 | NHGraphCoder *coder = [NHGraphCoder codeWithImage:img__]; 29 | coder.center = CGPointMake(size.width*0.5, size.height*0.5); 30 | [coder handleGraphicCoderVerifyEvent:^(NHGraphCoder * _Nonnull cd, BOOL success) { 31 | NSLog(@"验证结果:%d",success); 32 | }]; 33 | [self.view addSubview:coder]; 34 | ``` 35 | - 网络图片验证 36 | ```ObjectiveC 37 | //network image to detect 38 | NSString *url = @"http://pic.pimg.tw/loloto/1357207442-1350656755_l.jpg?v=1357207447"; 39 | NHGraphCoder *coder = [NHGraphCoder codeWithURL:url]; 40 | coder.center = CGPointMake(size.width*0.5, size.height*0.5); 41 | [coder handleGraphicCoderVerifyEvent:^(NHGraphCoder * _Nonnull cd, BOOL success) { 42 | NSLog(@"验证结果:%d",success); 43 | }]; 44 | [self.view addSubview:coder]; 45 | ``` 46 | 47 | 刷新图形: 48 | ```ObjectiveC 49 | [coder resetStateForDetect]; 50 | ``` 51 | 52 | #### Feedback: 53 | nanhujiaju@gmail.com --------------------------------------------------------------------------------