└── AsyncDrawDemo ├── AsyncDrawDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── Stk.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── Stk.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── AsyncDrawDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Core │ ├── AsyncDraw │ │ ├── ADLayer.h │ │ ├── ADLayer.m │ │ ├── ADManager.h │ │ ├── ADManager.m │ │ ├── ADOperation.h │ │ ├── ADOperation.m │ │ ├── ADQueue.h │ │ ├── ADQueue.m │ │ ├── ADTask.h │ │ └── ADTask.m │ ├── AsyncUI │ │ ├── ADImageView.h │ │ ├── ADImageView.m │ │ ├── ADLabel.h │ │ ├── ADLabel.m │ │ ├── ADTableView.h │ │ └── ADTableView.m │ └── Helper │ │ ├── iColor.h │ │ ├── iConstant.h │ │ └── iMethods.h ├── Info.plist ├── Main │ ├── Controller │ │ ├── ViewController.h │ │ └── ViewController.m │ ├── Model │ │ ├── TestModel.h │ │ └── TestModel.m │ └── View │ │ ├── TestTableViewCell.h │ │ └── TestTableViewCell.m ├── SceneDelegate.h ├── SceneDelegate.m ├── Vendor │ └── YYFPSLabel │ │ ├── YYFPSLabel.h │ │ ├── YYFPSLabel.m │ │ ├── YYWeakProxy.h │ │ └── YYWeakProxy.m └── main.m ├── AsyncDrawDemoTests ├── AsyncDrawDemoTests.m └── Info.plist └── AsyncDrawDemoUITests ├── AsyncDrawDemoUITests.m └── Info.plist /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 58113D5B255E54CE002CF6BB /* ADImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113D5A255E54CE002CF6BB /* ADImageView.m */; }; 11 | 58113D8B255E68A3002CF6BB /* TestTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113D89255E68A2002CF6BB /* TestTableViewCell.m */; }; 12 | 58113D91255E68B8002CF6BB /* ADLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113D8F255E68B8002CF6BB /* ADLabel.m */; }; 13 | 58113DB0255E6F20002CF6BB /* YYFPSLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113DAF255E6F20002CF6BB /* YYFPSLabel.m */; }; 14 | 58113DB6255E6F3F002CF6BB /* YYWeakProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113DB4255E6F3F002CF6BB /* YYWeakProxy.m */; }; 15 | 58113E1E255FCF33002CF6BB /* ADLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113E1D255FCF33002CF6BB /* ADLayer.m */; }; 16 | 58113E24255FCF5F002CF6BB /* ADTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113E23255FCF5F002CF6BB /* ADTask.m */; }; 17 | 58113E522560F3D8002CF6BB /* ADQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113E512560F3D8002CF6BB /* ADQueue.m */; }; 18 | 58113E5B25612159002CF6BB /* ADManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113E5A25612159002CF6BB /* ADManager.m */; }; 19 | 58113E6825621FF9002CF6BB /* TestModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 58113E6725621FF9002CF6BB /* TestModel.m */; }; 20 | 584C01672559572000B14346 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C01662559572000B14346 /* AppDelegate.m */; }; 21 | 584C016A2559572000B14346 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C01692559572000B14346 /* SceneDelegate.m */; }; 22 | 584C016D2559572000B14346 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C016C2559572000B14346 /* ViewController.m */; }; 23 | 584C01702559572000B14346 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 584C016E2559572000B14346 /* Main.storyboard */; }; 24 | 584C01722559572300B14346 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 584C01712559572300B14346 /* Assets.xcassets */; }; 25 | 584C01752559572300B14346 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 584C01732559572300B14346 /* LaunchScreen.storyboard */; }; 26 | 584C01782559572300B14346 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C01772559572300B14346 /* main.m */; }; 27 | 584C01822559572300B14346 /* AsyncDrawDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C01812559572300B14346 /* AsyncDrawDemoTests.m */; }; 28 | 584C018D2559572300B14346 /* AsyncDrawDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 584C018C2559572300B14346 /* AsyncDrawDemoUITests.m */; }; 29 | 58C9ADD92569689C0072D564 /* ADOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C9ADD82569689C0072D564 /* ADOperation.m */; }; 30 | 58C9ADDF25697E810072D564 /* ADTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C9ADDE25697E810072D564 /* ADTableView.m */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 584C017E2559572300B14346 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 584C015A2559572000B14346 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 584C01612559572000B14346; 39 | remoteInfo = AsyncDrawDemo; 40 | }; 41 | 584C01892559572300B14346 /* PBXContainerItemProxy */ = { 42 | isa = PBXContainerItemProxy; 43 | containerPortal = 584C015A2559572000B14346 /* Project object */; 44 | proxyType = 1; 45 | remoteGlobalIDString = 584C01612559572000B14346; 46 | remoteInfo = AsyncDrawDemo; 47 | }; 48 | /* End PBXContainerItemProxy section */ 49 | 50 | /* Begin PBXFileReference section */ 51 | 58113D58255E5336002CF6BB /* iConstant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iConstant.h; sourceTree = ""; }; 52 | 58113D59255E54CE002CF6BB /* ADImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADImageView.h; sourceTree = ""; }; 53 | 58113D5A255E54CE002CF6BB /* ADImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADImageView.m; sourceTree = ""; }; 54 | 58113D7B255E59DF002CF6BB /* iMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iMethods.h; sourceTree = ""; }; 55 | 58113D7F255E5A4A002CF6BB /* iColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iColor.h; sourceTree = ""; }; 56 | 58113D89255E68A2002CF6BB /* TestTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestTableViewCell.m; sourceTree = ""; }; 57 | 58113D8A255E68A3002CF6BB /* TestTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestTableViewCell.h; sourceTree = ""; }; 58 | 58113D8F255E68B8002CF6BB /* ADLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADLabel.m; sourceTree = ""; }; 59 | 58113D90255E68B8002CF6BB /* ADLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADLabel.h; sourceTree = ""; }; 60 | 58113DAE255E6F20002CF6BB /* YYFPSLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYFPSLabel.h; sourceTree = ""; }; 61 | 58113DAF255E6F20002CF6BB /* YYFPSLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYFPSLabel.m; sourceTree = ""; }; 62 | 58113DB4255E6F3F002CF6BB /* YYWeakProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYWeakProxy.m; sourceTree = ""; }; 63 | 58113DB5255E6F3F002CF6BB /* YYWeakProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYWeakProxy.h; sourceTree = ""; }; 64 | 58113E1C255FCF33002CF6BB /* ADLayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADLayer.h; sourceTree = ""; }; 65 | 58113E1D255FCF33002CF6BB /* ADLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADLayer.m; sourceTree = ""; }; 66 | 58113E22255FCF5F002CF6BB /* ADTask.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADTask.h; sourceTree = ""; }; 67 | 58113E23255FCF5F002CF6BB /* ADTask.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADTask.m; sourceTree = ""; }; 68 | 58113E502560F3D8002CF6BB /* ADQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADQueue.h; sourceTree = ""; }; 69 | 58113E512560F3D8002CF6BB /* ADQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADQueue.m; sourceTree = ""; }; 70 | 58113E5925612159002CF6BB /* ADManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADManager.h; sourceTree = ""; }; 71 | 58113E5A25612159002CF6BB /* ADManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADManager.m; sourceTree = ""; }; 72 | 58113E6625621FF9002CF6BB /* TestModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestModel.h; sourceTree = ""; }; 73 | 58113E6725621FF9002CF6BB /* TestModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TestModel.m; sourceTree = ""; }; 74 | 584C01622559572000B14346 /* AsyncDrawDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AsyncDrawDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | 584C01652559572000B14346 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 76 | 584C01662559572000B14346 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 77 | 584C01682559572000B14346 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 78 | 584C01692559572000B14346 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 79 | 584C016B2559572000B14346 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 80 | 584C016C2559572000B14346 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 81 | 584C016F2559572000B14346 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 82 | 584C01712559572300B14346 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 83 | 584C01742559572300B14346 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 84 | 584C01762559572300B14346 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85 | 584C01772559572300B14346 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 86 | 584C017D2559572300B14346 /* AsyncDrawDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AsyncDrawDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 87 | 584C01812559572300B14346 /* AsyncDrawDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AsyncDrawDemoTests.m; sourceTree = ""; }; 88 | 584C01832559572300B14346 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 89 | 584C01882559572300B14346 /* AsyncDrawDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AsyncDrawDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 90 | 584C018C2559572300B14346 /* AsyncDrawDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AsyncDrawDemoUITests.m; sourceTree = ""; }; 91 | 584C018E2559572300B14346 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 92 | 58C9ADD72569689C0072D564 /* ADOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADOperation.h; sourceTree = ""; }; 93 | 58C9ADD82569689C0072D564 /* ADOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADOperation.m; sourceTree = ""; }; 94 | 58C9ADDD25697E810072D564 /* ADTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADTableView.h; sourceTree = ""; }; 95 | 58C9ADDE25697E810072D564 /* ADTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADTableView.m; sourceTree = ""; }; 96 | /* End PBXFileReference section */ 97 | 98 | /* Begin PBXFrameworksBuildPhase section */ 99 | 584C015F2559572000B14346 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | 584C017A2559572300B14346 /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | 584C01852559572300B14346 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | ); 118 | runOnlyForDeploymentPostprocessing = 0; 119 | }; 120 | /* End PBXFrameworksBuildPhase section */ 121 | 122 | /* Begin PBXGroup section */ 123 | 58113D86255E67E9002CF6BB /* Main */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 58113D87255E6801002CF6BB /* Controller */, 127 | 58113D88255E6808002CF6BB /* View */, 128 | 58113E6525621EA1002CF6BB /* Model */, 129 | ); 130 | path = Main; 131 | sourceTree = ""; 132 | }; 133 | 58113D87255E6801002CF6BB /* Controller */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 584C016B2559572000B14346 /* ViewController.h */, 137 | 584C016C2559572000B14346 /* ViewController.m */, 138 | ); 139 | path = Controller; 140 | sourceTree = ""; 141 | }; 142 | 58113D88255E6808002CF6BB /* View */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 58113D8A255E68A3002CF6BB /* TestTableViewCell.h */, 146 | 58113D89255E68A2002CF6BB /* TestTableViewCell.m */, 147 | ); 148 | path = View; 149 | sourceTree = ""; 150 | }; 151 | 58113DAD255E6EFE002CF6BB /* YYFPSLabel */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 58113DAE255E6F20002CF6BB /* YYFPSLabel.h */, 155 | 58113DAF255E6F20002CF6BB /* YYFPSLabel.m */, 156 | 58113DB5255E6F3F002CF6BB /* YYWeakProxy.h */, 157 | 58113DB4255E6F3F002CF6BB /* YYWeakProxy.m */, 158 | ); 159 | path = YYFPSLabel; 160 | sourceTree = ""; 161 | }; 162 | 58113DC6255E71F8002CF6BB /* Vendor */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 58113DAD255E6EFE002CF6BB /* YYFPSLabel */, 166 | ); 167 | path = Vendor; 168 | sourceTree = ""; 169 | }; 170 | 58113E11255FCD91002CF6BB /* AsyncUI */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 58113D90255E68B8002CF6BB /* ADLabel.h */, 174 | 58113D8F255E68B8002CF6BB /* ADLabel.m */, 175 | 58113D59255E54CE002CF6BB /* ADImageView.h */, 176 | 58113D5A255E54CE002CF6BB /* ADImageView.m */, 177 | 58C9ADDD25697E810072D564 /* ADTableView.h */, 178 | 58C9ADDE25697E810072D564 /* ADTableView.m */, 179 | ); 180 | path = AsyncUI; 181 | sourceTree = ""; 182 | }; 183 | 58113E12255FCDD8002CF6BB /* AsyncDraw */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 58113E5925612159002CF6BB /* ADManager.h */, 187 | 58113E5A25612159002CF6BB /* ADManager.m */, 188 | 58113E1C255FCF33002CF6BB /* ADLayer.h */, 189 | 58113E1D255FCF33002CF6BB /* ADLayer.m */, 190 | 58113E22255FCF5F002CF6BB /* ADTask.h */, 191 | 58113E23255FCF5F002CF6BB /* ADTask.m */, 192 | 58113E502560F3D8002CF6BB /* ADQueue.h */, 193 | 58113E512560F3D8002CF6BB /* ADQueue.m */, 194 | 58C9ADD72569689C0072D564 /* ADOperation.h */, 195 | 58C9ADD82569689C0072D564 /* ADOperation.m */, 196 | ); 197 | path = AsyncDraw; 198 | sourceTree = ""; 199 | }; 200 | 58113E342560CF89002CF6BB /* Helper */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 58113D58255E5336002CF6BB /* iConstant.h */, 204 | 58113D7F255E5A4A002CF6BB /* iColor.h */, 205 | 58113D7B255E59DF002CF6BB /* iMethods.h */, 206 | ); 207 | path = Helper; 208 | sourceTree = ""; 209 | }; 210 | 58113E6525621EA1002CF6BB /* Model */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | 58113E6625621FF9002CF6BB /* TestModel.h */, 214 | 58113E6725621FF9002CF6BB /* TestModel.m */, 215 | ); 216 | path = Model; 217 | sourceTree = ""; 218 | }; 219 | 584C01592559572000B14346 = { 220 | isa = PBXGroup; 221 | children = ( 222 | 584C01642559572000B14346 /* AsyncDrawDemo */, 223 | 584C01802559572300B14346 /* AsyncDrawDemoTests */, 224 | 584C018B2559572300B14346 /* AsyncDrawDemoUITests */, 225 | 584C01632559572000B14346 /* Products */, 226 | 7DE7B929DFD9B7ABB3A67539 /* Frameworks */, 227 | ); 228 | sourceTree = ""; 229 | }; 230 | 584C01632559572000B14346 /* Products */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 584C01622559572000B14346 /* AsyncDrawDemo.app */, 234 | 584C017D2559572300B14346 /* AsyncDrawDemoTests.xctest */, 235 | 584C01882559572300B14346 /* AsyncDrawDemoUITests.xctest */, 236 | ); 237 | name = Products; 238 | sourceTree = ""; 239 | }; 240 | 584C01642559572000B14346 /* AsyncDrawDemo */ = { 241 | isa = PBXGroup; 242 | children = ( 243 | 584C019D2559573700B14346 /* Core */, 244 | 58113D86255E67E9002CF6BB /* Main */, 245 | 58113DC6255E71F8002CF6BB /* Vendor */, 246 | 584C01652559572000B14346 /* AppDelegate.h */, 247 | 584C01662559572000B14346 /* AppDelegate.m */, 248 | 584C01682559572000B14346 /* SceneDelegate.h */, 249 | 584C01692559572000B14346 /* SceneDelegate.m */, 250 | 584C016E2559572000B14346 /* Main.storyboard */, 251 | 584C01712559572300B14346 /* Assets.xcassets */, 252 | 584C01732559572300B14346 /* LaunchScreen.storyboard */, 253 | 584C01762559572300B14346 /* Info.plist */, 254 | 584C01772559572300B14346 /* main.m */, 255 | ); 256 | path = AsyncDrawDemo; 257 | sourceTree = ""; 258 | }; 259 | 584C01802559572300B14346 /* AsyncDrawDemoTests */ = { 260 | isa = PBXGroup; 261 | children = ( 262 | 584C01812559572300B14346 /* AsyncDrawDemoTests.m */, 263 | 584C01832559572300B14346 /* Info.plist */, 264 | ); 265 | path = AsyncDrawDemoTests; 266 | sourceTree = ""; 267 | }; 268 | 584C018B2559572300B14346 /* AsyncDrawDemoUITests */ = { 269 | isa = PBXGroup; 270 | children = ( 271 | 584C018C2559572300B14346 /* AsyncDrawDemoUITests.m */, 272 | 584C018E2559572300B14346 /* Info.plist */, 273 | ); 274 | path = AsyncDrawDemoUITests; 275 | sourceTree = ""; 276 | }; 277 | 584C019D2559573700B14346 /* Core */ = { 278 | isa = PBXGroup; 279 | children = ( 280 | 58113E12255FCDD8002CF6BB /* AsyncDraw */, 281 | 58113E11255FCD91002CF6BB /* AsyncUI */, 282 | 58113E342560CF89002CF6BB /* Helper */, 283 | ); 284 | path = Core; 285 | sourceTree = ""; 286 | }; 287 | 7DE7B929DFD9B7ABB3A67539 /* Frameworks */ = { 288 | isa = PBXGroup; 289 | children = ( 290 | ); 291 | name = Frameworks; 292 | sourceTree = ""; 293 | }; 294 | /* End PBXGroup section */ 295 | 296 | /* Begin PBXNativeTarget section */ 297 | 584C01612559572000B14346 /* AsyncDrawDemo */ = { 298 | isa = PBXNativeTarget; 299 | buildConfigurationList = 584C01912559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemo" */; 300 | buildPhases = ( 301 | 584C015E2559572000B14346 /* Sources */, 302 | 584C015F2559572000B14346 /* Frameworks */, 303 | 584C01602559572000B14346 /* Resources */, 304 | ); 305 | buildRules = ( 306 | ); 307 | dependencies = ( 308 | ); 309 | name = AsyncDrawDemo; 310 | productName = AsyncDrawDemo; 311 | productReference = 584C01622559572000B14346 /* AsyncDrawDemo.app */; 312 | productType = "com.apple.product-type.application"; 313 | }; 314 | 584C017C2559572300B14346 /* AsyncDrawDemoTests */ = { 315 | isa = PBXNativeTarget; 316 | buildConfigurationList = 584C01942559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemoTests" */; 317 | buildPhases = ( 318 | C72E9B0B3F0E82C4789222C5 /* [CP] Check Pods Manifest.lock */, 319 | 584C01792559572300B14346 /* Sources */, 320 | 584C017A2559572300B14346 /* Frameworks */, 321 | 584C017B2559572300B14346 /* Resources */, 322 | ); 323 | buildRules = ( 324 | ); 325 | dependencies = ( 326 | 584C017F2559572300B14346 /* PBXTargetDependency */, 327 | ); 328 | name = AsyncDrawDemoTests; 329 | productName = AsyncDrawDemoTests; 330 | productReference = 584C017D2559572300B14346 /* AsyncDrawDemoTests.xctest */; 331 | productType = "com.apple.product-type.bundle.unit-test"; 332 | }; 333 | 584C01872559572300B14346 /* AsyncDrawDemoUITests */ = { 334 | isa = PBXNativeTarget; 335 | buildConfigurationList = 584C01972559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemoUITests" */; 336 | buildPhases = ( 337 | 99C288E44E687454D19ED8F7 /* [CP] Check Pods Manifest.lock */, 338 | 584C01842559572300B14346 /* Sources */, 339 | 584C01852559572300B14346 /* Frameworks */, 340 | 584C01862559572300B14346 /* Resources */, 341 | ); 342 | buildRules = ( 343 | ); 344 | dependencies = ( 345 | 584C018A2559572300B14346 /* PBXTargetDependency */, 346 | ); 347 | name = AsyncDrawDemoUITests; 348 | productName = AsyncDrawDemoUITests; 349 | productReference = 584C01882559572300B14346 /* AsyncDrawDemoUITests.xctest */; 350 | productType = "com.apple.product-type.bundle.ui-testing"; 351 | }; 352 | /* End PBXNativeTarget section */ 353 | 354 | /* Begin PBXProject section */ 355 | 584C015A2559572000B14346 /* Project object */ = { 356 | isa = PBXProject; 357 | attributes = { 358 | LastUpgradeCheck = 1210; 359 | TargetAttributes = { 360 | 584C01612559572000B14346 = { 361 | CreatedOnToolsVersion = 12.1; 362 | }; 363 | 584C017C2559572300B14346 = { 364 | CreatedOnToolsVersion = 12.1; 365 | TestTargetID = 584C01612559572000B14346; 366 | }; 367 | 584C01872559572300B14346 = { 368 | CreatedOnToolsVersion = 12.1; 369 | TestTargetID = 584C01612559572000B14346; 370 | }; 371 | }; 372 | }; 373 | buildConfigurationList = 584C015D2559572000B14346 /* Build configuration list for PBXProject "AsyncDrawDemo" */; 374 | compatibilityVersion = "Xcode 9.3"; 375 | developmentRegion = en; 376 | hasScannedForEncodings = 0; 377 | knownRegions = ( 378 | en, 379 | Base, 380 | ); 381 | mainGroup = 584C01592559572000B14346; 382 | productRefGroup = 584C01632559572000B14346 /* Products */; 383 | projectDirPath = ""; 384 | projectRoot = ""; 385 | targets = ( 386 | 584C01612559572000B14346 /* AsyncDrawDemo */, 387 | 584C017C2559572300B14346 /* AsyncDrawDemoTests */, 388 | 584C01872559572300B14346 /* AsyncDrawDemoUITests */, 389 | ); 390 | }; 391 | /* End PBXProject section */ 392 | 393 | /* Begin PBXResourcesBuildPhase section */ 394 | 584C01602559572000B14346 /* Resources */ = { 395 | isa = PBXResourcesBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | 584C01752559572300B14346 /* LaunchScreen.storyboard in Resources */, 399 | 584C01722559572300B14346 /* Assets.xcassets in Resources */, 400 | 584C01702559572000B14346 /* Main.storyboard in Resources */, 401 | ); 402 | runOnlyForDeploymentPostprocessing = 0; 403 | }; 404 | 584C017B2559572300B14346 /* Resources */ = { 405 | isa = PBXResourcesBuildPhase; 406 | buildActionMask = 2147483647; 407 | files = ( 408 | ); 409 | runOnlyForDeploymentPostprocessing = 0; 410 | }; 411 | 584C01862559572300B14346 /* Resources */ = { 412 | isa = PBXResourcesBuildPhase; 413 | buildActionMask = 2147483647; 414 | files = ( 415 | ); 416 | runOnlyForDeploymentPostprocessing = 0; 417 | }; 418 | /* End PBXResourcesBuildPhase section */ 419 | 420 | /* Begin PBXShellScriptBuildPhase section */ 421 | 99C288E44E687454D19ED8F7 /* [CP] Check Pods Manifest.lock */ = { 422 | isa = PBXShellScriptBuildPhase; 423 | buildActionMask = 2147483647; 424 | files = ( 425 | ); 426 | inputFileListPaths = ( 427 | ); 428 | inputPaths = ( 429 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 430 | "${PODS_ROOT}/Manifest.lock", 431 | ); 432 | name = "[CP] Check Pods Manifest.lock"; 433 | outputFileListPaths = ( 434 | ); 435 | outputPaths = ( 436 | "$(DERIVED_FILE_DIR)/Pods-AsyncDrawDemoUITests-checkManifestLockResult.txt", 437 | ); 438 | runOnlyForDeploymentPostprocessing = 0; 439 | shellPath = /bin/sh; 440 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 441 | showEnvVarsInLog = 0; 442 | }; 443 | C72E9B0B3F0E82C4789222C5 /* [CP] Check Pods Manifest.lock */ = { 444 | isa = PBXShellScriptBuildPhase; 445 | buildActionMask = 2147483647; 446 | files = ( 447 | ); 448 | inputFileListPaths = ( 449 | ); 450 | inputPaths = ( 451 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 452 | "${PODS_ROOT}/Manifest.lock", 453 | ); 454 | name = "[CP] Check Pods Manifest.lock"; 455 | outputFileListPaths = ( 456 | ); 457 | outputPaths = ( 458 | "$(DERIVED_FILE_DIR)/Pods-AsyncDrawDemoTests-checkManifestLockResult.txt", 459 | ); 460 | runOnlyForDeploymentPostprocessing = 0; 461 | shellPath = /bin/sh; 462 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 463 | showEnvVarsInLog = 0; 464 | }; 465 | /* End PBXShellScriptBuildPhase section */ 466 | 467 | /* Begin PBXSourcesBuildPhase section */ 468 | 584C015E2559572000B14346 /* Sources */ = { 469 | isa = PBXSourcesBuildPhase; 470 | buildActionMask = 2147483647; 471 | files = ( 472 | 58C9ADDF25697E810072D564 /* ADTableView.m in Sources */, 473 | 58113E5B25612159002CF6BB /* ADManager.m in Sources */, 474 | 58113D8B255E68A3002CF6BB /* TestTableViewCell.m in Sources */, 475 | 58113D91255E68B8002CF6BB /* ADLabel.m in Sources */, 476 | 58113E1E255FCF33002CF6BB /* ADLayer.m in Sources */, 477 | 584C016D2559572000B14346 /* ViewController.m in Sources */, 478 | 58113D5B255E54CE002CF6BB /* ADImageView.m in Sources */, 479 | 584C01672559572000B14346 /* AppDelegate.m in Sources */, 480 | 58C9ADD92569689C0072D564 /* ADOperation.m in Sources */, 481 | 58113E6825621FF9002CF6BB /* TestModel.m in Sources */, 482 | 58113DB6255E6F3F002CF6BB /* YYWeakProxy.m in Sources */, 483 | 584C01782559572300B14346 /* main.m in Sources */, 484 | 58113DB0255E6F20002CF6BB /* YYFPSLabel.m in Sources */, 485 | 584C016A2559572000B14346 /* SceneDelegate.m in Sources */, 486 | 58113E522560F3D8002CF6BB /* ADQueue.m in Sources */, 487 | 58113E24255FCF5F002CF6BB /* ADTask.m in Sources */, 488 | ); 489 | runOnlyForDeploymentPostprocessing = 0; 490 | }; 491 | 584C01792559572300B14346 /* Sources */ = { 492 | isa = PBXSourcesBuildPhase; 493 | buildActionMask = 2147483647; 494 | files = ( 495 | 584C01822559572300B14346 /* AsyncDrawDemoTests.m in Sources */, 496 | ); 497 | runOnlyForDeploymentPostprocessing = 0; 498 | }; 499 | 584C01842559572300B14346 /* Sources */ = { 500 | isa = PBXSourcesBuildPhase; 501 | buildActionMask = 2147483647; 502 | files = ( 503 | 584C018D2559572300B14346 /* AsyncDrawDemoUITests.m in Sources */, 504 | ); 505 | runOnlyForDeploymentPostprocessing = 0; 506 | }; 507 | /* End PBXSourcesBuildPhase section */ 508 | 509 | /* Begin PBXTargetDependency section */ 510 | 584C017F2559572300B14346 /* PBXTargetDependency */ = { 511 | isa = PBXTargetDependency; 512 | target = 584C01612559572000B14346 /* AsyncDrawDemo */; 513 | targetProxy = 584C017E2559572300B14346 /* PBXContainerItemProxy */; 514 | }; 515 | 584C018A2559572300B14346 /* PBXTargetDependency */ = { 516 | isa = PBXTargetDependency; 517 | target = 584C01612559572000B14346 /* AsyncDrawDemo */; 518 | targetProxy = 584C01892559572300B14346 /* PBXContainerItemProxy */; 519 | }; 520 | /* End PBXTargetDependency section */ 521 | 522 | /* Begin PBXVariantGroup section */ 523 | 584C016E2559572000B14346 /* Main.storyboard */ = { 524 | isa = PBXVariantGroup; 525 | children = ( 526 | 584C016F2559572000B14346 /* Base */, 527 | ); 528 | name = Main.storyboard; 529 | sourceTree = ""; 530 | }; 531 | 584C01732559572300B14346 /* LaunchScreen.storyboard */ = { 532 | isa = PBXVariantGroup; 533 | children = ( 534 | 584C01742559572300B14346 /* Base */, 535 | ); 536 | name = LaunchScreen.storyboard; 537 | sourceTree = ""; 538 | }; 539 | /* End PBXVariantGroup section */ 540 | 541 | /* Begin XCBuildConfiguration section */ 542 | 584C018F2559572300B14346 /* Debug */ = { 543 | isa = XCBuildConfiguration; 544 | buildSettings = { 545 | ALWAYS_SEARCH_USER_PATHS = NO; 546 | CLANG_ANALYZER_NONNULL = YES; 547 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 548 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 549 | CLANG_CXX_LIBRARY = "libc++"; 550 | CLANG_ENABLE_MODULES = YES; 551 | CLANG_ENABLE_OBJC_ARC = YES; 552 | CLANG_ENABLE_OBJC_WEAK = YES; 553 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 554 | CLANG_WARN_BOOL_CONVERSION = YES; 555 | CLANG_WARN_COMMA = YES; 556 | CLANG_WARN_CONSTANT_CONVERSION = YES; 557 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 558 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 559 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 560 | CLANG_WARN_EMPTY_BODY = YES; 561 | CLANG_WARN_ENUM_CONVERSION = YES; 562 | CLANG_WARN_INFINITE_RECURSION = YES; 563 | CLANG_WARN_INT_CONVERSION = YES; 564 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 565 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 566 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 567 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 568 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 569 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 570 | CLANG_WARN_STRICT_PROTOTYPES = YES; 571 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 572 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 573 | CLANG_WARN_UNREACHABLE_CODE = YES; 574 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 575 | COPY_PHASE_STRIP = NO; 576 | DEBUG_INFORMATION_FORMAT = dwarf; 577 | ENABLE_STRICT_OBJC_MSGSEND = YES; 578 | ENABLE_TESTABILITY = YES; 579 | GCC_C_LANGUAGE_STANDARD = gnu11; 580 | GCC_DYNAMIC_NO_PIC = NO; 581 | GCC_NO_COMMON_BLOCKS = YES; 582 | GCC_OPTIMIZATION_LEVEL = 0; 583 | GCC_PREPROCESSOR_DEFINITIONS = ( 584 | "DEBUG=1", 585 | "$(inherited)", 586 | ); 587 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 588 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 589 | GCC_WARN_UNDECLARED_SELECTOR = YES; 590 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 591 | GCC_WARN_UNUSED_FUNCTION = YES; 592 | GCC_WARN_UNUSED_VARIABLE = YES; 593 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 594 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 595 | MTL_FAST_MATH = YES; 596 | ONLY_ACTIVE_ARCH = YES; 597 | OTHER_LDFLAGS = ""; 598 | SDKROOT = iphoneos; 599 | }; 600 | name = Debug; 601 | }; 602 | 584C01902559572300B14346 /* Release */ = { 603 | isa = XCBuildConfiguration; 604 | buildSettings = { 605 | ALWAYS_SEARCH_USER_PATHS = NO; 606 | CLANG_ANALYZER_NONNULL = YES; 607 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 608 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 609 | CLANG_CXX_LIBRARY = "libc++"; 610 | CLANG_ENABLE_MODULES = YES; 611 | CLANG_ENABLE_OBJC_ARC = YES; 612 | CLANG_ENABLE_OBJC_WEAK = YES; 613 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 614 | CLANG_WARN_BOOL_CONVERSION = YES; 615 | CLANG_WARN_COMMA = YES; 616 | CLANG_WARN_CONSTANT_CONVERSION = YES; 617 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 618 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 619 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 620 | CLANG_WARN_EMPTY_BODY = YES; 621 | CLANG_WARN_ENUM_CONVERSION = YES; 622 | CLANG_WARN_INFINITE_RECURSION = YES; 623 | CLANG_WARN_INT_CONVERSION = YES; 624 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 625 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 626 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 627 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 628 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 629 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 630 | CLANG_WARN_STRICT_PROTOTYPES = YES; 631 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 632 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 633 | CLANG_WARN_UNREACHABLE_CODE = YES; 634 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 635 | COPY_PHASE_STRIP = NO; 636 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 637 | ENABLE_NS_ASSERTIONS = NO; 638 | ENABLE_STRICT_OBJC_MSGSEND = YES; 639 | GCC_C_LANGUAGE_STANDARD = gnu11; 640 | GCC_NO_COMMON_BLOCKS = YES; 641 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 642 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 643 | GCC_WARN_UNDECLARED_SELECTOR = YES; 644 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 645 | GCC_WARN_UNUSED_FUNCTION = YES; 646 | GCC_WARN_UNUSED_VARIABLE = YES; 647 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 648 | MTL_ENABLE_DEBUG_INFO = NO; 649 | MTL_FAST_MATH = YES; 650 | OTHER_LDFLAGS = ""; 651 | SDKROOT = iphoneos; 652 | VALIDATE_PRODUCT = YES; 653 | }; 654 | name = Release; 655 | }; 656 | 584C01922559572300B14346 /* Debug */ = { 657 | isa = XCBuildConfiguration; 658 | buildSettings = { 659 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 660 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 661 | CODE_SIGN_STYLE = Automatic; 662 | DEVELOPMENT_TEAM = 359W3Y62HK; 663 | INFOPLIST_FILE = AsyncDrawDemo/Info.plist; 664 | LD_RUNPATH_SEARCH_PATHS = ( 665 | "$(inherited)", 666 | "@executable_path/Frameworks", 667 | ); 668 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemo; 669 | PRODUCT_NAME = "$(TARGET_NAME)"; 670 | TARGETED_DEVICE_FAMILY = "1,2"; 671 | }; 672 | name = Debug; 673 | }; 674 | 584C01932559572300B14346 /* Release */ = { 675 | isa = XCBuildConfiguration; 676 | buildSettings = { 677 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 678 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 679 | CODE_SIGN_STYLE = Automatic; 680 | DEVELOPMENT_TEAM = 359W3Y62HK; 681 | INFOPLIST_FILE = AsyncDrawDemo/Info.plist; 682 | LD_RUNPATH_SEARCH_PATHS = ( 683 | "$(inherited)", 684 | "@executable_path/Frameworks", 685 | ); 686 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemo; 687 | PRODUCT_NAME = "$(TARGET_NAME)"; 688 | TARGETED_DEVICE_FAMILY = "1,2"; 689 | }; 690 | name = Release; 691 | }; 692 | 584C01952559572300B14346 /* Debug */ = { 693 | isa = XCBuildConfiguration; 694 | buildSettings = { 695 | BUNDLE_LOADER = "$(TEST_HOST)"; 696 | CODE_SIGN_STYLE = Automatic; 697 | INFOPLIST_FILE = AsyncDrawDemoTests/Info.plist; 698 | IPHONEOS_DEPLOYMENT_TARGET = 14.1; 699 | LD_RUNPATH_SEARCH_PATHS = ( 700 | "$(inherited)", 701 | "@executable_path/Frameworks", 702 | "@loader_path/Frameworks", 703 | ); 704 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemoTests; 705 | PRODUCT_NAME = "$(TARGET_NAME)"; 706 | TARGETED_DEVICE_FAMILY = "1,2"; 707 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDrawDemo.app/AsyncDrawDemo"; 708 | }; 709 | name = Debug; 710 | }; 711 | 584C01962559572300B14346 /* Release */ = { 712 | isa = XCBuildConfiguration; 713 | buildSettings = { 714 | BUNDLE_LOADER = "$(TEST_HOST)"; 715 | CODE_SIGN_STYLE = Automatic; 716 | INFOPLIST_FILE = AsyncDrawDemoTests/Info.plist; 717 | IPHONEOS_DEPLOYMENT_TARGET = 14.1; 718 | LD_RUNPATH_SEARCH_PATHS = ( 719 | "$(inherited)", 720 | "@executable_path/Frameworks", 721 | "@loader_path/Frameworks", 722 | ); 723 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemoTests; 724 | PRODUCT_NAME = "$(TARGET_NAME)"; 725 | TARGETED_DEVICE_FAMILY = "1,2"; 726 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDrawDemo.app/AsyncDrawDemo"; 727 | }; 728 | name = Release; 729 | }; 730 | 584C01982559572300B14346 /* Debug */ = { 731 | isa = XCBuildConfiguration; 732 | buildSettings = { 733 | CODE_SIGN_STYLE = Automatic; 734 | INFOPLIST_FILE = AsyncDrawDemoUITests/Info.plist; 735 | LD_RUNPATH_SEARCH_PATHS = ( 736 | "$(inherited)", 737 | "@executable_path/Frameworks", 738 | "@loader_path/Frameworks", 739 | ); 740 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemoUITests; 741 | PRODUCT_NAME = "$(TARGET_NAME)"; 742 | TARGETED_DEVICE_FAMILY = "1,2"; 743 | TEST_TARGET_NAME = AsyncDrawDemo; 744 | }; 745 | name = Debug; 746 | }; 747 | 584C01992559572300B14346 /* Release */ = { 748 | isa = XCBuildConfiguration; 749 | buildSettings = { 750 | CODE_SIGN_STYLE = Automatic; 751 | INFOPLIST_FILE = AsyncDrawDemoUITests/Info.plist; 752 | LD_RUNPATH_SEARCH_PATHS = ( 753 | "$(inherited)", 754 | "@executable_path/Frameworks", 755 | "@loader_path/Frameworks", 756 | ); 757 | PRODUCT_BUNDLE_IDENTIFIER = com.STK.AsyncDrawDemoUITests; 758 | PRODUCT_NAME = "$(TARGET_NAME)"; 759 | TARGETED_DEVICE_FAMILY = "1,2"; 760 | TEST_TARGET_NAME = AsyncDrawDemo; 761 | }; 762 | name = Release; 763 | }; 764 | /* End XCBuildConfiguration section */ 765 | 766 | /* Begin XCConfigurationList section */ 767 | 584C015D2559572000B14346 /* Build configuration list for PBXProject "AsyncDrawDemo" */ = { 768 | isa = XCConfigurationList; 769 | buildConfigurations = ( 770 | 584C018F2559572300B14346 /* Debug */, 771 | 584C01902559572300B14346 /* Release */, 772 | ); 773 | defaultConfigurationIsVisible = 0; 774 | defaultConfigurationName = Release; 775 | }; 776 | 584C01912559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemo" */ = { 777 | isa = XCConfigurationList; 778 | buildConfigurations = ( 779 | 584C01922559572300B14346 /* Debug */, 780 | 584C01932559572300B14346 /* Release */, 781 | ); 782 | defaultConfigurationIsVisible = 0; 783 | defaultConfigurationName = Release; 784 | }; 785 | 584C01942559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemoTests" */ = { 786 | isa = XCConfigurationList; 787 | buildConfigurations = ( 788 | 584C01952559572300B14346 /* Debug */, 789 | 584C01962559572300B14346 /* Release */, 790 | ); 791 | defaultConfigurationIsVisible = 0; 792 | defaultConfigurationName = Release; 793 | }; 794 | 584C01972559572300B14346 /* Build configuration list for PBXNativeTarget "AsyncDrawDemoUITests" */ = { 795 | isa = XCConfigurationList; 796 | buildConfigurations = ( 797 | 584C01982559572300B14346 /* Debug */, 798 | 584C01992559572300B14346 /* Release */, 799 | ); 800 | defaultConfigurationIsVisible = 0; 801 | defaultConfigurationName = Release; 802 | }; 803 | /* End XCConfigurationList section */ 804 | }; 805 | rootObject = 584C015A2559572000B14346 /* Project object */; 806 | } 807 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/project.xcworkspace/xcuserdata/Stk.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stkusegithub/AsyncDraw/c5771453a0fab90a9661ad99b888f81674ef3324/AsyncDrawDemo/AsyncDrawDemo.xcodeproj/project.xcworkspace/xcuserdata/Stk.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/xcuserdata/Stk.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo.xcodeproj/xcuserdata/Stk.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AsyncDrawDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | #pragma mark - UISceneSession lifecycle 24 | 25 | 26 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 30 | } 31 | 32 | 33 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/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 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/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 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADLayer.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import 9 | #import 10 | 11 | @class ADLayer; 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @protocol ADLayerDelegate 16 | /** 17 | 外部ui遵循此协议进行内容绘制 18 | 19 | @param layer 图层 20 | @param ctx 图形上下文 21 | @param canceled 是否已取消 22 | */ 23 | - (void)asyncDrawLayer:(ADLayer *)layer inContext:(CGContextRef __nullable)ctx canceled:(BOOL)canceled; 24 | @end 25 | 26 | @interface ADLayer : CALayer 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADLayer.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import "ADLayer.h" 9 | #import "ADManager.h" 10 | 11 | typedef enum { 12 | ADLayerStatusCancel = 0,// 绘制取消 13 | ADLayerStatusDrawing,// 正在绘制 14 | }ADLayerStatus; 15 | 16 | @interface ADLayer () 17 | 18 | /** 19 | 维护线程安全的绘制状态 20 | */ 21 | @property (atomic, assign) ADLayerStatus status; 22 | @end 23 | 24 | @implementation ADLayer 25 | 26 | #pragma mark - Override 27 | 28 | - (void)setNeedsDisplay { 29 | // 收到新的绘制请求时,同步正在绘制的线程本次取消 30 | self.status = ADLayerStatusCancel; 31 | 32 | [super setNeedsDisplay]; 33 | } 34 | 35 | - (void)display { 36 | // 标记正在绘制 37 | self.status = ADLayerStatusDrawing; 38 | 39 | if ([self.delegate respondsToSelector:@selector(asyncDrawLayer:inContext:canceled:)]) { 40 | [self asyncDraw]; 41 | } else { 42 | [super display]; 43 | } 44 | } 45 | 46 | #pragma mark - Pri MD 47 | 48 | - (BOOL)canceled { 49 | return self.status == ADLayerStatusCancel; 50 | } 51 | 52 | - (void)asyncDraw { 53 | 54 | __block ADQueue *q = [[ADManager shareInstance] ad_getExecuteTaskQueue]; 55 | __block id delegate = (id)self.delegate; 56 | 57 | dispatch_async(q.queue, ^{ 58 | // 重绘取消 59 | if ([self canceled]) { 60 | [[ADManager shareInstance] ad_finishTask:q]; 61 | return; 62 | } 63 | // 生成上下文context 64 | CGSize size = self.bounds.size; 65 | BOOL opaque = self.opaque; 66 | CGFloat scale = [UIScreen mainScreen].scale; 67 | CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL; 68 | UIGraphicsBeginImageContextWithOptions(size, opaque, scale); 69 | CGContextRef context = UIGraphicsGetCurrentContext(); 70 | if (opaque && context) { 71 | CGContextSaveGState(context); { 72 | if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) { 73 | CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 74 | CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale)); 75 | CGContextFillPath(context); 76 | } 77 | if (backgroundColor) { 78 | CGContextSetFillColorWithColor(context, backgroundColor); 79 | CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale)); 80 | CGContextFillPath(context); 81 | } 82 | } CGContextRestoreGState(context); 83 | CGColorRelease(backgroundColor); 84 | } else { 85 | CGColorRelease(backgroundColor); 86 | } 87 | // 使用context绘制 88 | [delegate asyncDrawLayer:self inContext:context canceled:[self canceled]]; 89 | // 重绘取消 90 | if ([self canceled]) { 91 | [[ADManager shareInstance] ad_finishTask:q]; 92 | UIGraphicsEndImageContext(); 93 | return; 94 | } 95 | // 获取image 96 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 97 | UIGraphicsEndImageContext(); 98 | // 结束任务 99 | [[ADManager shareInstance] ad_finishTask:q]; 100 | // 重绘取消 101 | if ([self canceled]) { 102 | return; 103 | } 104 | // 主线程刷新 105 | dispatch_async(dispatch_get_main_queue(), ^{ 106 | self.contents = (__bridge id)(image.CGImage); 107 | }); 108 | 109 | }); 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADManager.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/15. 6 | // 7 | 8 | #import 9 | 10 | #import "ADQueue.h" 11 | #import "ADTask.h" 12 | #import "ADOperation.h" 13 | 14 | typedef enum { 15 | ADManagerAsyncDrawTypeText = 1,// 文本 16 | ADManagerAsyncDrawTypeImage,// 图片 17 | }ADManagerAsyncDrawType; 18 | 19 | NS_ASSUME_NONNULL_BEGIN 20 | 21 | @interface ADManager : NSObject 22 | 23 | + (instancetype)shareInstance; 24 | /** 25 | 添加任务 26 | @param target layer对象 27 | @param selector 执行方法 28 | */ 29 | - (void)ad_addTaskWith:(id)target selector:(SEL)selector; 30 | /** 31 | 获取最优队列执行异步操作 32 | */ 33 | - (ADQueue *)ad_getExecuteTaskQueue; 34 | /** 35 | 取消或完成任务 36 | @param q 队列存储对象 37 | */ 38 | - (void)ad_finishTask:(ADQueue *)q; 39 | /** 40 | 异步下载图片 41 | @param url 图片地址 42 | @param completedBlock 完成回调 43 | */ 44 | - (void)ad_setImageWithURL:(NSURL *)url target:(id)target completed:(void (^)(UIImage * _Nullable image, NSError * _Nullable error))completedBlock; 45 | @end 46 | 47 | NS_ASSUME_NONNULL_END 48 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADManager.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/15. 6 | // 7 | 8 | #import 9 | #import "ADManager.h" 10 | 11 | #define kMAX_QUEUE_COUNT 6 12 | #define kMAX_OPERATION_COUNT 6 13 | #define kERROR_DOMAIN @"com.stk.AsyncDraw.imageLoad" 14 | 15 | @interface ADManager () 16 | 17 | /** 18 | 文本绘制的队列数组 19 | */ 20 | @property (nonatomic, strong) NSMutableArray *queueArr; 21 | /** 22 | 限制最大串行队列数 23 | */ 24 | @property (nonatomic, assign) NSUInteger limitQueueCount; 25 | 26 | /** 27 | 图片绘制的队列 28 | */ 29 | @property (nonatomic, strong) NSOperationQueue *operationQueue; 30 | /** 31 | 图片下载任务字典 32 | */ 33 | @property (nonatomic, strong) NSMutableDictionary *operationDict; 34 | /** 35 | 图片缓存字典 36 | */ 37 | @property (nonatomic, strong) NSMutableDictionary *imageDataDict; 38 | 39 | 40 | @end 41 | 42 | static id _instance; 43 | /** 44 | runloop回调,并发执行异步绘制任务 45 | */ 46 | static NSMutableSet *_taskSet = nil; 47 | static void ADRunLoopCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { 48 | if (_taskSet.count == 0) return; 49 | NSSet *currentSet = _taskSet; 50 | _taskSet = [NSMutableSet set]; 51 | [currentSet enumerateObjectsUsingBlock:^(ADTask *task, BOOL *stop) { 52 | [task excute]; 53 | }]; 54 | } 55 | 56 | @implementation ADManager 57 | 58 | #pragma mark - Pub MD 59 | 60 | + (instancetype)shareInstance { 61 | static dispatch_once_t onceToken; 62 | dispatch_once(&onceToken, ^{ 63 | _instance = [[self alloc] init]; 64 | }); 65 | return _instance; 66 | } 67 | 68 | #pragma mark - 处理文本 69 | 70 | - (void)ad_addTaskWith:(id)target selector:(SEL)selector { 71 | if (!target || !selector) return; 72 | ADTask *task = ADTask.init(target, selector); 73 | [_taskSet addObject:task]; 74 | } 75 | 76 | - (ADQueue *)ad_getExecuteTaskQueue { 77 | // 1、创建对应数量串行队列处理并发任务,并行队列线程数无法控制 78 | if (self.queueArr.count < self.limitQueueCount) { 79 | ADQueue *q = [[ADQueue alloc] init]; 80 | q.index = self.queueArr.count; 81 | [self.queueArr addObject:q]; 82 | q.asyncCount += 1; 83 | NSLog(@"queue[%ld]-asyncCount:%ld", (long)q.index, (long)q.asyncCount); 84 | return q; 85 | } 86 | 87 | // 2、当队列数已达上限,择优获取异步任务数最少的队列 88 | NSUInteger minAsync = [[self.queueArr valueForKeyPath:@"@min.asyncCount"] integerValue]; 89 | __block ADQueue *q = nil; 90 | [self.queueArr enumerateObjectsUsingBlock:^(ADQueue * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 91 | if (obj.asyncCount <= minAsync) { 92 | *stop = YES; 93 | q = obj; 94 | } 95 | }]; 96 | q.asyncCount += 1; 97 | NSLog(@"queue[%ld]-excute-count:%ld", (long)q.index, (long)q.asyncCount); 98 | return q; 99 | } 100 | 101 | - (void)ad_finishTask:(ADQueue *)q { 102 | q.asyncCount -= 1; 103 | if (q.asyncCount < 0) { 104 | q.asyncCount = 0; 105 | } 106 | NSLog(@"queue[%ld]-done-count:%ld", (long)q.index, (long)q.asyncCount); 107 | } 108 | 109 | #pragma mark - 处理图片 110 | 111 | - (void)ad_setImageWithURL:(NSURL *)url target:(id)target completed:(void (^)(UIImage * _Nullable image, NSError * _Nullable error))completedBlock { 112 | if (!url) { 113 | if (completedBlock) { 114 | NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Expected URL to be a image URL", @"AsyncDraw", nil)}; 115 | NSError *error = [[NSError alloc] initWithDomain:kERROR_DOMAIN code:NSURLErrorBadURL userInfo:userInfo]; 116 | completedBlock(nil, error); 117 | } 118 | return; 119 | } 120 | 121 | // 1、缓存中读取 122 | NSString *imageKey = url.absoluteString; 123 | NSData *imageData = self.imageDataDict[imageKey]; 124 | if (imageData) { 125 | UIImage *image = [UIImage imageWithData:imageData]; 126 | if (completedBlock) { 127 | completedBlock(image, nil); 128 | } 129 | } else { 130 | 131 | // 2、沙盒中读取 132 | NSString *imagePath = [NSString stringWithFormat:@"%@/Library/Caches/%@", NSHomeDirectory(), url.lastPathComponent]; 133 | imageData = [NSData dataWithContentsOfFile:imagePath]; 134 | if (imageData) { 135 | UIImage *image = [UIImage imageWithData:imageData]; 136 | if (completedBlock) { 137 | completedBlock(image, nil); 138 | } 139 | } else { 140 | 141 | // 3、下载并缓存写入沙盒 142 | ADOperation *operation = [self ad_downloadImageWithURL:url toPath:imagePath completed:completedBlock]; 143 | // 4、添加图片渲染对象 144 | [operation addTarget:target]; 145 | } 146 | } 147 | } 148 | 149 | - (ADOperation *)ad_downloadImageWithURL:(NSURL *)url toPath:(NSString *)imagePath completed:(void (^)(UIImage * _Nullable image, NSError * _Nullable error))completedBlock { 150 | NSString *imageKey = url.absoluteString; 151 | 152 | ADOperation *operation = self.operationDict[imageKey]; 153 | if (!operation) { 154 | operation = [ADOperation blockOperationWithBlock:^{ 155 | NSLog(@"AsyncDraw image loading~"); 156 | NSData *newImageData = [NSData dataWithContentsOfURL:url]; 157 | 158 | // 下载失败处理 159 | if (!newImageData) { 160 | [self.operationDict removeObjectForKey:imageKey]; 161 | 162 | NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: NSLocalizedStringFromTable(@"Failed to load the image", @"AsyncDraw", nil)}; 163 | NSError *error = [[NSError alloc] initWithDomain:kERROR_DOMAIN code:NSURLErrorUnknown userInfo:userInfo]; 164 | if (completedBlock) { 165 | completedBlock(nil, error); 166 | } 167 | return; 168 | } 169 | 170 | // 缓存图片数据 171 | [self.imageDataDict setValue:newImageData forKey:imageKey]; 172 | }]; 173 | 174 | // 设置完成回调 175 | __block ADOperation *blockOperation = operation; 176 | [operation setCompletionBlock:^{ 177 | NSLog(@"AsyncDraw image load completed~"); 178 | // 取缓存 179 | NSData *newImageData = self.imageDataDict[imageKey]; 180 | if (!newImageData) { 181 | return; 182 | } 183 | 184 | // 返回主线程刷新 185 | [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 186 | UIImage *newImage = [UIImage imageWithData:newImageData]; 187 | // 遍历渲染同个图片地址的所有控件 188 | [blockOperation.targetSet enumerateObjectsUsingBlock:^(id _Nonnull obj, BOOL * _Nonnull stop) { 189 | if ([obj isKindOfClass:[UIImageView class]]) { 190 | UIImageView *imageView = (UIImageView *)obj; 191 | // ADImageView内部判断“超出可视范围,放弃渲染~” 192 | imageView.image = newImage; 193 | } 194 | }]; 195 | [blockOperation removeAllTargets]; 196 | }]; 197 | 198 | // 写入沙盒 199 | [newImageData writeToFile:imagePath atomically:YES]; 200 | 201 | // 移除任务 202 | [self.operationDict removeObjectForKey:imageKey]; 203 | }]; 204 | 205 | // 加入队列 206 | [self.operationQueue addOperation:operation]; 207 | 208 | // 添加opertion 209 | [self.operationDict setValue:operation forKey:imageKey]; 210 | } 211 | 212 | return operation; 213 | } 214 | 215 | #pragma mark - Overide 216 | 217 | + (instancetype)allocWithZone:(struct _NSZone *)zone { 218 | static dispatch_once_t onceToken; 219 | dispatch_once(&onceToken, ^{ 220 | _instance = [super allocWithZone:zone]; 221 | }); 222 | return _instance; 223 | } 224 | 225 | - (id)copyWithZone:(NSZone *)zone { 226 | return _instance; 227 | } 228 | 229 | - (instancetype)init { 230 | self = [super init]; 231 | if (self) { 232 | [self setupRunLoopObserver]; 233 | } 234 | return self; 235 | } 236 | 237 | #pragma mark Pri MD 238 | 239 | - (void)setupRunLoopObserver { 240 | // 创建任务集合 241 | _taskSet = [NSMutableSet set]; 242 | // 获取主线程的runloop 243 | CFRunLoopRef runloop = CFRunLoopGetMain(); 244 | // 创建观察者,监听即将休眠和退出 245 | CFRunLoopObserverRef observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 246 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 247 | true, // 重复 248 | 0xFFFFFF, // 设置优先级低于CATransaction(2000000) 249 | ADRunLoopCallBack, NULL); 250 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 251 | CFRelease(observer); 252 | } 253 | 254 | #pragma mark - Lazy Property 255 | 256 | - (NSMutableArray *)queueArr { 257 | if (!_queueArr) { 258 | _queueArr = [[NSMutableArray alloc] initWithCapacity:kMAX_QUEUE_COUNT]; 259 | } 260 | return _queueArr; 261 | } 262 | 263 | - (NSUInteger)limitQueueCount { 264 | if (_limitQueueCount == 0) { 265 | // 获取当前系统处于激活状态的处理器数量 266 | NSUInteger processorCount = [NSProcessInfo processInfo].activeProcessorCount; 267 | // 根据处理器的数量和设置的最大队列数来设定当前队列数组的大小 268 | _limitQueueCount = processorCount > 0 ? (processorCount > kMAX_QUEUE_COUNT ? kMAX_QUEUE_COUNT : processorCount) : 1; 269 | } 270 | 271 | return _limitQueueCount; 272 | } 273 | 274 | - (NSOperationQueue *)operationQueue { 275 | if (!_operationQueue) { 276 | _operationQueue = [[NSOperationQueue alloc] init]; 277 | _operationQueue.maxConcurrentOperationCount = kMAX_OPERATION_COUNT; 278 | } 279 | return _operationQueue; 280 | } 281 | 282 | - (NSMutableDictionary *)operationDict { 283 | if (!_operationDict) { 284 | _operationDict = [NSMutableDictionary dictionary]; 285 | } 286 | return _operationDict; 287 | } 288 | 289 | - (NSMutableDictionary *)imageDataDict { 290 | if (!_imageDataDict) { 291 | _imageDataDict = [NSMutableDictionary dictionary]; 292 | } 293 | return _imageDataDict; 294 | } 295 | 296 | @end 297 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADOperation.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/21. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ADOperation : NSBlockOperation 13 | 14 | @property (nonatomic, strong) NSSet *targetSet; 15 | 16 | - (void)addTarget:(id)target; 17 | - (void)removeAllTargets; 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADOperation.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/21. 6 | // 7 | 8 | #import "ADOperation.h" 9 | 10 | @interface ADOperation () 11 | 12 | @property (nonatomic, strong) NSMutableSet *targetInnerSet; 13 | @end 14 | 15 | @implementation ADOperation 16 | 17 | - (void)addTarget:(id)target { 18 | [self.targetInnerSet addObject:target]; 19 | } 20 | 21 | - (void)removeAllTargets { 22 | [self.targetInnerSet removeAllObjects]; 23 | } 24 | 25 | - (NSSet *)targetSet { 26 | return self.targetInnerSet; 27 | } 28 | 29 | - (NSMutableSet *)targetInnerSet { 30 | if (!_targetInnerSet) { 31 | _targetInnerSet = [NSMutableSet set]; 32 | } 33 | return _targetInnerSet; 34 | } 35 | @end 36 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADQueue.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADQueue.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/15. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ADQueue : NSObject 13 | 14 | /** 15 | 对应索引 16 | */ 17 | @property (nonatomic, assign) NSUInteger index; 18 | /** 19 | 队列 20 | */ 21 | @property (nonatomic, strong, readonly) dispatch_queue_t queue; 22 | /** 23 | 异步执行次数 24 | */ 25 | @property (nonatomic, assign) NSUInteger asyncCount; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADQueue.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADQueue.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/15. 6 | // 7 | 8 | #import "ADQueue.h" 9 | 10 | #define kADQueueSerial "kADQueueSerial" 11 | 12 | @implementation ADQueue { 13 | dispatch_queue_t _queue; 14 | } 15 | 16 | #pragma mark - Pub MD 17 | 18 | - (dispatch_queue_t)queue { 19 | return _queue; 20 | } 21 | 22 | #pragma mark - Overide 23 | 24 | - (instancetype)init { 25 | self = [super init]; 26 | if (self) { 27 | dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0); 28 | _queue = dispatch_queue_create(kADQueueSerial, attr); 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADTask.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADTask.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class ADTask; 13 | typedef ADTask *_Nonnull(^ADTaskBlock)(id target, SEL selector); 14 | 15 | @interface ADTask : NSObject 16 | 17 | /** 18 | 初始化 19 | 20 | ADTaskBlock:target-ui对象,selector-调用方法 21 | */ 22 | + (ADTaskBlock)init; 23 | /** 24 | 执行任务 25 | */ 26 | - (void)excute; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncDraw/ADTask.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADTask.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import "ADTask.h" 9 | 10 | @interface ADTask () 11 | 12 | @property (nonatomic, strong) id target; 13 | @property (nonatomic, assign) SEL selector; 14 | @end 15 | 16 | @implementation ADTask 17 | 18 | #pragma mark - Pub MD 19 | 20 | //1. 创建一个ADTask的对象 21 | + (ADTaskBlock)init { 22 | return ^ADTask *(id target, SEL selector) { 23 | if (!target || !selector) return nil; 24 | ADTask *t = [ADTask new]; 25 | t.target = target; 26 | t.selector = selector; 27 | return t; 28 | }; 29 | } 30 | 31 | - (void)excute { 32 | ((void (*)(id, SEL))[self.target methodForSelector:self.selector])(self.target, self.selector); 33 | } 34 | 35 | #pragma mark - Overide 36 | 37 | // 由于使用NSMutableSet存储自定义对象,而NSMutableSet使用HashMap实现存储结构的。hashmap中需要先寻找hash索引,以便快速的找到对应存储的对象。 38 | - (NSUInteger)hash { 39 | long v1 = (long)((void *)_selector); 40 | long v2 = (long)_target; 41 | // 位异或处理,即相同_selector、_target则hash值相同,在set中只存储一份事务 42 | return v1 ^ v2; 43 | } 44 | // 在hashmap中寻找对象的时候,当找到hash索引后,如果对应位置没有值,则直接存储,否则需要判断对象是否相等,如果相等则放弃存储,否则指定位置使用链表或者再次寻址。 45 | - (BOOL)isEqual:(id)object { 46 | if (self == object) return YES; 47 | if (![object isMemberOfClass:self.class]) return NO; 48 | ADTask *other = object; 49 | return other.selector == _selector && other.target == _target; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADImageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADImageView.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/13. 6 | // 7 | 8 | #import 9 | 10 | @class ADImageView; 11 | 12 | @protocol ADImageViewDelegate 13 | 14 | - (void)ADImageViewClicked:(ADImageView *)imageView; 15 | 16 | @end 17 | 18 | @interface ADImageView : UIImageView 19 | 20 | @property (nonatomic, weak) id delegate; 21 | 22 | @property (nonatomic, copy) void(^imageViewClickBlock)(ADImageView *image); 23 | 24 | // 普通图片(渐变,未加载图片没背景色) 25 | @property (nonatomic, strong) NSString *url; 26 | // 正在展示(在可显cell上) 27 | @property (nonatomic, assign) BOOL isDisplay; 28 | 29 | // 外部传入占位图(有渐变,加载默认图) 30 | - (void)setUrl:(NSString *)url placeholderName:(NSString *)placeholderName; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADImageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADImageView.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/13. 6 | // 7 | 8 | #import "ADImageView.h" 9 | #import "ADManager.h" 10 | 11 | @implementation ADImageView 12 | 13 | #pragma mark - Public Methods 14 | 15 | - (void)setImage:(UIImage *)image { 16 | if (self.isDisplay) { 17 | [super setImage:image]; 18 | } else { 19 | NSLog(@"超出可视范围,放弃渲染~"); 20 | } 21 | } 22 | 23 | - (void)setUrl:(NSString *)url { 24 | _url = url; 25 | 26 | [[ADManager shareInstance] ad_setImageWithURL:[NSURL URLWithString:self.url] target:self completed:^(UIImage * _Nullable image, NSError * _Nullable error) { 27 | if (image) { 28 | self.image = image; 29 | } 30 | }]; 31 | } 32 | 33 | - (void)setUrl:(NSString *)url placeholderName:(NSString *)placeholderName { 34 | _url = url; 35 | 36 | // UIImage *placeholderImage = [UIImage imageNamed:placeholderName]; 37 | // 待完善 38 | } 39 | 40 | #pragma mark - Override 41 | 42 | - (instancetype)initWithFrame:(CGRect)frame { 43 | self = [super initWithFrame:frame]; 44 | if (self) { 45 | self.backgroundColor = [UIColor lightGrayColor]; 46 | self.contentMode = UIViewContentModeScaleAspectFill; 47 | 48 | self.layer.masksToBounds = YES; 49 | self.userInteractionEnabled = YES; 50 | 51 | UITapGestureRecognizer *tapGr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGrAction:)]; 52 | [self addGestureRecognizer:tapGr]; 53 | } 54 | return self; 55 | } 56 | 57 | #pragma mark - Pri Property 58 | 59 | - (void)tapGrAction:(UITapGestureRecognizer *)gr { 60 | 61 | if (self.imageViewClickBlock) { 62 | self.imageViewClickBlock(self); 63 | } 64 | 65 | if ([self.delegate respondsToSelector:@selector(ADImageViewClicked:)]) { 66 | [self.delegate ADImageViewClicked:self]; 67 | } 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADLabel.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ADLabel : UILabel 13 | 14 | @property (nonatomic, strong) NSParagraphStyle *paragraphStyle; 15 | //@property (nonatomic, assign) CGFloat cornerRadius; 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADLabel.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import "ADLabel.h" 9 | #import "ADLayer.h" 10 | #import "ADManager.h" 11 | 12 | #import 13 | 14 | @interface ADLabel () 15 | { 16 | UIColor *_backgroundColor; 17 | NSString *_text; 18 | UIFont *_font; 19 | UIColor *_textColor; 20 | } 21 | @end 22 | 23 | @implementation ADLabel 24 | 25 | #pragma mark - Pub MD 26 | 27 | - (void)setBackgroundColor:(UIColor *)backgroundColor { 28 | _backgroundColor = backgroundColor; 29 | 30 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 31 | } 32 | 33 | - (void)setText:(NSString *)text { 34 | _text = text; 35 | 36 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 37 | } 38 | 39 | - (void)setFont:(UIFont *)font { 40 | _font = font; 41 | 42 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 43 | } 44 | 45 | - (void)setTextColor:(UIColor *)textColor { 46 | _textColor = textColor; 47 | 48 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 49 | } 50 | 51 | - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle { 52 | _paragraphStyle = paragraphStyle; 53 | 54 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 55 | } 56 | 57 | //- (void)setCornerRadius:(CGFloat)cornerRadius { 58 | // _cornerRadius = cornerRadius; 59 | // 60 | // [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(setNeedsDisplay)]; 61 | //} 62 | 63 | - (void)layoutSubviews { 64 | [super layoutSubviews]; 65 | 66 | [[ADManager shareInstance] ad_addTaskWith:self selector:@selector(asyncDraw)]; 67 | } 68 | // 绑定异步绘制layer 69 | + (Class)layerClass { 70 | return ADLayer.class; 71 | } 72 | 73 | #pragma mark - Pri MD 74 | 75 | - (void)asyncDraw { 76 | [self.layer setNeedsDisplay]; 77 | } 78 | 79 | #pragma mark - ADLayerDelegate 80 | 81 | - (void)layerWillDraw:(CALayer *)layer { 82 | } 83 | 84 | - (void)asyncDrawLayer:(ADLayer *)layer inContext:(CGContextRef __nullable)ctx canceled:(BOOL)canceled { 85 | 86 | if (canceled) { 87 | NSLog(@"异步绘制取消~"); 88 | return; 89 | } 90 | 91 | UIColor *backgroundColor = _backgroundColor; 92 | NSString *text = _text; 93 | UIFont *font = _font; 94 | UIColor *textColor = _textColor; 95 | CGSize size = layer.bounds.size; 96 | 97 | CGContextSetTextMatrix(ctx, CGAffineTransformIdentity); 98 | CGContextTranslateCTM(ctx, 0, size.height); 99 | CGContextScaleCTM(ctx, 1, -1); 100 | 101 | // 绘制区域 102 | CGMutablePathRef path = CGPathCreateMutable(); 103 | CGPathAddRect(path, NULL, CGRectMake(0, 0, size.width, size.height)); 104 | 105 | // 绘制的内容属性字符串 106 | NSDictionary *attributes = @{NSFontAttributeName : font, 107 | NSForegroundColorAttributeName: textColor, 108 | NSBackgroundColorAttributeName : backgroundColor, 109 | NSParagraphStyleAttributeName : self.paragraphStyle ?:[NSParagraphStyle new] 110 | }; 111 | NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:text attributes:attributes]; 112 | 113 | // 使用NSMutableAttributedString创建CTFrame 114 | CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrStr); 115 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrStr.length), path, NULL); 116 | CFRelease(framesetter); 117 | CGPathRelease(path); 118 | 119 | // 使用CTFrame在CGContextRef上下文上绘制 120 | CTFrameDraw(frame, ctx); 121 | CFRelease(frame); 122 | 123 | // 如果存在圆角则进行绘制>>暂不支持 124 | // if (self.cornerRadius > 0) { 125 | // CGRect bounds = CGRectMake(0, 0, size.width, size.height); 126 | // 127 | // UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounds]; 128 | // UIBezierPath *cornerPath = [[UIBezierPath bezierPathWithRoundedRect:bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(self.cornerRadius, self.cornerRadius)] bezierPathByReversingPath]; 129 | // [path appendPath:cornerPath]; 130 | // //裁剪出圆角路径 131 | // CGContextAddPath(context, path.CGPath); 132 | // //用背景色填充路径 133 | // [backgroundColor set]; 134 | // CGContextFillPath(context); 135 | // } 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ADTableView.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/22. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ADTableView : UITableView 13 | 14 | @property (nonatomic, strong) NSMutableArray *dataArr; 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/AsyncUI/ADTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ADTableView.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/22. 6 | // 7 | 8 | #import "ADTableView.h" 9 | 10 | @implementation ADTableView 11 | 12 | - (NSMutableArray *)dataArr { 13 | if (!_dataArr) { 14 | _dataArr = [NSMutableArray array]; 15 | } 16 | return _dataArr; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/Helper/iColor.h: -------------------------------------------------------------------------------- 1 | // 2 | // iColor.h 3 | // iOS_BananaCP 4 | // 5 | // Created by Stk on 2019/6/27. 6 | // Copyright © 2019 STK. All rights reserved. 7 | // 8 | 9 | #ifndef iColor_h 10 | #define iColor_h 11 | 12 | #define UIColorSetAlpha(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]// alpha 十进制色值 13 | #define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]// RGB 十六进制色值 14 | #define UIColorFromRGBA(rgbValue, a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:(a)] 15 | 16 | #endif /* iColor_h */ 17 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/Helper/iConstant.h: -------------------------------------------------------------------------------- 1 | // 2 | // iConstant.h 3 | // iOS_BananaCP 4 | // 5 | // Created by Stk on 2019/6/27. 6 | // Copyright © 2019 STK. All rights reserved. 7 | // 8 | 9 | #ifndef iConstant_h 10 | #define iConstant_h 11 | 12 | #define kCommonEdge 15.f 13 | #define ScreenWidth [[UIScreen mainScreen] bounds].size.width// 屏幕宽度 14 | #define ScreenHeight [[UIScreen mainScreen] bounds].size.height// 屏幕高度 15 | #define IsIPhoneX ((ScreenWidth == 375 && ScreenHeight == 812) || (ScreenWidth == 414 && ScreenHeight == 896)) 16 | #define kStatusBarHeight (IsIPhoneX? 44.f:20.f)// 状态栏 17 | #define kNaviBarHeight (IsIPhoneX? 88.f:64.f)// 导航栏 18 | #define kTabBarHeight (IsIPhoneX? 83.f:49.f)// tabbar高度 19 | #define kBottomAppendHeight (IsIPhoneX ? 34.f : 0.f) 20 | #define kSpaceToScreenTop(v) (IsIPhoneX? (24.f+v):v) 21 | /**宽度等比适配*/ 22 | #define kScaleWidth(__VA_ARGS__) ([UIScreen mainScreen].bounds.size.width/375)*(__VA_ARGS__) 23 | /**高度等比适配*/ 24 | #define kScaleHeight(__VA_ARGS__) ([UIScreen mainScreen].bounds.size.height/667)*(__VA_ARGS__) 25 | 26 | #define kCommonPageSize 10// 分页数量 27 | #define kPictureTest @"https://5b0988e595225.cdn.sohucs.com/images/20190316/a2b4fe8ce83a453a91354d6e88751f2a.jpeg" 28 | 29 | #endif /* iConstant_h */ 30 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Core/Helper/iMethods.h: -------------------------------------------------------------------------------- 1 | // 2 | // iMethods.h 3 | // LanSongEditor_DEMO 4 | // 5 | // Created by Stk on 2019/6/27. 6 | // Copyright © 2019 STK. All rights reserved. 7 | // 8 | 9 | #ifndef iMethods_h 10 | #define iMethods_h 11 | 12 | #ifndef weakify 13 | #if DEBUG 14 | #if __has_feature(objc_arc) 15 | #define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object; 16 | #else 17 | #define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object; 18 | #endif 19 | #else 20 | #if __has_feature(objc_arc) 21 | #define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object; 22 | #else 23 | #define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object; 24 | #endif 25 | #endif 26 | #endif 27 | 28 | 29 | #ifndef strongify 30 | #if DEBUG 31 | #if __has_feature(objc_arc) 32 | #define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object; 33 | #else 34 | #define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object; 35 | #endif 36 | #else 37 | #if __has_feature(objc_arc) 38 | #define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object; 39 | #else 40 | #define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object; 41 | #endif 42 | #endif 43 | #endif 44 | 45 | #endif /* iMethods_h */ 46 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UIApplicationSupportsIndirectInputEvents 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIMainStoryboardFile 47 | Main 48 | UIRequiredDeviceCapabilities 49 | 50 | armv7 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | UIUserInterfaceStyle 66 | Light 67 | 68 | 69 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/Controller/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/Controller/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import "ViewController.h" 9 | #import "ADTableView.h" 10 | #import "TestTableViewCell.h" 11 | #import "YYFPSLabel.h" 12 | #import "iConstant.h" 13 | #import "TestModel.h" 14 | 15 | @interface ViewController () 16 | 17 | @property (nonatomic, strong) ADTableView *tableView; 18 | @property (nonatomic, strong) YYFPSLabel *fpsLabel; 19 | 20 | @end 21 | 22 | @implementation ViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | [self loadSubViews]; 28 | [self loaddataArr]; 29 | } 30 | 31 | #pragma mark - Private 32 | 33 | - (void)loadSubViews { 34 | [self.view addSubview:self.tableView]; 35 | [self.view addSubview:self.fpsLabel]; 36 | } 37 | 38 | - (void)loaddataArr { 39 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 40 | NSString *appendingStr = @"😊😄😂😭💗🌹😳😫🤮😛等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等😊😄😂😭💗🌹😳😫🤮😛等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等😊😄😂😭💗🌹😳😫🤮😛等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等😊😄😂😭💗🌹😳😫🤮😛等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等😊😄😂😭💗🌹😳😫🤮😛等等😊😄😂😭💗🌹😳😫🤮😛等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等等😊😄😂😭💗🌹😳😫🤮😛"; 41 | for (int i = 0; i < 1000; i ++) { 42 | TestModel *m = [TestModel new]; 43 | m.title = [NSString stringWithFormat:@"紧急重要特别严重敏感新闻事件播报>>>%d", i]; 44 | m.imageURL = kPictureTest; 45 | m.content = [NSString stringWithFormat:@"此demo主要封装了异步绘制框架,针对大量文本的UILabel进行异步绘制,UIImageView异步渲染缓存图片,减轻主线程压力;并进行cell动态文本高度计算缓存,减少CPU计算,进而提升UITableView的流畅性,保持屏幕刷新帧率在60fps,具体可以应用到的场景有新闻图文混排列表高频消息列表%@......", [appendingStr substringToIndex:arc4random() % appendingStr.length]]; 46 | [self.tableView.dataArr addObject:m]; 47 | } 48 | 49 | dispatch_async(dispatch_get_main_queue(), ^{ 50 | [self.tableView reloadData]; 51 | }); 52 | }); 53 | } 54 | 55 | #pragma mark - UITableView 56 | 57 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 58 | NSLog(@"numberOfSectionsInTableView 被调用了~"); 59 | return 1; 60 | } 61 | 62 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 63 | return self.tableView.dataArr.count; 64 | } 65 | 66 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 67 | TestTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([TestTableViewCell class])]; 68 | return cell; 69 | } 70 | 71 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { 72 | TestTableViewCell *testCell = (TestTableViewCell *)cell; 73 | testCell.isDisplay = YES; 74 | [testCell assignModel:self.tableView.dataArr[indexPath.row]]; 75 | } 76 | 77 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath { 78 | TestTableViewCell *testCell = (TestTableViewCell *)cell; 79 | testCell.isDisplay = NO; 80 | } 81 | 82 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 83 | return [TestTableViewCell heightWith:self.tableView.dataArr[indexPath.row]]; 84 | } 85 | 86 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 87 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 88 | } 89 | 90 | #pragma mark - Lazy property 91 | 92 | - (ADTableView *)tableView { 93 | if (!_tableView) { 94 | _tableView = [[ADTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; 95 | _tableView.delegate = self; 96 | _tableView.dataSource = self; 97 | 98 | [_tableView registerClass:[TestTableViewCell class] forCellReuseIdentifier:NSStringFromClass([TestTableViewCell class])]; 99 | } 100 | return _tableView; 101 | } 102 | 103 | - (YYFPSLabel *)fpsLabel { 104 | if (!_fpsLabel) { 105 | _fpsLabel = [YYFPSLabel new]; 106 | _fpsLabel.frame = CGRectMake(10.0, (ScreenHeight - 40.0) / 2, 60.0, 40.0); 107 | } 108 | return _fpsLabel; 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/Model/TestModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestModel.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/16. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface TestModel : NSObject 13 | 14 | @property (nonatomic, copy) NSString *title; 15 | @property (nonatomic, copy) NSString *imageURL; 16 | @property (nonatomic, copy) NSString *content; 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/Model/TestModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestModel.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/16. 6 | // 7 | 8 | #import "TestModel.h" 9 | 10 | @implementation TestModel 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/View/TestTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestTableViewCell.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #import 9 | 10 | @class TestModel; 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface TestTableViewCell : UITableViewCell 15 | 16 | // 正在展示 17 | @property (nonatomic, assign) BOOL isDisplay; 18 | 19 | + (CGFloat)heightWith:(TestModel *)model; 20 | - (void)assignModel:(TestModel *)model; 21 | @end 22 | 23 | NS_ASSUME_NONNULL_END 24 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Main/View/TestTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestTableViewCell.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/14. 6 | // 7 | 8 | #define kAsyncDraw true // 异步开关 9 | //#define kOnlyShowText true // 仅显示文本进行测试 10 | 11 | #define kCommonSpace 10.0 12 | #define kTitleHeight 30.0 13 | #define kImageHeight 200.0 14 | 15 | #import "TestTableViewCell.h" 16 | #import "TestModel.h" 17 | #import "iConstant.h" 18 | 19 | #ifdef kAsyncDraw 20 | #import "ADImageView.h" 21 | #import "ADLabel.h" 22 | #endif 23 | 24 | @interface TestTableViewCell () 25 | 26 | #ifdef kAsyncDraw 27 | @property (nonatomic, weak) ADImageView *bigImageView; 28 | @property (nonatomic, weak) ADLabel *titleLabel; 29 | @property (nonatomic, weak) ADLabel *contentLabel; 30 | #else 31 | @property (nonatomic, weak) UIImageView *bigImageView; 32 | @property (nonatomic, weak) UILabel *titleLabel; 33 | @property (nonatomic, weak) UILabel *contentLabel; 34 | #endif 35 | 36 | @end 37 | 38 | @implementation TestTableViewCell 39 | 40 | #pragma mark - Pub MD 41 | 42 | // 缓存高度,减少cpu计算 43 | + (CGFloat)heightWith:(TestModel *)model { 44 | static NSCache *cache = nil; 45 | if (!cache) { 46 | cache = [NSCache new]; 47 | cache.name = @"TestTableViewCellHeightCache"; 48 | cache.countLimit = 10000; 49 | } 50 | 51 | NSString *content = model.content ?: @""; 52 | NSString *key = [NSString stringWithFormat:@"%@Key-%ld", cache.name, (long)model.hash]; 53 | NSValue *value = [cache objectForKey:key]; 54 | if (value) { return value.CGSizeValue.height; } 55 | 56 | /// 计算高度进行缓存 57 | CGSize contentSize = [content boundingRectWithSize:CGSizeMake(ScreenWidth - kCommonSpace * 2 , MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [self textFont], NSParagraphStyleAttributeName : [self paragraphStyle]} context:nil].size; 58 | 59 | CGSize totalSize = CGSizeMake(1.0, contentSize.height + kCommonSpace * 3 + kTitleHeight + kImageHeight); 60 | 61 | [cache setObject:@(totalSize) forKey:key]; 62 | 63 | return totalSize.height; 64 | } 65 | 66 | - (void)setIsDisplay:(BOOL)isDisplay { 67 | _isDisplay = isDisplay; 68 | 69 | #ifdef kAsyncDraw 70 | self.bigImageView.isDisplay = isDisplay; 71 | #endif 72 | } 73 | 74 | - (void)assignModel:(TestModel *)model { 75 | self.titleLabel.text = model.title; 76 | 77 | #ifdef kAsyncDraw 78 | 79 | self.contentLabel.text = model.content; 80 | #ifndef kOnlyShowText 81 | self.bigImageView.url = model.imageURL; 82 | #endif 83 | 84 | #else 85 | NSDictionary *attributes = @{NSFontAttributeName : self.contentLabel.font, 86 | NSParagraphStyleAttributeName : [self.class paragraphStyle] ?:[NSParagraphStyle new] 87 | }; 88 | NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:model.content attributes:attributes]; 89 | self.contentLabel.attributedText = attrStr; 90 | 91 | #ifndef kOnlyShowText 92 | NSURL *imgUrl = [NSURL URLWithString:model.imageURL]; 93 | NSData *imgData = [NSData dataWithContentsOfURL:imgUrl options:(NSDataReadingMappedIfSafe) error:nil]; 94 | self.bigImageView.contentMode = UIViewContentModeScaleAspectFill; 95 | self.bigImageView.image = [UIImage imageWithData:imgData]; 96 | #endif 97 | 98 | #endif 99 | } 100 | 101 | #pragma mark - Overide 102 | 103 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 104 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 105 | [self loadSubViews]; 106 | } 107 | return self; 108 | } 109 | 110 | - (void)layoutSubviews { 111 | [super layoutSubviews]; 112 | 113 | CGFloat itemWidth = ScreenWidth - kCommonSpace * 2; 114 | CGFloat totalHeight = self.contentView.bounds.size.height; 115 | 116 | self.titleLabel.frame = CGRectMake(kCommonSpace, kCommonSpace, itemWidth, kTitleHeight); 117 | self.bigImageView.frame = CGRectMake(kCommonSpace, CGRectGetMaxY(self.titleLabel.frame), itemWidth, kImageHeight); 118 | self.contentLabel.frame = CGRectMake(kCommonSpace, CGRectGetMaxY(self.bigImageView.frame) + kCommonSpace, itemWidth, totalHeight - (CGRectGetMaxY(self.bigImageView.frame) + kCommonSpace * 2)); 119 | } 120 | 121 | #pragma mark - Pri MD 122 | 123 | + (UIFont *)textFont { 124 | return [UIFont systemFontOfSize:14]; 125 | } 126 | 127 | + (NSParagraphStyle *)paragraphStyle { 128 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 129 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 130 | [paragraphStyle setLineSpacing:5.0]; 131 | return paragraphStyle; 132 | } 133 | 134 | - (void)loadSubViews { 135 | 136 | #ifdef kAsyncDraw 137 | ADLabel *titleLabel = [[ADLabel alloc] init]; 138 | 139 | ADImageView *imgView = [[ADImageView alloc] init]; 140 | 141 | ADLabel *contentLabel = [[ADLabel alloc] init]; 142 | contentLabel.paragraphStyle = [self.class paragraphStyle]; 143 | #else 144 | UILabel *titleLabel = [[UILabel alloc] init]; 145 | 146 | UIImageView *imgView = [[UIImageView alloc] init]; 147 | 148 | UILabel *contentLabel = [[UILabel alloc] init]; 149 | #endif 150 | 151 | titleLabel.backgroundColor = [UIColor yellowColor]; 152 | titleLabel.font = [UIFont systemFontOfSize:17]; 153 | titleLabel.textColor = [UIColor blackColor]; 154 | [self.contentView addSubview:titleLabel]; 155 | self.titleLabel = titleLabel; 156 | 157 | imgView.layer.cornerRadius = 8.0; 158 | imgView.layer.masksToBounds = YES; 159 | [self.contentView addSubview:imgView]; 160 | self.bigImageView = imgView; 161 | 162 | contentLabel.numberOfLines = 0; 163 | contentLabel.backgroundColor = [UIColor yellowColor]; 164 | contentLabel.font = [UIFont systemFontOfSize:14]; 165 | contentLabel.textColor = [UIColor grayColor]; 166 | [self.contentView addSubview:contentLabel]; 167 | self.contentLabel = contentLabel; 168 | } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Vendor/YYFPSLabel/YYFPSLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYFPSLabel.h 3 | // YYKitExample 4 | // 5 | // Created by ibireme on 15/9/3. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | Show Screen FPS... 13 | 14 | The maximum fps in OSX/iOS Simulator is 60.00. 15 | The maximum fps on iPhone is 59.97. 16 | The maxmium fps on iPad is 60.0. 17 | */ 18 | @interface YYFPSLabel : UILabel 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Vendor/YYFPSLabel/YYFPSLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYFPSLabel.m 3 | // YYKitExample 4 | // 5 | // Created by ibireme on 15/9/3. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "YYFPSLabel.h" 10 | #import "YYWeakProxy.h" 11 | 12 | #define kSize CGSizeMake(55, 20) 13 | 14 | @implementation YYFPSLabel { 15 | CADisplayLink *_link; 16 | NSUInteger _count; 17 | NSTimeInterval _lastTime; 18 | UIFont *_font; 19 | UIFont *_subFont; 20 | 21 | NSTimeInterval _llll; 22 | } 23 | 24 | - (instancetype)initWithFrame:(CGRect)frame { 25 | if (frame.size.width == 0 && frame.size.height == 0) { 26 | frame.size = kSize; 27 | } 28 | self = [super initWithFrame:frame]; 29 | 30 | self.layer.cornerRadius = 5; 31 | self.clipsToBounds = YES; 32 | self.textAlignment = NSTextAlignmentCenter; 33 | self.userInteractionEnabled = NO; 34 | self.backgroundColor = [UIColor colorWithWhite:0.000 alpha:0.700]; 35 | 36 | _font = [UIFont fontWithName:@"Menlo" size:14]; 37 | if (_font) { 38 | _subFont = [UIFont fontWithName:@"Menlo" size:4]; 39 | } else { 40 | _font = [UIFont fontWithName:@"Courier" size:14]; 41 | _subFont = [UIFont fontWithName:@"Courier" size:4]; 42 | } 43 | self.font = _font; 44 | 45 | _link = [CADisplayLink displayLinkWithTarget:[YYWeakProxy proxyWithTarget:self] selector:@selector(tick:)]; 46 | [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 47 | return self; 48 | } 49 | 50 | - (void)dealloc { 51 | [_link invalidate]; 52 | } 53 | 54 | - (CGSize)sizeThatFits:(CGSize)size { 55 | return kSize; 56 | } 57 | 58 | - (void)tick:(CADisplayLink *)link { 59 | if (_lastTime == 0) { 60 | _lastTime = link.timestamp; 61 | return; 62 | } 63 | 64 | _count++; 65 | NSTimeInterval delta = link.timestamp - _lastTime; 66 | if (delta < 1) return; 67 | _lastTime = link.timestamp; 68 | float fps = _count / delta; 69 | _count = 0; 70 | 71 | CGFloat progress = fps / 60.0; 72 | UIColor *color = [UIColor colorWithHue:0.27 * (progress - 0.2) saturation:1 brightness:0.9 alpha:1]; 73 | 74 | NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%d FPS",(int)round(fps)]]; 75 | [text addAttributes:@{NSForegroundColorAttributeName : color} range:NSMakeRange(0, text.length - 3)]; 76 | [text addAttributes:@{NSForegroundColorAttributeName : [UIColor whiteColor]} range:NSMakeRange(text.length - 3, 3)]; 77 | [text addAttributes:@{NSFontAttributeName : _subFont} range:NSMakeRange(text.length - 4, 1)]; 78 | 79 | self.attributedText = text; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Vendor/YYFPSLabel/YYWeakProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYWeakProxy.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | A proxy used to hold a weak object. 18 | It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink. 19 | 20 | sample code: 21 | 22 | @implementation MyView { 23 | NSTimer *_timer; 24 | } 25 | 26 | - (void)initTimer { 27 | YYWeakProxy *proxy = [YYWeakProxy proxyWithTarget:self]; 28 | _timer = [NSTimer timerWithTimeInterval:0.1 target:proxy selector:@selector(tick:) userInfo:nil repeats:YES]; 29 | } 30 | 31 | - (void)tick:(NSTimer *)timer {...} 32 | @end 33 | */ 34 | @interface YYWeakProxy : NSProxy 35 | 36 | /** 37 | The proxy target. 38 | */ 39 | @property (nullable, nonatomic, weak, readonly) id target; 40 | 41 | /** 42 | Creates a new weak proxy for target. 43 | 44 | @param target Target object. 45 | 46 | @return A new proxy object. 47 | */ 48 | - (instancetype)initWithTarget:(id)target; 49 | 50 | /** 51 | Creates a new weak proxy for target. 52 | 53 | @param target Target object. 54 | 55 | @return A new proxy object. 56 | */ 57 | + (instancetype)proxyWithTarget:(id)target; 58 | 59 | @end 60 | 61 | NS_ASSUME_NONNULL_END 62 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/Vendor/YYFPSLabel/YYWeakProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYWeakProxy.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 14/10/18. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYWeakProxy.h" 13 | 14 | 15 | @implementation YYWeakProxy 16 | 17 | - (instancetype)initWithTarget:(id)target { 18 | _target = target; 19 | return self; 20 | } 21 | 22 | + (instancetype)proxyWithTarget:(id)target { 23 | return [[YYWeakProxy alloc] initWithTarget:target]; 24 | } 25 | 26 | - (id)forwardingTargetForSelector:(SEL)selector { 27 | return _target; 28 | } 29 | 30 | - (void)forwardInvocation:(NSInvocation *)invocation { 31 | void *null = NULL; 32 | [invocation setReturnValue:&null]; 33 | } 34 | 35 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 36 | return [NSObject instanceMethodSignatureForSelector:@selector(init)]; 37 | } 38 | 39 | - (BOOL)respondsToSelector:(SEL)aSelector { 40 | return [_target respondsToSelector:aSelector]; 41 | } 42 | 43 | - (BOOL)isEqual:(id)object { 44 | return [_target isEqual:object]; 45 | } 46 | 47 | - (NSUInteger)hash { 48 | return [_target hash]; 49 | } 50 | 51 | - (Class)superclass { 52 | return [_target superclass]; 53 | } 54 | 55 | - (Class)class { 56 | return [_target class]; 57 | } 58 | 59 | - (BOOL)isKindOfClass:(Class)aClass { 60 | return [_target isKindOfClass:aClass]; 61 | } 62 | 63 | - (BOOL)isMemberOfClass:(Class)aClass { 64 | return [_target isMemberOfClass:aClass]; 65 | } 66 | 67 | - (BOOL)conformsToProtocol:(Protocol *)aProtocol { 68 | return [_target conformsToProtocol:aProtocol]; 69 | } 70 | 71 | - (BOOL)isProxy { 72 | return YES; 73 | } 74 | 75 | - (NSString *)description { 76 | return [_target description]; 77 | } 78 | 79 | - (NSString *)debugDescription { 80 | return [_target debugDescription]; 81 | } 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AsyncDrawDemo 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemoTests/AsyncDrawDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncDrawDemoTests.m 3 | // AsyncDrawDemoTests 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | 10 | @interface AsyncDrawDemoTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation AsyncDrawDemoTests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | } 19 | 20 | - (void)tearDown { 21 | // Put teardown code here. This method is called after the invocation of each test method in the class. 22 | } 23 | 24 | - (void)testExample { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | - (void)testPerformanceExample { 30 | // This is an example of a performance test case. 31 | [self measureBlock:^{ 32 | // Put the code you want to measure the time of here. 33 | }]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemoUITests/AsyncDrawDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AsyncDrawDemoUITests.m 3 | // AsyncDrawDemoUITests 4 | // 5 | // Created by Stk on 2020/11/9. 6 | // 7 | 8 | #import 9 | 10 | @interface AsyncDrawDemoUITests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation AsyncDrawDemoUITests 15 | 16 | - (void)setUp { 17 | // Put setup code here. This method is called before the invocation of each test method in the class. 18 | 19 | // In UI tests it is usually best to stop immediately when a failure occurs. 20 | self.continueAfterFailure = NO; 21 | 22 | // 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. 23 | } 24 | 25 | - (void)tearDown { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | - (void)testExample { 30 | // UI tests must launch the application that they test. 31 | XCUIApplication *app = [[XCUIApplication alloc] init]; 32 | [app launch]; 33 | 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | - (void)testLaunchPerformance { 39 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 40 | // This measures how long it takes to launch your application. 41 | [self measureWithMetrics:@[[[XCTApplicationLaunchMetric alloc] init]] block:^{ 42 | [[[XCUIApplication alloc] init] launch]; 43 | }]; 44 | } 45 | } 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /AsyncDrawDemo/AsyncDrawDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | --------------------------------------------------------------------------------