├── .gitignore ├── LICENSE ├── MSCachedAsyncViewDrawing-SampleProject.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── MSCachedAsyncViewDrawing-SampleProject ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── MSAppDelegate.h ├── MSAppDelegate.m ├── MSAsyncDrawingCell.h ├── MSAsyncDrawingCell.m ├── MSAsyncDrawingTVC.h ├── MSAsyncDrawingTVC.m ├── MSCachedAsyncViewDrawing-SampleProject-Info.plist ├── MSCachedAsyncViewDrawing-SampleProject-Prefix.pch ├── MSCommonTVC.h ├── MSCommonTVC.m ├── MSCustomDrawnView.h ├── MSCustomDrawnView.m ├── MSCustomDrawnViewCell.h ├── MSTraditionalDrawingCell.h ├── MSTraditionalDrawingCell.m ├── MSTraditionalDrawingTVC.h ├── MSTraditionalDrawingTVC.m ├── en.lproj │ └── InfoPlist.strings ├── main.m ├── mindsnacks_logo.png └── mindsnacks_logo@2x.png ├── MSCachedAsyncViewDrawing.h ├── MSCachedAsyncViewDrawing.m ├── MSCachedAsyncViewDrawing.podspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.pbxuser 3 | *.perspectivev3 4 | *.xcuserstate 5 | *.mergesave 6 | *.bak 7 | .~* 8 | .DS_Store 9 | xcuserdata 10 | Thumbs.db 11 | build 12 | Build 13 | Generated 14 | GeneratedAssets 15 | InfoPlist_Prefix.h 16 | test-reports 17 | !contents.xcworkspacedata 18 | .svn 19 | BuildScripts/Provisioning-Profiles 20 | .BridgeSort 21 | .virtualenv 22 | .venv 23 | .env 24 | .pyc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 MindSnacks (http://mindsnacks.com/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D01B4443164EFF9F00842F7F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01B4442164EFF9F00842F7F /* UIKit.framework */; }; 11 | D01B4445164EFF9F00842F7F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01B4444164EFF9F00842F7F /* Foundation.framework */; }; 12 | D01B4447164EFF9F00842F7F /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01B4446164EFF9F00842F7F /* CoreGraphics.framework */; }; 13 | D01B444D164EFF9F00842F7F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D01B444B164EFF9F00842F7F /* InfoPlist.strings */; }; 14 | D01B444F164EFF9F00842F7F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B444E164EFF9F00842F7F /* main.m */; }; 15 | D01B4453164EFF9F00842F7F /* MSAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B4452164EFF9F00842F7F /* MSAppDelegate.m */; }; 16 | D01B4455164EFF9F00842F7F /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = D01B4454164EFF9F00842F7F /* Default.png */; }; 17 | D01B4457164EFF9F00842F7F /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D01B4456164EFF9F00842F7F /* Default@2x.png */; }; 18 | D01B4459164EFF9F00842F7F /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D01B4458164EFF9F00842F7F /* Default-568h@2x.png */; }; 19 | D01B447B164EFFDC00842F7F /* MSCachedAsyncViewDrawing.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B447A164EFFDC00842F7F /* MSCachedAsyncViewDrawing.m */; }; 20 | D01B447E164F11BC00842F7F /* MSCustomDrawnView.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B447D164F11BC00842F7F /* MSCustomDrawnView.m */; }; 21 | D01B4481164F143000842F7F /* MSTraditionalDrawingTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B4480164F143000842F7F /* MSTraditionalDrawingTVC.m */; }; 22 | D01B4485164F146600842F7F /* MSAsyncDrawingTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B4484164F146600842F7F /* MSAsyncDrawingTVC.m */; }; 23 | D01B4489164F147F00842F7F /* MSCommonTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B4488164F147F00842F7F /* MSCommonTVC.m */; }; 24 | D01B448F164F16AD00842F7F /* MSTraditionalDrawingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B448E164F16AC00842F7F /* MSTraditionalDrawingCell.m */; }; 25 | D01B4492164F16C400842F7F /* MSAsyncDrawingCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D01B4491164F16C400842F7F /* MSAsyncDrawingCell.m */; }; 26 | D0E49054164F218400485EC0 /* mindsnacks_logo@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D0E49053164F218400485EC0 /* mindsnacks_logo@2x.png */; }; 27 | D0E490A0164F25C000485EC0 /* mindsnacks_logo.png in Resources */ = {isa = PBXBuildFile; fileRef = D0E4909F164F25C000485EC0 /* mindsnacks_logo.png */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | D01B443E164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MSCachedAsyncViewDrawing-SampleProject.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | D01B4442164EFF9F00842F7F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 33 | D01B4444164EFF9F00842F7F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 34 | D01B4446164EFF9F00842F7F /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 35 | D01B444A164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MSCachedAsyncViewDrawing-SampleProject-Info.plist"; sourceTree = ""; }; 36 | D01B444C164EFF9F00842F7F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 37 | D01B444E164EFF9F00842F7F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | D01B4450164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MSCachedAsyncViewDrawing-SampleProject-Prefix.pch"; sourceTree = ""; }; 39 | D01B4451164EFF9F00842F7F /* MSAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSAppDelegate.h; sourceTree = ""; }; 40 | D01B4452164EFF9F00842F7F /* MSAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MSAppDelegate.m; sourceTree = ""; }; 41 | D01B4454164EFF9F00842F7F /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 42 | D01B4456164EFF9F00842F7F /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 43 | D01B4458164EFF9F00842F7F /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 44 | D01B4479164EFFDC00842F7F /* MSCachedAsyncViewDrawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCachedAsyncViewDrawing.h; sourceTree = SOURCE_ROOT; }; 45 | D01B447A164EFFDC00842F7F /* MSCachedAsyncViewDrawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSCachedAsyncViewDrawing.m; sourceTree = SOURCE_ROOT; }; 46 | D01B447C164F11BC00842F7F /* MSCustomDrawnView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCustomDrawnView.h; sourceTree = ""; }; 47 | D01B447D164F11BC00842F7F /* MSCustomDrawnView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSCustomDrawnView.m; sourceTree = ""; }; 48 | D01B447F164F143000842F7F /* MSTraditionalDrawingTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSTraditionalDrawingTVC.h; sourceTree = ""; }; 49 | D01B4480164F143000842F7F /* MSTraditionalDrawingTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSTraditionalDrawingTVC.m; sourceTree = ""; }; 50 | D01B4483164F146600842F7F /* MSAsyncDrawingTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSAsyncDrawingTVC.h; sourceTree = ""; }; 51 | D01B4484164F146600842F7F /* MSAsyncDrawingTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSAsyncDrawingTVC.m; sourceTree = ""; }; 52 | D01B4487164F147F00842F7F /* MSCommonTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCommonTVC.h; sourceTree = ""; }; 53 | D01B4488164F147F00842F7F /* MSCommonTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSCommonTVC.m; sourceTree = ""; }; 54 | D01B448A164F165C00842F7F /* MSCustomDrawnViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCustomDrawnViewCell.h; sourceTree = ""; }; 55 | D01B448D164F16AC00842F7F /* MSTraditionalDrawingCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSTraditionalDrawingCell.h; sourceTree = ""; }; 56 | D01B448E164F16AC00842F7F /* MSTraditionalDrawingCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSTraditionalDrawingCell.m; sourceTree = ""; }; 57 | D01B4490164F16C400842F7F /* MSAsyncDrawingCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSAsyncDrawingCell.h; sourceTree = ""; }; 58 | D01B4491164F16C400842F7F /* MSAsyncDrawingCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSAsyncDrawingCell.m; sourceTree = ""; }; 59 | D0E49053164F218400485EC0 /* mindsnacks_logo@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mindsnacks_logo@2x.png"; sourceTree = ""; }; 60 | D0E4909F164F25C000485EC0 /* mindsnacks_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mindsnacks_logo.png; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | D01B443B164EFF9F00842F7F /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | D01B4443164EFF9F00842F7F /* UIKit.framework in Frameworks */, 69 | D01B4445164EFF9F00842F7F /* Foundation.framework in Frameworks */, 70 | D01B4447164EFF9F00842F7F /* CoreGraphics.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | D01B4433164EFF9F00842F7F = { 78 | isa = PBXGroup; 79 | children = ( 80 | D01B4448164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject */, 81 | D01B4478164EFFC400842F7F /* MSCachedAsyncViewDrawing */, 82 | D01B4441164EFF9F00842F7F /* Frameworks */, 83 | D01B443F164EFF9F00842F7F /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | D01B443F164EFF9F00842F7F /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | D01B443E164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | D01B4441164EFF9F00842F7F /* Frameworks */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | D01B4442164EFF9F00842F7F /* UIKit.framework */, 99 | D01B4444164EFF9F00842F7F /* Foundation.framework */, 100 | D01B4446164EFF9F00842F7F /* CoreGraphics.framework */, 101 | ); 102 | name = Frameworks; 103 | sourceTree = ""; 104 | }; 105 | D01B4448164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | D01B4451164EFF9F00842F7F /* MSAppDelegate.h */, 109 | D01B4452164EFF9F00842F7F /* MSAppDelegate.m */, 110 | D01B4482164F144E00842F7F /* ViewControllers */, 111 | D01B4486164F146C00842F7F /* Views */, 112 | D01B4449164EFF9F00842F7F /* Supporting Files */, 113 | ); 114 | path = "MSCachedAsyncViewDrawing-SampleProject"; 115 | sourceTree = ""; 116 | }; 117 | D01B4449164EFF9F00842F7F /* Supporting Files */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | D0E4909F164F25C000485EC0 /* mindsnacks_logo.png */, 121 | D0E49053164F218400485EC0 /* mindsnacks_logo@2x.png */, 122 | D01B444A164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject-Info.plist */, 123 | D01B444B164EFF9F00842F7F /* InfoPlist.strings */, 124 | D01B444E164EFF9F00842F7F /* main.m */, 125 | D01B4450164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject-Prefix.pch */, 126 | D01B4454164EFF9F00842F7F /* Default.png */, 127 | D01B4456164EFF9F00842F7F /* Default@2x.png */, 128 | D01B4458164EFF9F00842F7F /* Default-568h@2x.png */, 129 | ); 130 | name = "Supporting Files"; 131 | sourceTree = ""; 132 | }; 133 | D01B4478164EFFC400842F7F /* MSCachedAsyncViewDrawing */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | D01B4479164EFFDC00842F7F /* MSCachedAsyncViewDrawing.h */, 137 | D01B447A164EFFDC00842F7F /* MSCachedAsyncViewDrawing.m */, 138 | ); 139 | name = MSCachedAsyncViewDrawing; 140 | path = "MSCachedAsyncViewDrawing-SampleProject"; 141 | sourceTree = ""; 142 | }; 143 | D01B4482164F144E00842F7F /* ViewControllers */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | D01B4487164F147F00842F7F /* MSCommonTVC.h */, 147 | D01B4488164F147F00842F7F /* MSCommonTVC.m */, 148 | D01B447F164F143000842F7F /* MSTraditionalDrawingTVC.h */, 149 | D01B4480164F143000842F7F /* MSTraditionalDrawingTVC.m */, 150 | D01B4483164F146600842F7F /* MSAsyncDrawingTVC.h */, 151 | D01B4484164F146600842F7F /* MSAsyncDrawingTVC.m */, 152 | ); 153 | name = ViewControllers; 154 | sourceTree = ""; 155 | }; 156 | D01B4486164F146C00842F7F /* Views */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | D01B447C164F11BC00842F7F /* MSCustomDrawnView.h */, 160 | D01B447D164F11BC00842F7F /* MSCustomDrawnView.m */, 161 | D01B448A164F165C00842F7F /* MSCustomDrawnViewCell.h */, 162 | D01B448D164F16AC00842F7F /* MSTraditionalDrawingCell.h */, 163 | D01B448E164F16AC00842F7F /* MSTraditionalDrawingCell.m */, 164 | D01B4490164F16C400842F7F /* MSAsyncDrawingCell.h */, 165 | D01B4491164F16C400842F7F /* MSAsyncDrawingCell.m */, 166 | ); 167 | name = Views; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | D01B443D164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = D01B4470164EFF9F00842F7F /* Build configuration list for PBXNativeTarget "MSCachedAsyncViewDrawing-SampleProject" */; 176 | buildPhases = ( 177 | D01B443A164EFF9F00842F7F /* Sources */, 178 | D01B443B164EFF9F00842F7F /* Frameworks */, 179 | D01B443C164EFF9F00842F7F /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = "MSCachedAsyncViewDrawing-SampleProject"; 186 | productName = "MSCachedAsyncViewDrawing-SampleProject"; 187 | productReference = D01B443E164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject.app */; 188 | productType = "com.apple.product-type.application"; 189 | }; 190 | /* End PBXNativeTarget section */ 191 | 192 | /* Begin PBXProject section */ 193 | D01B4435164EFF9F00842F7F /* Project object */ = { 194 | isa = PBXProject; 195 | attributes = { 196 | CLASSPREFIX = MS; 197 | LastUpgradeCheck = 0450; 198 | ORGANIZATIONNAME = MindSnacks; 199 | }; 200 | buildConfigurationList = D01B4438164EFF9F00842F7F /* Build configuration list for PBXProject "MSCachedAsyncViewDrawing-SampleProject" */; 201 | compatibilityVersion = "Xcode 3.2"; 202 | developmentRegion = English; 203 | hasScannedForEncodings = 0; 204 | knownRegions = ( 205 | en, 206 | ); 207 | mainGroup = D01B4433164EFF9F00842F7F; 208 | productRefGroup = D01B443F164EFF9F00842F7F /* Products */; 209 | projectDirPath = ""; 210 | projectRoot = ""; 211 | targets = ( 212 | D01B443D164EFF9F00842F7F /* MSCachedAsyncViewDrawing-SampleProject */, 213 | ); 214 | }; 215 | /* End PBXProject section */ 216 | 217 | /* Begin PBXResourcesBuildPhase section */ 218 | D01B443C164EFF9F00842F7F /* Resources */ = { 219 | isa = PBXResourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | D01B444D164EFF9F00842F7F /* InfoPlist.strings in Resources */, 223 | D01B4455164EFF9F00842F7F /* Default.png in Resources */, 224 | D01B4457164EFF9F00842F7F /* Default@2x.png in Resources */, 225 | D01B4459164EFF9F00842F7F /* Default-568h@2x.png in Resources */, 226 | D0E49054164F218400485EC0 /* mindsnacks_logo@2x.png in Resources */, 227 | D0E490A0164F25C000485EC0 /* mindsnacks_logo.png in Resources */, 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | }; 231 | /* End PBXResourcesBuildPhase section */ 232 | 233 | /* Begin PBXSourcesBuildPhase section */ 234 | D01B443A164EFF9F00842F7F /* Sources */ = { 235 | isa = PBXSourcesBuildPhase; 236 | buildActionMask = 2147483647; 237 | files = ( 238 | D01B444F164EFF9F00842F7F /* main.m in Sources */, 239 | D01B4453164EFF9F00842F7F /* MSAppDelegate.m in Sources */, 240 | D01B447B164EFFDC00842F7F /* MSCachedAsyncViewDrawing.m in Sources */, 241 | D01B447E164F11BC00842F7F /* MSCustomDrawnView.m in Sources */, 242 | D01B4481164F143000842F7F /* MSTraditionalDrawingTVC.m in Sources */, 243 | D01B4485164F146600842F7F /* MSAsyncDrawingTVC.m in Sources */, 244 | D01B4489164F147F00842F7F /* MSCommonTVC.m in Sources */, 245 | D01B448F164F16AD00842F7F /* MSTraditionalDrawingCell.m in Sources */, 246 | D01B4492164F16C400842F7F /* MSAsyncDrawingCell.m in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | /* End PBXSourcesBuildPhase section */ 251 | 252 | /* Begin PBXVariantGroup section */ 253 | D01B444B164EFF9F00842F7F /* InfoPlist.strings */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | D01B444C164EFF9F00842F7F /* en */, 257 | ); 258 | name = InfoPlist.strings; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | D01B446E164EFF9F00842F7F /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_OBJC_ARC = YES; 271 | CLANG_WARN_EMPTY_BODY = YES; 272 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 273 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 274 | COPY_PHASE_STRIP = NO; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_DYNAMIC_NO_PIC = NO; 277 | GCC_OPTIMIZATION_LEVEL = 0; 278 | GCC_PREPROCESSOR_DEFINITIONS = ( 279 | "DEBUG=1", 280 | "$(inherited)", 281 | ); 282 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 284 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 285 | GCC_WARN_UNUSED_VARIABLE = YES; 286 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 287 | ONLY_ACTIVE_ARCH = YES; 288 | SDKROOT = iphoneos; 289 | }; 290 | name = Debug; 291 | }; 292 | D01B446F164EFF9F00842F7F /* Release */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_OBJC_ARC = YES; 299 | CLANG_WARN_EMPTY_BODY = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 302 | COPY_PHASE_STRIP = YES; 303 | GCC_C_LANGUAGE_STANDARD = gnu99; 304 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 308 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 309 | SDKROOT = iphoneos; 310 | VALIDATE_PRODUCT = YES; 311 | }; 312 | name = Release; 313 | }; 314 | D01B4471164EFF9F00842F7F /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 318 | GCC_PREFIX_HEADER = "MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Prefix.pch"; 319 | INFOPLIST_FILE = "MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Info.plist"; 320 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | WRAPPER_EXTENSION = app; 323 | }; 324 | name = Debug; 325 | }; 326 | D01B4472164EFF9F00842F7F /* Release */ = { 327 | isa = XCBuildConfiguration; 328 | buildSettings = { 329 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 330 | GCC_PREFIX_HEADER = "MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Prefix.pch"; 331 | INFOPLIST_FILE = "MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Info.plist"; 332 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 333 | PRODUCT_NAME = "$(TARGET_NAME)"; 334 | WRAPPER_EXTENSION = app; 335 | }; 336 | name = Release; 337 | }; 338 | /* End XCBuildConfiguration section */ 339 | 340 | /* Begin XCConfigurationList section */ 341 | D01B4438164EFF9F00842F7F /* Build configuration list for PBXProject "MSCachedAsyncViewDrawing-SampleProject" */ = { 342 | isa = XCConfigurationList; 343 | buildConfigurations = ( 344 | D01B446E164EFF9F00842F7F /* Debug */, 345 | D01B446F164EFF9F00842F7F /* Release */, 346 | ); 347 | defaultConfigurationIsVisible = 0; 348 | defaultConfigurationName = Release; 349 | }; 350 | D01B4470164EFF9F00842F7F /* Build configuration list for PBXNativeTarget "MSCachedAsyncViewDrawing-SampleProject" */ = { 351 | isa = XCConfigurationList; 352 | buildConfigurations = ( 353 | D01B4471164EFF9F00842F7F /* Debug */, 354 | D01B4472164EFF9F00842F7F /* Release */, 355 | ); 356 | defaultConfigurationIsVisible = 0; 357 | defaultConfigurationName = Release; 358 | }; 359 | /* End XCConfigurationList section */ 360 | }; 361 | rootObject = D01B4435164EFF9F00842F7F /* Project object */; 362 | } 363 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindsnacks/MSCachedAsyncViewDrawing/75a52ad3a7d749835f5c33a7e1106d1fa0a4039a/MSCachedAsyncViewDrawing-SampleProject/Default-568h@2x.png -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindsnacks/MSCachedAsyncViewDrawing/75a52ad3a7d749835f5c33a7e1106d1fa0a4039a/MSCachedAsyncViewDrawing-SampleProject/Default.png -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindsnacks/MSCachedAsyncViewDrawing/75a52ad3a7d749835f5c33a7e1106d1fa0a4039a/MSCachedAsyncViewDrawing-SampleProject/Default@2x.png -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSAppDelegate.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MSAppDelegate : UIResponder 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSAppDelegate.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSAppDelegate.h" 10 | 11 | #import "MSTraditionalDrawingTVC.h" 12 | #import "MSAsyncDrawingTVC.h" 13 | 14 | @interface MSAppDelegate () 15 | 16 | @property (strong, nonatomic) UIWindow *window; 17 | @property (strong, nonatomic) UITabBarController *tabBarController; 18 | 19 | @end 20 | 21 | @implementation MSAppDelegate 22 | 23 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 24 | { 25 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 26 | 27 | MSTraditionalDrawingTVC *vc1 = [[MSTraditionalDrawingTVC alloc] init]; 28 | MSAsyncDrawingTVC *vc2 = [[MSAsyncDrawingTVC alloc] init]; 29 | 30 | self.tabBarController = [[UITabBarController alloc] init]; 31 | self.tabBarController.viewControllers = @[vc1, vc2]; 32 | 33 | self.window.rootViewController = self.tabBarController; 34 | [self.window makeKeyAndVisible]; 35 | 36 | return YES; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAsyncDrawingCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSAsyncDrawingCell.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCustomDrawnViewCell.h" 10 | 11 | @interface MSAsyncDrawingCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAsyncDrawingCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSAsyncDrawingCell.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSAsyncDrawingCell.h" 10 | 11 | #import "MSCustomDrawnView.h" 12 | #import "MSCachedAsyncViewDrawing.h" 13 | 14 | /** 15 | * @discussion UIImageView is orders of magnitude faster than -[UIImage drawInRect]. 16 | * Thanks to MSCachedAsyncViewDrawing, we can take the rendered UIImage and pass it to the UIImageView. 17 | */ 18 | @interface MSCustomDrawnViewImageView : UIImageView 19 | 20 | @property (atomic, strong) UIColor *circleColor; 21 | 22 | @end 23 | 24 | @implementation MSCustomDrawnViewImageView 25 | 26 | @synthesize circleColor = _circleColor; 27 | 28 | - (void)setFrame:(CGRect)frame 29 | { 30 | const BOOL sizeHasChanged = !CGSizeEqualToSize(frame.size, self.frame.size); 31 | 32 | [super setFrame:frame]; 33 | 34 | if (sizeHasChanged) 35 | { 36 | [self setNeedsImageReload]; 37 | } 38 | } 39 | 40 | - (void)setCircleColor:(UIColor *)circleColor 41 | { 42 | @synchronized(self) 43 | { 44 | if (circleColor != _circleColor) 45 | { 46 | _circleColor = circleColor; 47 | 48 | [self setNeedsImageReload]; 49 | } 50 | } 51 | } 52 | 53 | - (UIColor *)circleColor 54 | { 55 | @synchronized(self) 56 | { 57 | return _circleColor; 58 | } 59 | } 60 | 61 | - (void)setBackgroundColor:(UIColor *)backgroundColor 62 | { 63 | const BOOL backgroundColorHasChanged = ![backgroundColor isEqual:self.backgroundColor]; 64 | 65 | [super setBackgroundColor:backgroundColor]; 66 | 67 | if (backgroundColorHasChanged) 68 | { 69 | [self setNeedsImageReload]; 70 | } 71 | } 72 | 73 | - (void)setNeedsImageReload 74 | { 75 | self.image = nil; 76 | [self loadImage]; 77 | } 78 | 79 | - (void)loadImage 80 | { 81 | if (CGSizeEqualToSize(self.frame.size, CGSizeZero) || !self.circleColor) 82 | { 83 | self.image = nil; 84 | 85 | return; 86 | } 87 | 88 | UIColor *currentCircleColor = self.circleColor; 89 | CGSize currentSize = self.frame.size; 90 | 91 | NSString *cacheKey = [NSString stringWithFormat:@"com.mindsnacks.circle.%@.%@", currentCircleColor, NSStringFromCGSize(self.frame.size)]; 92 | [[MSCachedAsyncViewDrawing sharedInstance] drawViewAsyncWithCacheKey:cacheKey 93 | size:self.frame.size 94 | backgroundColor:self.backgroundColor 95 | drawBlock:^(CGRect frame) 96 | { 97 | MSCustomDrawnView *view = [[MSCustomDrawnView alloc] initWithFrame:frame 98 | circleColor:currentCircleColor]; 99 | 100 | [view drawRect:frame]; 101 | } 102 | completionBlock:^(UIImage *drawnImage) 103 | { 104 | // Prevent race conditions: 105 | BOOL receivedImageHasCurrentConfiguration = ([currentCircleColor isEqual:self.circleColor] && CGSizeEqualToSize(currentSize, self.frame.size)); 106 | 107 | if (receivedImageHasCurrentConfiguration) 108 | { 109 | self.image = drawnImage; 110 | } 111 | }]; 112 | } 113 | 114 | @end 115 | 116 | @interface MSAsyncDrawingCell () 117 | 118 | @property (nonatomic, strong) NSArray *imageViews; 119 | 120 | @end 121 | 122 | @implementation MSAsyncDrawingCell 123 | 124 | @synthesize circleColors = _circleColors; 125 | 126 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 127 | { 128 | if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) 129 | { 130 | self.contentView.backgroundColor = kBackgroundColor; 131 | 132 | NSMutableArray *imageViews = [NSMutableArray array]; 133 | for (NSUInteger i = 0; i < kViewsPerRow; i++) 134 | { 135 | MSCustomDrawnViewImageView *imageView = [[MSCustomDrawnViewImageView alloc] init]; 136 | imageView.backgroundColor = kBackgroundColor; 137 | [self addSubview:imageView]; 138 | [imageViews addObject:imageView]; 139 | } 140 | 141 | self.imageViews = imageViews; 142 | } 143 | 144 | return self; 145 | } 146 | 147 | - (void)layoutSubviews 148 | { 149 | [super layoutSubviews]; 150 | 151 | const CGFloat widthPerView = self.bounds.size.width / (CGFloat)kViewsPerRow; 152 | const CGFloat viewHeight = self.bounds.size.height; 153 | 154 | __block CGFloat currentX = 0.0f; 155 | 156 | [self.imageViews enumerateObjectsUsingBlock:^(MSCustomDrawnViewImageView *imageView, NSUInteger idx, BOOL *stop) { 157 | imageView.frame = CGRectIntegral(CGRectMake(currentX, 158 | 0.0f, 159 | widthPerView, 160 | viewHeight)); 161 | 162 | currentX += widthPerView; 163 | }]; 164 | } 165 | 166 | - (void)setCircleColors:(NSArray *)circleColors 167 | { 168 | if (circleColors != _circleColors) 169 | { 170 | _circleColors = circleColors; 171 | 172 | [circleColors enumerateObjectsUsingBlock:^(UIColor *color, NSUInteger idx, BOOL *stop) { 173 | [self.imageViews[idx] setCircleColor:color]; 174 | }]; 175 | } 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAsyncDrawingTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSAsyncDrawingTVC.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCommonTVC.h" 10 | 11 | @interface MSAsyncDrawingTVC : MSCommonTVC 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSAsyncDrawingTVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSAsyncDrawingTVC.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSAsyncDrawingTVC.h" 10 | 11 | @interface MSAsyncDrawingTVC () 12 | 13 | @end 14 | 15 | @interface MSAsyncDrawingCell : NSObject 16 | 17 | @end 18 | 19 | @implementation MSAsyncDrawingTVC 20 | 21 | - (id)init 22 | { 23 | if ((self = [super initWithCellClass:[MSAsyncDrawingCell class]])) 24 | { 25 | self.title = @"MSCachedAsyncViewDrawing"; 26 | } 27 | 28 | return self; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | mindsnacks.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCachedAsyncViewDrawing-SampleProject-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'MSCachedAsyncViewDrawing-SampleProject' target in the 'MSCachedAsyncViewDrawing-SampleProject' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCommonTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSCommonTVC.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "MSCustomDrawnViewCell.h" 12 | 13 | @interface MSCommonTVC : UITableViewController 14 | 15 | /** 16 | * @discussion designated initializer 17 | * @param cellClass has to conform to `MSCustomDrawnViewCell` 18 | */ 19 | - (id)initWithCellClass:(Class)cellClass; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCommonTVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSCommonTVC.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCommonTVC.h" 10 | 11 | #import "MSCustomDrawnViewCell.h" 12 | 13 | #define kNumberOfRows 200 14 | #define kRowHeight 100.0f 15 | 16 | @interface MSCommonTVC () 17 | 18 | @property (nonatomic, assign) Class cellClass; 19 | 20 | @end 21 | 22 | @implementation MSCommonTVC 23 | 24 | - (id)initWithCellClass:(Class)cellClass 25 | { 26 | if ((self = [super initWithStyle:UITableViewStylePlain])) 27 | { 28 | self.cellClass = cellClass; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | #pragma mark - View Life Cycle 35 | 36 | - (void)viewDidLoad 37 | { 38 | [super viewDidLoad]; 39 | 40 | self.tableView.backgroundColor = kBackgroundColor; 41 | self.tableView.rowHeight = kRowHeight; 42 | } 43 | 44 | #pragma mark - Table View Methods 45 | 46 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 47 | { 48 | return kNumberOfRows; 49 | } 50 | 51 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 52 | { 53 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[self cellID]]; 54 | if (cell == nil) 55 | { 56 | cell = [[self.cellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[self cellID]]; 57 | } 58 | 59 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 60 | 61 | cell.circleColors = [self circleColorsForIndexPath:indexPath]; 62 | 63 | return cell; 64 | } 65 | 66 | #pragma mark - 67 | 68 | /** 69 | * @discussion using consistent colors between calls so that the view can be cached and reused (as opposed to using just random colors) 70 | */ 71 | - (NSArray *)circleColorsForIndexPath:(NSIndexPath *)indexPath 72 | { 73 | const NSUInteger row = indexPath.row; 74 | 75 | NSMutableArray *circleColors = [NSMutableArray array]; 76 | for (NSUInteger i = 0; i < kViewsPerRow; i++) 77 | { 78 | const CGFloat componentValue = (row + i) / (CGFloat)kNumberOfRows; 79 | 80 | UIColor *color = [UIColor colorWithRed:componentValue + i / 10.f 81 | green:componentValue + i / 20.0f 82 | blue:componentValue + i / 30.0f 83 | alpha:1.0f]; 84 | 85 | [circleColors addObject:color]; 86 | } 87 | 88 | return circleColors; 89 | } 90 | 91 | - (NSString *)cellID 92 | { 93 | return NSStringFromClass(self.cellClass); 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCustomDrawnView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSCustomDrawnView.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MSCustomDrawnView : UIView 12 | 13 | @property (atomic, strong) UIColor *circleColor; 14 | 15 | - (id)initWithFrame:(CGRect)frame 16 | circleColor:(UIColor *)circleColor; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCustomDrawnView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSCustomDrawnView.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCustomDrawnView.h" 10 | 11 | #define kStrokeColor [UIColor whiteColor] 12 | #define kStrokeWidth 2.0f 13 | 14 | #define kShadowHeight 1.0f 15 | 16 | @implementation MSCustomDrawnView 17 | 18 | @synthesize circleColor = _circleColor; 19 | 20 | - (id)initWithFrame:(CGRect)frame 21 | { 22 | return [self initWithFrame:frame 23 | circleColor:nil]; 24 | } 25 | 26 | - (id)initWithFrame:(CGRect)frame 27 | circleColor:(UIColor *)circleColor 28 | { 29 | NSParameterAssert(circleColor); 30 | 31 | if ((self = [super initWithFrame:frame])) 32 | { 33 | self.circleColor = circleColor; 34 | 35 | self.contentMode = UIViewContentModeRedraw; 36 | } 37 | 38 | return self; 39 | } 40 | 41 | - (void)setCircleColor:(UIColor *)circleColor 42 | { 43 | @synchronized(self) 44 | { 45 | if (circleColor != _circleColor) 46 | { 47 | _circleColor = circleColor; 48 | 49 | [self setNeedsDisplay]; 50 | } 51 | } 52 | } 53 | 54 | - (void)setFrame:(CGRect)frame 55 | { 56 | frame.size.width = frame.size.height; 57 | 58 | [super setFrame:frame]; 59 | } 60 | 61 | - (UIColor *)circleColor 62 | { 63 | @synchronized(self) 64 | { 65 | return _circleColor; 66 | } 67 | } 68 | 69 | - (void)drawRect:(CGRect)rect 70 | { 71 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 72 | 73 | UIColor *circleColor = self.circleColor; 74 | 75 | CGRect circleFrame = CGRectIntegral(CGRectInset(rect, kStrokeWidth + kShadowHeight, kStrokeWidth + kShadowHeight)); 76 | CGPathRef circlePath = CGPathCreateWithEllipseInRect(circleFrame, NULL); 77 | 78 | CGContextAddPath(ctx, circlePath); 79 | 80 | CGContextSaveGState(ctx); 81 | { 82 | CGContextClip(ctx); 83 | CGContextAddPath(ctx, circlePath); 84 | 85 | // Background 86 | CGContextSaveGState(ctx); 87 | { 88 | CGContextSetFillColorWithColor(ctx, circleColor.CGColor); 89 | CGContextFillPath(ctx); 90 | } 91 | CGContextRestoreGState(ctx); 92 | 93 | CGContextSaveGState(ctx); 94 | { 95 | // Draw icon 96 | UIImage *mindsnacksLogoImage = [UIImage imageNamed:@"mindsnacks_logo"]; 97 | 98 | CGSize imageSize = mindsnacksLogoImage.size; 99 | 100 | CGFloat imageRectWidth = (circleFrame.size.width * 0.9f); 101 | 102 | CGRect imageFrame = CGRectZero; 103 | imageFrame.size.width = imageRectWidth; 104 | imageFrame.size.height = (imageRectWidth / imageSize.width) * imageSize.height; 105 | imageFrame.origin.x = circleFrame.origin.x + (circleFrame.size.width - imageFrame.size.width) / 2.0f; 106 | imageFrame.origin.y = circleFrame.origin.y + (circleFrame.size.height - imageFrame.size.height) / 2.0f; 107 | 108 | [mindsnacksLogoImage drawInRect:imageFrame]; 109 | } 110 | CGContextRestoreGState(ctx); 111 | } 112 | CGContextRestoreGState(ctx); 113 | 114 | // Stroke + shadow 115 | CGContextSaveGState(ctx); 116 | { 117 | CGContextAddPath(ctx, circlePath); 118 | 119 | CGContextSetStrokeColorWithColor(ctx, kStrokeColor.CGColor); 120 | CGContextSetShadowWithColor(ctx, CGSizeMake(0, kShadowHeight), 0.0f, [UIColor colorWithWhite:0.0 alpha:0.6f].CGColor); 121 | 122 | CGContextSetLineWidth(ctx, kStrokeWidth); 123 | 124 | CGContextStrokePath(ctx); 125 | } 126 | 127 | CGPathRelease(circlePath); 128 | } 129 | 130 | @end 131 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSCustomDrawnViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSCustomDrawnViewCell.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define kViewsPerRow 3 12 | #define kBackgroundColor [UIColor lightGrayColor] 13 | 14 | /** 15 | * @see MSTraditionalDrawingCell 16 | * @see MSAsyncDrawingCell 17 | */ 18 | @protocol MSCustomDrawnViewCell 19 | 20 | @property (nonatomic, copy) NSArray *circleColors; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSTraditionalDrawingCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSTraditionalDrawingCell.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCustomDrawnViewCell.h" 10 | 11 | @interface MSTraditionalDrawingCell : UITableViewCell 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSTraditionalDrawingCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSTraditionalDrawingCell.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSTraditionalDrawingCell.h" 10 | 11 | #import "MSCustomDrawnView.h" 12 | 13 | @interface MSTraditionalDrawingCell () 14 | 15 | @end 16 | 17 | @implementation MSTraditionalDrawingCell 18 | 19 | @synthesize circleColors = _circleColors; 20 | 21 | - (void)setCircleColors:(NSArray *)circleColors 22 | { 23 | if (circleColors != _circleColors) 24 | { 25 | _circleColors = circleColors; 26 | 27 | [self setNeedsDisplay]; 28 | } 29 | } 30 | 31 | #pragma mark - Drawing 32 | 33 | - (UIColor *)backgroundColor 34 | { 35 | return kBackgroundColor; 36 | } 37 | 38 | - (void)drawRect:(CGRect)rect 39 | { 40 | CGContextRef ctx = UIGraphicsGetCurrentContext(); 41 | 42 | CGContextSaveGState(ctx); 43 | { 44 | CGContextSetFillColorWithColor(ctx, self.backgroundColor.CGColor); 45 | CGContextFillRect(ctx, rect); 46 | } 47 | CGContextRestoreGState(ctx); 48 | 49 | const CGFloat widthPerView = rect.size.width / (CGFloat)kViewsPerRow; 50 | const CGFloat viewHeight = rect.size.height; 51 | 52 | CGFloat currentX = 0.0f; 53 | 54 | for (UIColor *circleColor in self.circleColors) 55 | { 56 | CGRect frame = CGRectIntegral(CGRectMake(currentX, 57 | 0.0f, 58 | widthPerView, 59 | viewHeight)); 60 | MSCustomDrawnView *view = [[MSCustomDrawnView alloc] initWithFrame:frame 61 | circleColor:circleColor]; 62 | [view drawRect:frame]; 63 | 64 | currentX += widthPerView; 65 | } 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSTraditionalDrawingTVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSTraditionalDrawingVC.h 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSCommonTVC.h" 10 | 11 | @interface MSTraditionalDrawingTVC : MSCommonTVC 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/MSTraditionalDrawingTVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSTraditionalDrawingVC.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import "MSTraditionalDrawingTVC.h" 10 | 11 | #import "MSTraditionalDrawingCell.h" 12 | 13 | @interface MSTraditionalDrawingTVC () 14 | 15 | @end 16 | 17 | @implementation MSTraditionalDrawingTVC 18 | 19 | - (id)init 20 | { 21 | if ((self = [super initWithCellClass:[MSTraditionalDrawingCell class]])) 22 | { 23 | self.title = @"No MSCachedAsyncViewDrawing"; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MSCachedAsyncViewDrawing-SampleProject 4 | // 5 | // Created by Javier Soto on 11/10/12. 6 | // Copyright (c) 2012 MindSnacks. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "MSAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([MSAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/mindsnacks_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindsnacks/MSCachedAsyncViewDrawing/75a52ad3a7d749835f5c33a7e1106d1fa0a4039a/MSCachedAsyncViewDrawing-SampleProject/mindsnacks_logo.png -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing-SampleProject/mindsnacks_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mindsnacks/MSCachedAsyncViewDrawing/75a52ad3a7d749835f5c33a7e1106d1fa0a4039a/MSCachedAsyncViewDrawing-SampleProject/mindsnacks_logo@2x.png -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSCachedAsyncViewDrawing.h 3 | // MindSnacks 4 | // 5 | // Created by Javier Soto on 11/8/12. 6 | // 7 | // 8 | 9 | #import 10 | 11 | typedef void (^MSCachedAsyncViewDrawingDrawBlock)(CGRect frame); 12 | typedef void (^MSCachedAsyncViewDrawingCompletionBlock)(UIImage *drawnImage); 13 | 14 | @protocol MSCachedAsyncViewDrawingCache; 15 | 16 | @interface MSCachedAsyncViewDrawing : NSObject 17 | 18 | /** 19 | * You can use the shared instance to have a shared cache. 20 | * Uses in-memory caching by default. 21 | * @note It's perfectly valid to create separate instances of `MSCachedAsyncViewDrawing`, they will just have independent caches. 22 | */ 23 | + (MSCachedAsyncViewDrawing *)sharedInstance; 24 | 25 | /** 26 | * Initialize instance using in-memory caching. 27 | */ 28 | - (instancetype)init; 29 | 30 | /** 31 | * Designated initializer. 32 | * @param cache: must be thread safe. 33 | */ 34 | - (instancetype)initWithCache:(id)cache; 35 | 36 | /** 37 | * This method will call `drawBlock` _on a background thread_ passing a `CGRect` that you can pass to a `drawRect:` method 38 | * of a view or a layer. 39 | * Once finished, it'll call the completion block on the main thread with the drawn `UIImage` object. 40 | * `MSCachedAsyncViewDrawing` objects keep an internal cache so multiple calls to this method with the same `cacheKey` 41 | * will result in the immediate call of `completionBlock`. 42 | * @param cacheKey make sure you create a string with the paremeters of the view. Two views configured 43 | * differently (say, different text or font color) should have different cache keys to avoid collisions) 44 | * @param backgroundColor if you want a transparent image, just pass `[UIColor clearColor]`. 45 | * To generate an opaque image, pass a color with alpha = 1. 46 | * @param drawBlock this method is called from a background thread, so you must pay special attention to the thread safety of 47 | * anything you do in it. It's safe to use UIKit methods like `-[UIImage drawInRect:]` or `-[NSString drawInRect:]`. 48 | */ 49 | - (void)drawViewAsyncWithCacheKey:(NSString *)cacheKey 50 | size:(CGSize)imageSize 51 | backgroundColor:(UIColor *)backgroundColor 52 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock 53 | completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock; 54 | 55 | /** 56 | * This is the synchronous version of the other method. 57 | * It waits until the image is loaded and returns it instead of calling a completion block. 58 | * @param drawBlock is called on the caller thread. 59 | */ 60 | - (UIImage *)drawViewSyncWithCacheKey:(NSString *)cacheKey 61 | size:(CGSize)imageSize 62 | backgroundColor:(UIColor *)backgroundColor 63 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock; 64 | 65 | 66 | @end 67 | 68 | @protocol MSCachedAsyncViewDrawingCache 69 | 70 | /** 71 | * @return `nil` if the image for `key` is not available. 72 | */ 73 | - (UIImage *)imageForKey:(NSString *)key; 74 | - (void)setImage:(UIImage *)image forKey:(NSString *)key; 75 | 76 | @end -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing.m: -------------------------------------------------------------------------------- 1 | // 2 | // MSCachedAsyncViewDrawing.m 3 | // MindSnacks 4 | // 5 | // Created by Javier Soto on 11/8/12. 6 | // 7 | // 8 | 9 | #import "MSCachedAsyncViewDrawing.h" 10 | 11 | #if !__has_feature(objc_arc) 12 | #error MSCachedAsyncViewDrawing is ARC only. Either turn on ARC for the project or use -fobjc-arc flag 13 | #endif 14 | 15 | #define MSTreatQueuesAsObjects OS_OBJECT_USE_OBJC 16 | 17 | #if MSTreatQueuesAsObjects 18 | #define MS_dispatch_queue_t_property_qualifier strong 19 | #else 20 | #define MS_dispatch_queue_t_property_qualifier assign 21 | #endif 22 | 23 | @interface _MSCachedAsyncViewDrawingMemoryImageCache : NSObject 24 | { 25 | NSCache *_cache; 26 | } 27 | 28 | @end 29 | 30 | @interface MSCachedAsyncViewDrawing () 31 | 32 | @property (nonatomic) id cache; 33 | 34 | @property (nonatomic, MS_dispatch_queue_t_property_qualifier) dispatch_queue_t dispatchQueue; 35 | 36 | @end 37 | 38 | @implementation MSCachedAsyncViewDrawing 39 | 40 | + (MSCachedAsyncViewDrawing *)sharedInstance 41 | { 42 | static MSCachedAsyncViewDrawing *sharedInstance = nil; 43 | 44 | static dispatch_once_t onceToken; 45 | dispatch_once(&onceToken, ^{ 46 | sharedInstance = [[self alloc] init]; 47 | }); 48 | 49 | return sharedInstance; 50 | } 51 | 52 | - (instancetype)init 53 | { 54 | return [self initWithCache:[_MSCachedAsyncViewDrawingMemoryImageCache new]]; 55 | } 56 | 57 | - (instancetype)initWithCache:(id)cache 58 | { 59 | NSParameterAssert(cache); 60 | 61 | if ((self = [super init])) 62 | { 63 | _cache = cache; 64 | _dispatchQueue = dispatch_queue_create("com.mindsnacks.view_drawing.queue", DISPATCH_QUEUE_CONCURRENT); 65 | } 66 | 67 | return self; 68 | } 69 | 70 | - (void)dealloc 71 | { 72 | #if !MSTreatQueuesAsObjects 73 | dispatch_release(_dispatchQueue); 74 | #endif 75 | } 76 | 77 | #pragma mark - Private 78 | 79 | - (void)drawViewWithCacheKey:(NSString *)cacheKey 80 | size:(CGSize)imageSize 81 | backgroundColor:(UIColor *)backgroundColor 82 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock 83 | completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock 84 | waitUntilDone:(BOOL)waitUntilDone 85 | { 86 | UIImage *cachedImage = [self.cache imageForKey:cacheKey]; 87 | 88 | if (cachedImage) 89 | { 90 | completionBlock(cachedImage); 91 | return; 92 | } 93 | 94 | drawBlock = [drawBlock copy]; 95 | completionBlock = [completionBlock copy]; 96 | 97 | dispatch_block_t loadImageBlock = ^{ 98 | const BOOL opaque = [self colorIsOpaque:backgroundColor]; 99 | 100 | UIImage *resultImage = nil; 101 | 102 | UIGraphicsBeginImageContextWithOptions(imageSize, opaque, 0); 103 | { 104 | CGContextRef context = UIGraphicsGetCurrentContext(); 105 | 106 | CGRect rectToDraw = (CGRect){.origin = CGPointZero, .size = imageSize}; 107 | 108 | const BOOL shouldDrawBackgroundColor = ![backgroundColor isEqual:[UIColor clearColor]]; 109 | 110 | if (shouldDrawBackgroundColor) 111 | { 112 | CGContextSaveGState(context); 113 | { 114 | CGContextSetFillColorWithColor(context, backgroundColor.CGColor); 115 | CGContextFillRect(context, rectToDraw); 116 | } 117 | CGContextRestoreGState(context); 118 | } 119 | 120 | drawBlock(rectToDraw); 121 | 122 | resultImage = UIGraphicsGetImageFromCurrentImageContext(); 123 | } 124 | UIGraphicsEndImageContext(); 125 | 126 | [self.cache setImage:resultImage forKey:cacheKey]; 127 | 128 | if (waitUntilDone) 129 | { 130 | completionBlock(resultImage); 131 | } 132 | else 133 | { 134 | dispatch_async(dispatch_get_main_queue(), ^{ 135 | completionBlock(resultImage); 136 | }); 137 | } 138 | }; 139 | 140 | if (waitUntilDone) 141 | { 142 | loadImageBlock(); 143 | } 144 | else 145 | { 146 | dispatch_async(self.dispatchQueue, loadImageBlock); 147 | } 148 | } 149 | 150 | #pragma mark - Public 151 | 152 | - (void)drawViewAsyncWithCacheKey:(NSString *)cacheKey 153 | size:(CGSize)imageSize 154 | backgroundColor:(UIColor *)backgroundColor 155 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock 156 | completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock 157 | { 158 | [self drawViewWithCacheKey:cacheKey 159 | size:imageSize 160 | backgroundColor:backgroundColor 161 | drawBlock:drawBlock 162 | completionBlock:completionBlock 163 | waitUntilDone:NO]; 164 | } 165 | 166 | - (UIImage *)drawViewSyncWithCacheKey:(NSString *)cacheKey 167 | size:(CGSize)imageSize 168 | backgroundColor:(UIColor *)backgroundColor 169 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock 170 | { 171 | __block UIImage *image = nil; 172 | 173 | [self drawViewWithCacheKey:cacheKey 174 | size:imageSize 175 | backgroundColor:backgroundColor 176 | drawBlock:drawBlock 177 | completionBlock:^(UIImage *drawnImage) { 178 | image = drawnImage; 179 | } 180 | waitUntilDone:YES]; 181 | 182 | return image; 183 | } 184 | 185 | #pragma mark - Aux 186 | 187 | - (BOOL)colorIsOpaque:(UIColor *)color 188 | { 189 | CGFloat alpha = -1.0f; 190 | [color getRed:NULL green:NULL blue:NULL alpha:&alpha]; 191 | 192 | BOOL wrongColorSpace = (alpha == -1.0f); 193 | if (wrongColorSpace) 194 | { 195 | [color getWhite:NULL alpha:&alpha]; 196 | } 197 | 198 | return (alpha == 1.0f); 199 | } 200 | 201 | @end 202 | 203 | @implementation _MSCachedAsyncViewDrawingMemoryImageCache 204 | 205 | - (id)init 206 | { 207 | if ((self = [super init])) 208 | { 209 | _cache = [NSCache new]; 210 | _cache.name = @"com.mindsnacks.view_drawing.cache"; 211 | } 212 | 213 | return self; 214 | } 215 | 216 | - (UIImage *)imageForKey:(NSString *)key 217 | { 218 | return [_cache objectForKey:key]; 219 | } 220 | 221 | - (void)setImage:(UIImage *)image forKey:(NSString *)key 222 | { 223 | [_cache setObject:image forKey:key]; 224 | } 225 | 226 | @end 227 | -------------------------------------------------------------------------------- /MSCachedAsyncViewDrawing.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "MSCachedAsyncViewDrawing" 3 | s.version = "1.0.1" 4 | s.summary = "Helper class that allows you to draw views (a)synchronously to a UIImage with caching for great performance." 5 | s.homepage = "https://github.com/mindsnacks/MSCachedAsyncViewDrawing" 6 | 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.author = { "Javier Soto" => "ios@javisoto.es" } 9 | 10 | s.source = { :git => "https://github.com/mindsnacks/MSCachedAsyncViewDrawing.git", :tag => s.version.to_s } 11 | s.platform = :ios, '5.0' 12 | s.source_files = 'MSCachedAsyncViewDrawing.{h,m}' 13 | s.requires_arc = true 14 | end 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MSCachedAsyncViewDrawing 2 | 3 | Helper class that allows you to draw views (a)synchronously to a UIImage with caching for great performance. 4 | 5 | ## Description 6 | 7 | (This assumes you know a bit about CoreGraphics and how some things like blending work. If not, go read [this post](https://blog.twitter.com/2012/simple-strategies-smooth-animation-iphone) in the Twitter Engineering Blog). 8 | 9 | So you have a UITableView in your application that scrolls slow. You decide to implement the cell drawing entirely in CoreGraphics implementing ```-[UIView drawRect:]``` in your cell. This is perfect, until you have to draw images. ```CGContextDrawImage``` is **really slow** compared to using ```UIImageView```. 10 | 11 | For this reason, many times you'll find yourself preferring to use ```UIImageView``` even if some compositing has to happen on the cell, because rendering images with it is **FAST** due to the crazy optimizations that it implements internally. 12 | 13 | But sometimes you do have to use ```CGContextDrawImage```, because you have to do something more complex like masking, clipping, etc. Wouldn't it be great if you could still do that, but pass the result to a ```UIImageView``` easily, so that you get the benefit from both worlds? That's what ```MSCachedAsyncViewDrawing``` does. 14 | 15 | ## When to use MSCachedAsyncViewDrawing 16 | Needless to say you shouldn't just go ahead and apply this to all of the UIViews in your app. There's a drawback in this approach, as you're incurring in higher memory usage by storing the result of the all the drawing operations. 17 | **The only way to know if using `MSCachedAsyncViewDrawing` improves or not the performance in your particular case, is to try it out and compare.** 18 | As a general rule on when *it makes sense* to use it would be when -`drawRect:` is becoming a bottleneck, specially if it's using `CGContextDrawImage` inside. This can happen when you have many complex views in the cells of a `UITableView`. 19 | 20 | ## Sample Project 21 | 22 | The sample project contains two view controllers that contain a table view in which every row has 3 views that implement `-drawInRect:`. One of them uses ```MSCachedAsyncViewDrawing``` and the other one doesn't. This is an example on how to use this class and its performance benefit. Install the sample app on your iOS device and compare. 23 | It's also a typical use case for this class, since there are many views on screen at the same time, and they all have to render a `UIImage`, this becomes a bottleneck. `MSCachedAsyncViewDrawing` makes this asynchronous, hence not blocking the main thread and getting perfect scrolling performance, and it also prevents the views from rendering more than once. 24 | 25 | ## How to use it 26 | 27 | - Install using [cocoapods](http://cocoapods.org/) by adding `pod 'MSCachedAsyncViewDrawing', '~> 1.0.1'` to your Podfile. 28 | - Or add the `MSCachedAsyncViewDrawing.(h+m)` files to your project. 29 | 30 | This is the main method in ```MSCachedAsyncViewDrawing```: 31 | 32 | ```objc 33 | - (void)drawViewAsyncWithCacheKey:(NSString *)cacheKey 34 | size:(CGSize)imageSize 35 | backgroundColor:(UIColor *)backgroundColor 36 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock 37 | completionBlock:(MSCachedAsyncViewDrawingCompletionBlock)completionBlock; 38 | ``` 39 | 40 | The block types are declared like this: 41 | 42 | ```objc 43 | typedef void (^MSCachedAsyncViewDrawingDrawBlock)(CGRect frame); 44 | typedef void (^MSCachedAsyncViewDrawingCompletionBlock)(UIImage *drawnImage); 45 | ``` 46 | 47 | ```MSCachedAsyncViewDrawing``` is going to take the `drawBlock` and call it on a background thread, passing it the `CGRect` that you can pass to a `-drawRect:` method of a view. When it's done, it's going to cache the `UIImage` object with the specified `cacheKey` and call your `completionBlock` with it. 48 | 49 | A subsequent call with the same `cacheKey` will result in the immediate call of the `completionBlock` without calling the drawBlock because it'll grab the rendered image from the cache. 50 | 51 | The cache is implemented using `NSCache`, so you don't have to worry about putting caching too many images, because iOS is going to take care of evicting obejcts as the available memory goes low. 52 | 53 | If you prefer to block the UI while the rendering is happening, because you want to make sure that the image view is not empty at any point, you can use this other method, which immediately returns the ```UIImage``` object: 54 | 55 | ```objc 56 | - (UIImage *)drawViewSyncWithCacheKey:(NSString *)cacheKey 57 | size:(CGSize)imageSize 58 | backgroundColor:(UIColor *)backgroundColor 59 | drawBlock:(MSCachedAsyncViewDrawingDrawBlock)drawBlock; 60 | ``` 61 | 62 | 63 | ## Compatibility 64 | - ```MSCachedAsyncViewDrawing``` is compatible with iOS5.0+. 65 | - ```MSCachedAsyncViewDrawing``` uses ARC. To use in a non-ARC project, mark ```MSCachedAsyncViewDrawing.m``` with the linker flag ```-fobjc-arc.``` 66 | 67 | ## License 68 | `MSCachedAsyncViewDrawing` is available under the MIT license. See the LICENSE file for more info. 69 | --------------------------------------------------------------------------------