├── .gitignore ├── LICENSE ├── LocalCache.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── LocalCache ├── AppDelegate.h ├── AppDelegate.m ├── CustomURLCache.h ├── CustomURLCache.m ├── Default-568h@2x.png ├── Default.png ├── Default@2x.png ├── LocalCache-Info.plist ├── LocalCache-Prefix.pch ├── MBProgressHUD.h ├── MBProgressHUD.m ├── Reachability.h ├── Reachability.m ├── Util.h ├── Util.m ├── ViewController.h ├── ViewController.m ├── en.lproj │ ├── InfoPlist.strings │ └── ViewController.xib └── main.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | 55 | #Code Injection 56 | # 57 | # After new code Injection tools there's a generated folder /iOSInjectionProject 58 | # https://github.com/johnno1962/injectionforxcode 59 | 60 | iOSInjectionProject/ 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 lzhlewis2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LocalCache.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 34967B371C99603E006D491D /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 34967B361C99603E006D491D /* Reachability.m */; }; 11 | 34967B391C99609C006D491D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34967B381C99609C006D491D /* SystemConfiguration.framework */; }; 12 | 8363771D16C1E2AD005D9F00 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8363771C16C1E2AD005D9F00 /* UIKit.framework */; }; 13 | 8363771F16C1E2AD005D9F00 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8363771E16C1E2AD005D9F00 /* Foundation.framework */; }; 14 | 8363772116C1E2AD005D9F00 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8363772016C1E2AD005D9F00 /* CoreGraphics.framework */; }; 15 | 8363772716C1E2AD005D9F00 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8363772516C1E2AD005D9F00 /* InfoPlist.strings */; }; 16 | 8363772916C1E2AD005D9F00 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8363772816C1E2AD005D9F00 /* main.m */; }; 17 | 8363772D16C1E2AD005D9F00 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8363772C16C1E2AD005D9F00 /* AppDelegate.m */; }; 18 | 8363772F16C1E2AD005D9F00 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 8363772E16C1E2AD005D9F00 /* Default.png */; }; 19 | 8363773116C1E2AD005D9F00 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8363773016C1E2AD005D9F00 /* Default@2x.png */; }; 20 | 8363773316C1E2AE005D9F00 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8363773216C1E2AE005D9F00 /* Default-568h@2x.png */; }; 21 | 8363773616C1E2AE005D9F00 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8363773516C1E2AE005D9F00 /* ViewController.m */; }; 22 | 8363773916C1E2AE005D9F00 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8363773716C1E2AE005D9F00 /* ViewController.xib */; }; 23 | 8363775016C1E356005D9F00 /* Util.m in Sources */ = {isa = PBXBuildFile; fileRef = 8363774F16C1E356005D9F00 /* Util.m */; }; 24 | 8363775416C1F3A1005D9F00 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 8363775316C1F3A1005D9F00 /* MBProgressHUD.m */; }; 25 | 83D9BF1C16CA87AA00469371 /* CustomURLCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 83D9BF1B16CA87AA00469371 /* CustomURLCache.m */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 34967B351C99603E006D491D /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = ""; }; 30 | 34967B361C99603E006D491D /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = ""; }; 31 | 34967B381C99609C006D491D /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; 32 | 8363771816C1E2AD005D9F00 /* LocalCache.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LocalCache.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 8363771C16C1E2AD005D9F00 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 34 | 8363771E16C1E2AD005D9F00 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 35 | 8363772016C1E2AD005D9F00 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 36 | 8363772416C1E2AD005D9F00 /* LocalCache-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LocalCache-Info.plist"; sourceTree = ""; }; 37 | 8363772616C1E2AD005D9F00 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 38 | 8363772816C1E2AD005D9F00 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 39 | 8363772A16C1E2AD005D9F00 /* LocalCache-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LocalCache-Prefix.pch"; sourceTree = ""; }; 40 | 8363772B16C1E2AD005D9F00 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 41 | 8363772C16C1E2AD005D9F00 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 42 | 8363772E16C1E2AD005D9F00 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 43 | 8363773016C1E2AD005D9F00 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 44 | 8363773216C1E2AE005D9F00 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 45 | 8363773416C1E2AE005D9F00 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | 8363773516C1E2AE005D9F00 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | 8363773816C1E2AE005D9F00 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController.xib; sourceTree = ""; }; 48 | 8363774E16C1E356005D9F00 /* Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util.h; sourceTree = ""; }; 49 | 8363774F16C1E356005D9F00 /* Util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Util.m; sourceTree = ""; }; 50 | 8363775216C1F3A1005D9F00 /* MBProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBProgressHUD.h; sourceTree = ""; }; 51 | 8363775316C1F3A1005D9F00 /* MBProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = ""; }; 52 | 83D9BF1A16CA87AA00469371 /* CustomURLCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomURLCache.h; sourceTree = ""; }; 53 | 83D9BF1B16CA87AA00469371 /* CustomURLCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomURLCache.m; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | 8363771516C1E2AD005D9F00 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | 34967B391C99609C006D491D /* SystemConfiguration.framework in Frameworks */, 62 | 8363771D16C1E2AD005D9F00 /* UIKit.framework in Frameworks */, 63 | 8363771F16C1E2AD005D9F00 /* Foundation.framework in Frameworks */, 64 | 8363772116C1E2AD005D9F00 /* CoreGraphics.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 8363770D16C1E2AD005D9F00 = { 72 | isa = PBXGroup; 73 | children = ( 74 | 8363772216C1E2AD005D9F00 /* LocalCache */, 75 | 8363771B16C1E2AD005D9F00 /* Frameworks */, 76 | 8363771916C1E2AD005D9F00 /* Products */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | 8363771916C1E2AD005D9F00 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 8363771816C1E2AD005D9F00 /* LocalCache.app */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | 8363771B16C1E2AD005D9F00 /* Frameworks */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 34967B381C99609C006D491D /* SystemConfiguration.framework */, 92 | 8363771C16C1E2AD005D9F00 /* UIKit.framework */, 93 | 8363771E16C1E2AD005D9F00 /* Foundation.framework */, 94 | 8363772016C1E2AD005D9F00 /* CoreGraphics.framework */, 95 | ); 96 | name = Frameworks; 97 | sourceTree = ""; 98 | }; 99 | 8363772216C1E2AD005D9F00 /* LocalCache */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 8363774216C1E2BC005D9F00 /* extensions */, 103 | 8363774116C1E2BA005D9F00 /* models */, 104 | 8363774016C1E2B7005D9F00 /* controllers */, 105 | 8363773F16C1E2B5005D9F00 /* views */, 106 | 8363772316C1E2AD005D9F00 /* Supporting Files */, 107 | ); 108 | path = LocalCache; 109 | sourceTree = ""; 110 | }; 111 | 8363772316C1E2AD005D9F00 /* Supporting Files */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 8363772416C1E2AD005D9F00 /* LocalCache-Info.plist */, 115 | 8363772516C1E2AD005D9F00 /* InfoPlist.strings */, 116 | 8363772816C1E2AD005D9F00 /* main.m */, 117 | 8363772A16C1E2AD005D9F00 /* LocalCache-Prefix.pch */, 118 | 8363772E16C1E2AD005D9F00 /* Default.png */, 119 | 8363773016C1E2AD005D9F00 /* Default@2x.png */, 120 | 8363773216C1E2AE005D9F00 /* Default-568h@2x.png */, 121 | ); 122 | name = "Supporting Files"; 123 | sourceTree = ""; 124 | }; 125 | 8363773F16C1E2B5005D9F00 /* views */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 8363773716C1E2AE005D9F00 /* ViewController.xib */, 129 | ); 130 | name = views; 131 | sourceTree = ""; 132 | }; 133 | 8363774016C1E2B7005D9F00 /* controllers */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 8363773416C1E2AE005D9F00 /* ViewController.h */, 137 | 8363773516C1E2AE005D9F00 /* ViewController.m */, 138 | ); 139 | name = controllers; 140 | sourceTree = ""; 141 | }; 142 | 8363774116C1E2BA005D9F00 /* models */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 8363772B16C1E2AD005D9F00 /* AppDelegate.h */, 146 | 8363772C16C1E2AD005D9F00 /* AppDelegate.m */, 147 | ); 148 | name = models; 149 | sourceTree = ""; 150 | }; 151 | 8363774216C1E2BC005D9F00 /* extensions */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 8363775116C1F38F005D9F00 /* HUD */, 155 | 8363774D16C1E340005D9F00 /* other */, 156 | 8363774316C1E2F1005D9F00 /* cached */, 157 | ); 158 | name = extensions; 159 | sourceTree = ""; 160 | }; 161 | 8363774316C1E2F1005D9F00 /* cached */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 83D9BF1A16CA87AA00469371 /* CustomURLCache.h */, 165 | 83D9BF1B16CA87AA00469371 /* CustomURLCache.m */, 166 | ); 167 | name = cached; 168 | sourceTree = ""; 169 | }; 170 | 8363774D16C1E340005D9F00 /* other */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 34967B351C99603E006D491D /* Reachability.h */, 174 | 34967B361C99603E006D491D /* Reachability.m */, 175 | 8363774E16C1E356005D9F00 /* Util.h */, 176 | 8363774F16C1E356005D9F00 /* Util.m */, 177 | ); 178 | name = other; 179 | sourceTree = ""; 180 | }; 181 | 8363775116C1F38F005D9F00 /* HUD */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 8363775216C1F3A1005D9F00 /* MBProgressHUD.h */, 185 | 8363775316C1F3A1005D9F00 /* MBProgressHUD.m */, 186 | ); 187 | name = HUD; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXGroup section */ 191 | 192 | /* Begin PBXNativeTarget section */ 193 | 8363771716C1E2AD005D9F00 /* LocalCache */ = { 194 | isa = PBXNativeTarget; 195 | buildConfigurationList = 8363773C16C1E2AE005D9F00 /* Build configuration list for PBXNativeTarget "LocalCache" */; 196 | buildPhases = ( 197 | 8363771416C1E2AD005D9F00 /* Sources */, 198 | 8363771516C1E2AD005D9F00 /* Frameworks */, 199 | 8363771616C1E2AD005D9F00 /* Resources */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | ); 205 | name = LocalCache; 206 | productName = LocalCache; 207 | productReference = 8363771816C1E2AD005D9F00 /* LocalCache.app */; 208 | productType = "com.apple.product-type.application"; 209 | }; 210 | /* End PBXNativeTarget section */ 211 | 212 | /* Begin PBXProject section */ 213 | 8363770F16C1E2AD005D9F00 /* Project object */ = { 214 | isa = PBXProject; 215 | attributes = { 216 | LastUpgradeCheck = 0450; 217 | ORGANIZATIONNAME = adways; 218 | }; 219 | buildConfigurationList = 8363771216C1E2AD005D9F00 /* Build configuration list for PBXProject "LocalCache" */; 220 | compatibilityVersion = "Xcode 3.2"; 221 | developmentRegion = English; 222 | hasScannedForEncodings = 0; 223 | knownRegions = ( 224 | en, 225 | ); 226 | mainGroup = 8363770D16C1E2AD005D9F00; 227 | productRefGroup = 8363771916C1E2AD005D9F00 /* Products */; 228 | projectDirPath = ""; 229 | projectRoot = ""; 230 | targets = ( 231 | 8363771716C1E2AD005D9F00 /* LocalCache */, 232 | ); 233 | }; 234 | /* End PBXProject section */ 235 | 236 | /* Begin PBXResourcesBuildPhase section */ 237 | 8363771616C1E2AD005D9F00 /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | 8363772716C1E2AD005D9F00 /* InfoPlist.strings in Resources */, 242 | 8363772F16C1E2AD005D9F00 /* Default.png in Resources */, 243 | 8363773116C1E2AD005D9F00 /* Default@2x.png in Resources */, 244 | 8363773316C1E2AE005D9F00 /* Default-568h@2x.png in Resources */, 245 | 8363773916C1E2AE005D9F00 /* ViewController.xib in Resources */, 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | /* End PBXResourcesBuildPhase section */ 250 | 251 | /* Begin PBXSourcesBuildPhase section */ 252 | 8363771416C1E2AD005D9F00 /* Sources */ = { 253 | isa = PBXSourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 8363772916C1E2AD005D9F00 /* main.m in Sources */, 257 | 8363772D16C1E2AD005D9F00 /* AppDelegate.m in Sources */, 258 | 8363773616C1E2AE005D9F00 /* ViewController.m in Sources */, 259 | 34967B371C99603E006D491D /* Reachability.m in Sources */, 260 | 8363775016C1E356005D9F00 /* Util.m in Sources */, 261 | 8363775416C1F3A1005D9F00 /* MBProgressHUD.m in Sources */, 262 | 83D9BF1C16CA87AA00469371 /* CustomURLCache.m in Sources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXSourcesBuildPhase section */ 267 | 268 | /* Begin PBXVariantGroup section */ 269 | 8363772516C1E2AD005D9F00 /* InfoPlist.strings */ = { 270 | isa = PBXVariantGroup; 271 | children = ( 272 | 8363772616C1E2AD005D9F00 /* en */, 273 | ); 274 | name = InfoPlist.strings; 275 | sourceTree = ""; 276 | }; 277 | 8363773716C1E2AE005D9F00 /* ViewController.xib */ = { 278 | isa = PBXVariantGroup; 279 | children = ( 280 | 8363773816C1E2AE005D9F00 /* en */, 281 | ); 282 | name = ViewController.xib; 283 | sourceTree = ""; 284 | }; 285 | /* End PBXVariantGroup section */ 286 | 287 | /* Begin XCBuildConfiguration section */ 288 | 8363773A16C1E2AE005D9F00 /* Debug */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ALWAYS_SEARCH_USER_PATHS = NO; 292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 293 | CLANG_CXX_LIBRARY = "libc++"; 294 | CLANG_WARN_EMPTY_BODY = YES; 295 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 296 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 297 | COPY_PHASE_STRIP = NO; 298 | GCC_C_LANGUAGE_STANDARD = gnu99; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_OPTIMIZATION_LEVEL = 0; 301 | GCC_PREPROCESSOR_DEFINITIONS = ( 302 | "DEBUG=1", 303 | "$(inherited)", 304 | ); 305 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 308 | GCC_WARN_UNUSED_VARIABLE = YES; 309 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 310 | ONLY_ACTIVE_ARCH = YES; 311 | SDKROOT = iphoneos; 312 | }; 313 | name = Debug; 314 | }; 315 | 8363773B16C1E2AE005D9F00 /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 324 | COPY_PHASE_STRIP = YES; 325 | GCC_C_LANGUAGE_STANDARD = gnu99; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 330 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 331 | SDKROOT = iphoneos; 332 | VALIDATE_PRODUCT = YES; 333 | }; 334 | name = Release; 335 | }; 336 | 8363773D16C1E2AE005D9F00 /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 340 | GCC_PREFIX_HEADER = "LocalCache/LocalCache-Prefix.pch"; 341 | INFOPLIST_FILE = "LocalCache/LocalCache-Info.plist"; 342 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 343 | PRODUCT_NAME = "$(TARGET_NAME)"; 344 | WRAPPER_EXTENSION = app; 345 | }; 346 | name = Debug; 347 | }; 348 | 8363773E16C1E2AE005D9F00 /* Release */ = { 349 | isa = XCBuildConfiguration; 350 | buildSettings = { 351 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 352 | GCC_PREFIX_HEADER = "LocalCache/LocalCache-Prefix.pch"; 353 | INFOPLIST_FILE = "LocalCache/LocalCache-Info.plist"; 354 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 355 | PRODUCT_NAME = "$(TARGET_NAME)"; 356 | WRAPPER_EXTENSION = app; 357 | }; 358 | name = Release; 359 | }; 360 | /* End XCBuildConfiguration section */ 361 | 362 | /* Begin XCConfigurationList section */ 363 | 8363771216C1E2AD005D9F00 /* Build configuration list for PBXProject "LocalCache" */ = { 364 | isa = XCConfigurationList; 365 | buildConfigurations = ( 366 | 8363773A16C1E2AE005D9F00 /* Debug */, 367 | 8363773B16C1E2AE005D9F00 /* Release */, 368 | ); 369 | defaultConfigurationIsVisible = 0; 370 | defaultConfigurationName = Release; 371 | }; 372 | 8363773C16C1E2AE005D9F00 /* Build configuration list for PBXNativeTarget "LocalCache" */ = { 373 | isa = XCConfigurationList; 374 | buildConfigurations = ( 375 | 8363773D16C1E2AE005D9F00 /* Debug */, 376 | 8363773E16C1E2AE005D9F00 /* Release */, 377 | ); 378 | defaultConfigurationIsVisible = 0; 379 | defaultConfigurationName = Release; 380 | }; 381 | /* End XCConfigurationList section */ 382 | }; 383 | rootObject = 8363770F16C1E2AD005D9F00 /* Project object */; 384 | } 385 | -------------------------------------------------------------------------------- /LocalCache.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LocalCache/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ViewController; 12 | 13 | @interface AppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) ViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LocalCache/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | #import "ViewController.h" 12 | 13 | @implementation AppDelegate 14 | 15 | - (void)dealloc 16 | { 17 | [_window release]; 18 | [_viewController release]; 19 | [super dealloc]; 20 | } 21 | 22 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 23 | { 24 | self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; 25 | // Override point for customization after application launch. 26 | self.viewController = [[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; 27 | self.window.rootViewController = self.viewController; 28 | [self.window makeKeyAndVisible]; 29 | return YES; 30 | } 31 | 32 | - (void)applicationWillResignActive:(UIApplication *)application 33 | { 34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 35 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 36 | } 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application 39 | { 40 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 42 | } 43 | 44 | - (void)applicationWillEnterForeground:(UIApplication *)application 45 | { 46 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 47 | } 48 | 49 | - (void)applicationDidBecomeActive:(UIApplication *)application 50 | { 51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 52 | } 53 | 54 | - (void)applicationWillTerminate:(UIApplication *)application 55 | { 56 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /LocalCache/CustomURLCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // CustomURLCache.h 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-12. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Util.h" 11 | 12 | @interface CustomURLCache : NSURLCache 13 | 14 | @property(nonatomic, assign) NSInteger cacheTime; 15 | @property(nonatomic, retain) NSString *diskPath; 16 | @property(nonatomic, retain) NSMutableDictionary *responseDictionary; 17 | 18 | - (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /LocalCache/CustomURLCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // CustomURLCache.m 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-12. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import "CustomURLCache.h" 10 | #import "Reachability.h" 11 | @interface CustomURLCache(private) 12 | 13 | - (NSString *)cacheFolder; 14 | - (NSString *)cacheFilePath:(NSString *)file; 15 | - (NSString *)cacheRequestFileName:(NSString *)requestUrl; 16 | - (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl; 17 | - (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request; 18 | - (void)deleteCacheFolder; 19 | 20 | @end 21 | 22 | @implementation CustomURLCache 23 | 24 | @synthesize cacheTime = _cacheTime; 25 | @synthesize diskPath = _diskPath; 26 | @synthesize responseDictionary = _responseDictionary; 27 | 28 | - (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path cacheTime:(NSInteger)cacheTime { 29 | if (self = [self initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path]) { 30 | self.cacheTime = cacheTime; 31 | if (path) 32 | self.diskPath = path; 33 | else 34 | self.diskPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]; 35 | 36 | self.responseDictionary = [NSMutableDictionary dictionaryWithCapacity:0]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void)dealloc { 42 | [_diskPath release]; 43 | [_responseDictionary release]; 44 | [super dealloc]; 45 | } 46 | 47 | - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { 48 | if ([request.HTTPMethod compare:@"GET"] != NSOrderedSame) { 49 | return [super cachedResponseForRequest:request]; 50 | } 51 | 52 | return [self dataFromRequest:request]; 53 | } 54 | 55 | - (void)removeAllCachedResponses { 56 | [super removeAllCachedResponses]; 57 | 58 | [self deleteCacheFolder]; 59 | } 60 | 61 | - (void)removeCachedResponseForRequest:(NSURLRequest *)request { 62 | [super removeCachedResponseForRequest:request]; 63 | 64 | NSString *url = request.URL.absoluteString; 65 | NSString *fileName = [self cacheRequestFileName:url]; 66 | NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url]; 67 | NSString *filePath = [self cacheFilePath:fileName]; 68 | NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName]; 69 | NSFileManager *fileManager = [NSFileManager defaultManager]; 70 | [fileManager removeItemAtPath:filePath error:nil]; 71 | [fileManager removeItemAtPath:otherInfoPath error:nil]; 72 | } 73 | 74 | #pragma mark - custom url cache 75 | 76 | - (NSString *)cacheFolder { 77 | return @"URLCACHE"; 78 | } 79 | 80 | - (void)deleteCacheFolder { 81 | NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]]; 82 | NSFileManager *fileManager = [NSFileManager defaultManager]; 83 | [fileManager removeItemAtPath:path error:nil]; 84 | } 85 | 86 | - (NSString *)cacheFilePath:(NSString *)file { 87 | NSString *path = [NSString stringWithFormat:@"%@/%@", self.diskPath, [self cacheFolder]]; 88 | NSFileManager *fileManager = [NSFileManager defaultManager]; 89 | BOOL isDir; 90 | if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir) { 91 | 92 | } else { 93 | [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; 94 | } 95 | return [NSString stringWithFormat:@"%@/%@", path, file]; 96 | } 97 | 98 | - (NSString *)cacheRequestFileName:(NSString *)requestUrl { 99 | return [Util md5Hash:requestUrl]; 100 | } 101 | 102 | - (NSString *)cacheRequestOtherInfoFileName:(NSString *)requestUrl { 103 | return [Util md5Hash:[NSString stringWithFormat:@"%@-otherInfo", requestUrl]]; 104 | } 105 | 106 | - (NSCachedURLResponse *)dataFromRequest:(NSURLRequest *)request { 107 | NSString *url = request.URL.absoluteString; 108 | NSString *fileName = [self cacheRequestFileName:url]; 109 | NSString *otherInfoFileName = [self cacheRequestOtherInfoFileName:url]; 110 | NSString *filePath = [self cacheFilePath:fileName]; 111 | NSString *otherInfoPath = [self cacheFilePath:otherInfoFileName]; 112 | NSDate *date = [NSDate date]; 113 | 114 | NSFileManager *fileManager = [NSFileManager defaultManager]; 115 | if ([fileManager fileExistsAtPath:filePath]) { 116 | BOOL expire = false; 117 | NSDictionary *otherInfo = [NSDictionary dictionaryWithContentsOfFile:otherInfoPath]; 118 | 119 | if (self.cacheTime > 0) { 120 | NSInteger createTime = [[otherInfo objectForKey:@"time"] intValue]; 121 | if (createTime + self.cacheTime < [date timeIntervalSince1970]) { 122 | expire = true; 123 | } 124 | } 125 | 126 | if (expire == false) { 127 | NSLog(@"data from cache ..."); 128 | 129 | NSData *data = [NSData dataWithContentsOfFile:filePath]; 130 | NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL 131 | MIMEType:[otherInfo objectForKey:@"MIMEType"] 132 | expectedContentLength:data.length 133 | textEncodingName:[otherInfo objectForKey:@"textEncodingName"]]; 134 | NSCachedURLResponse *cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease]; 135 | [response release]; 136 | return cachedResponse; 137 | } else { 138 | NSLog(@"cache expire ... "); 139 | 140 | [fileManager removeItemAtPath:filePath error:nil]; 141 | [fileManager removeItemAtPath:otherInfoPath error:nil]; 142 | } 143 | } 144 | if (![Reachability networkAvailable]) { 145 | return nil; 146 | } 147 | 148 | //sendSynchronousRequest请求也要经过NSURLCache 149 | __block NSCachedURLResponse * cachedResponse = nil; 150 | id boolExsite = [self.responseDictionary objectForKey:url]; 151 | if (boolExsite == nil) { 152 | [self.responseDictionary setValue:[NSNumber numberWithBool:TRUE] forKey:url]; 153 | 154 | // NSURLResponse *response = nil; 155 | // NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 156 | 157 | [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) { 158 | if (response && data) { 159 | [self.responseDictionary removeObjectForKey:url]; 160 | } 161 | if (connectionError) { 162 | NSLog(@"error : %@", connectionError); 163 | NSLog(@"not cached: %@", request.URL.absoluteString); 164 | cachedResponse = nil; 165 | } 166 | 167 | NSLog(@"get request ... "); 168 | 169 | //save to cache 170 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%f", [date timeIntervalSince1970]], @"time", 171 | response.MIMEType, @"MIMEType", 172 | response.textEncodingName, @"textEncodingName", nil]; 173 | [dict writeToFile:otherInfoPath atomically:YES]; 174 | [data writeToFile:filePath atomically:YES]; 175 | 176 | cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:response data:data] autorelease]; 177 | 178 | 179 | 180 | 181 | 182 | }]; 183 | 184 | 185 | 186 | return cachedResponse; 187 | } 188 | return nil; 189 | } 190 | 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /LocalCache/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzhlewis2015/UIWebViewLocalCache/2c9a575597ef0333f8ee2f76a880267f51c685bf/LocalCache/Default-568h@2x.png -------------------------------------------------------------------------------- /LocalCache/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzhlewis2015/UIWebViewLocalCache/2c9a575597ef0333f8ee2f76a880267f51c685bf/LocalCache/Default.png -------------------------------------------------------------------------------- /LocalCache/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzhlewis2015/UIWebViewLocalCache/2c9a575597ef0333f8ee2f76a880267f51c685bf/LocalCache/Default@2x.png -------------------------------------------------------------------------------- /LocalCache/LocalCache-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | hk.adways.${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 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /LocalCache/LocalCache-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'LocalCache' target in the 'LocalCache' 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 | -------------------------------------------------------------------------------- /LocalCache/MBProgressHUD.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBProgressHUD.h 3 | // Version 0.5 4 | // Created by Matej Bukovinski on 2.4.09. 5 | // 6 | 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | 9 | // Copyright (c) 2011 Matej Bukovinski 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | #import 30 | #import 31 | #import 32 | 33 | @protocol MBProgressHUDDelegate; 34 | 35 | 36 | typedef enum { 37 | /** Progress is shown using an UIActivityIndicatorView. This is the default. */ 38 | MBProgressHUDModeIndeterminate, 39 | /** Progress is shown using a round, pie-chart like, progress view. */ 40 | MBProgressHUDModeDeterminate, 41 | /** Progress is shown using a ring-shaped progress view. */ 42 | MBProgressHUDModeAnnularDeterminate, 43 | /** Shows a custom view */ 44 | MBProgressHUDModeCustomView, 45 | /** Shows only labels */ 46 | MBProgressHUDModeText 47 | } MBProgressHUDMode; 48 | 49 | typedef enum { 50 | /** Opacity animation */ 51 | MBProgressHUDAnimationFade, 52 | /** Opacity + scale animation */ 53 | MBProgressHUDAnimationZoom, 54 | MBProgressHUDAnimationZoomOut = MBProgressHUDAnimationZoom, 55 | MBProgressHUDAnimationZoomIn 56 | } MBProgressHUDAnimation; 57 | 58 | 59 | #ifndef MB_STRONG 60 | #if __has_feature(objc_arc) 61 | #define MB_STRONG strong 62 | #else 63 | #define MB_STRONG retain 64 | #endif 65 | #endif 66 | 67 | #ifndef MB_WEAK 68 | #if __has_feature(objc_arc_weak) 69 | #define MB_WEAK weak 70 | #elif __has_feature(objc_arc) 71 | #define MB_WEAK unsafe_unretained 72 | #else 73 | #define MB_WEAK assign 74 | #endif 75 | #endif 76 | 77 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 78 | #define MBLabelAlignmentCenter NSTextAlignmentCenter 79 | #else 80 | #define MBLabelAlignmentCenter UITextAlignmentCenter 81 | #endif 82 | 83 | #if NS_BLOCKS_AVAILABLE 84 | typedef void (^MBProgressHUDCompletionBlock)(); 85 | #endif 86 | 87 | 88 | 89 | 90 | /** 91 | * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. 92 | * 93 | * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. 94 | * The MBProgressHUD window spans over the entire space given to it by the initWithFrame constructor and catches all 95 | * user input on this region, thereby preventing the user operations on components below the view. The HUD itself is 96 | * drawn centered as a rounded semi-transparent view which resizes depending on the user specified content. 97 | * 98 | * This view supports four modes of operation: 99 | * - MBProgressHUDModeIndeterminate - shows a UIActivityIndicatorView 100 | * - MBProgressHUDModeDeterminate - shows a custom round progress indicator 101 | * - MBProgressHUDModeAnnularDeterminate - shows a custom annular progress indicator 102 | * - MBProgressHUDModeCustomView - shows an arbitrary, user specified view (@see customView) 103 | * 104 | * All three modes can have optional labels assigned: 105 | * - If the labelText property is set and non-empty then a label containing the provided content is placed below the 106 | * indicator view. 107 | * - If also the detailsLabelText property is set then another label is placed below the first label. 108 | */ 109 | @interface MBProgressHUD : UIView 110 | 111 | /** 112 | * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. 113 | * 114 | * @param view The view that the HUD will be added to 115 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use 116 | * animations while appearing. 117 | * @return A reference to the created HUD. 118 | * 119 | * @see hideHUDForView:animated: 120 | * @see animationType 121 | */ 122 | + (MBProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; 123 | 124 | /** 125 | * Finds the top-most HUD subview and hides it. The counterpart to this method is showHUDAddedTo:animated:. 126 | * 127 | * @param view The view that is going to be searched for a HUD subview. 128 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 129 | * animations while disappearing. 130 | * @return YES if a HUD was found and removed, NO otherwise. 131 | * 132 | * @see showHUDAddedTo:animated: 133 | * @see animationType 134 | */ 135 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; 136 | 137 | /** 138 | * Finds all the HUD subviews and hides them. 139 | * 140 | * @param view The view that is going to be searched for HUD subviews. 141 | * @param animated If set to YES the HUDs will disappear using the current animationType. If set to NO the HUDs will not use 142 | * animations while disappearing. 143 | * @return the number of HUDs found and removed. 144 | * 145 | * @see hideAllHUDForView:animated: 146 | * @see animationType 147 | */ 148 | + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated; 149 | 150 | /** 151 | * Finds the top-most HUD subview and returns it. 152 | * 153 | * @param view The view that is going to be searched. 154 | * @return A reference to the last HUD subview discovered. 155 | */ 156 | + (MBProgressHUD *)HUDForView:(UIView *)view; 157 | 158 | /** 159 | * Finds all HUD subviews and returns them. 160 | * 161 | * @param view The view that is going to be searched. 162 | * @return All found HUD views (array of MBProgressHUD objects). 163 | */ 164 | + (NSArray *)allHUDsForView:(UIView *)view; 165 | 166 | /** 167 | * A convenience constructor that initializes the HUD with the window's bounds. Calls the designated constructor with 168 | * window.bounds as the parameter. 169 | * 170 | * @param window The window instance that will provide the bounds for the HUD. Should be the same instance as 171 | * the HUD's superview (i.e., the window that the HUD will be added to). 172 | */ 173 | - (id)initWithWindow:(UIWindow *)window; 174 | 175 | /** 176 | * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with 177 | * view.bounds as the parameter 178 | * 179 | * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as 180 | * the HUD's superview (i.e., the view that the HUD will be added to). 181 | */ 182 | - (id)initWithView:(UIView *)view; 183 | 184 | /** 185 | * Display the HUD. You need to make sure that the main thread completes its run loop soon after this method call so 186 | * the user interface can be updated. Call this method when your task is already set-up to be executed in a new thread 187 | * (e.g., when using something like NSOperation or calling an asynchronous call like NSURLRequest). 188 | * 189 | * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use 190 | * animations while appearing. 191 | * 192 | * @see animationType 193 | */ 194 | - (void)show:(BOOL)animated; 195 | 196 | /** 197 | * Hide the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to 198 | * hide the HUD when your task completes. 199 | * 200 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 201 | * animations while disappearing. 202 | * 203 | * @see animationType 204 | */ 205 | - (void)hide:(BOOL)animated; 206 | 207 | /** 208 | * Hide the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to 209 | * hide the HUD when your task completes. 210 | * 211 | * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use 212 | * animations while disappearing. 213 | * @param delay Delay in seconds until the HUD is hidden. 214 | * 215 | * @see animationType 216 | */ 217 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay; 218 | 219 | /** 220 | * Shows the HUD while a background task is executing in a new thread, then hides the HUD. 221 | * 222 | * This method also takes care of autorelease pools so your method does not have to be concerned with setting up a 223 | * pool. 224 | * 225 | * @param method The method to be executed while the HUD is shown. This method will be executed in a new thread. 226 | * @param target The object that the target method belongs to. 227 | * @param object An optional object to be passed to the method. 228 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use 229 | * animations while (dis)appearing. 230 | */ 231 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated; 232 | 233 | #if NS_BLOCKS_AVAILABLE 234 | 235 | /** 236 | * Shows the HUD while a block is executing on a background queue, then hides the HUD. 237 | * 238 | * @see showAnimated:whileExecutingBlock:onQueue:completion: 239 | */ 240 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block; 241 | 242 | /** 243 | * Shows the HUD while a block is executing on a background queue, then hides the HUD. 244 | * 245 | * @see showAnimated:whileExecutingBlock:onQueue:completion: 246 | */ 247 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion; 248 | 249 | /** 250 | * Shows the HUD while a block is executing on the specified dispatch queue, then hides the HUD. 251 | * 252 | * @see showAnimated:whileExecutingBlock:onQueue:completion: 253 | */ 254 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue; 255 | 256 | /** 257 | * Shows the HUD while a block is executing on the specified dispatch queue, executes completion block on the main queue, and then hides the HUD. 258 | * 259 | * @param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will 260 | * not use animations while (dis)appearing. 261 | * @param block The block to be executed while the HUD is shown. 262 | * @param queue The dispatch queue on which the block should be executed. 263 | * @param completion The block to be executed on completion. 264 | * 265 | * @see completionBlock 266 | */ 267 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue 268 | completionBlock:(MBProgressHUDCompletionBlock)completion; 269 | 270 | /** 271 | * A block that gets called after the HUD was completely hidden. 272 | */ 273 | @property (copy) MBProgressHUDCompletionBlock completionBlock; 274 | 275 | #endif 276 | 277 | /** 278 | * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. 279 | * 280 | * @see MBProgressHUDMode 281 | */ 282 | @property (assign) MBProgressHUDMode mode; 283 | 284 | /** 285 | * The animation type that should be used when the HUD is shown and hidden. 286 | * 287 | * @see MBProgressHUDAnimation 288 | */ 289 | @property (assign) MBProgressHUDAnimation animationType; 290 | 291 | /** 292 | * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. 293 | * For best results use a 37 by 37 pixel view (so the bounds match the built in indicator bounds). 294 | */ 295 | @property (MB_STRONG) UIView *customView; 296 | 297 | /** 298 | * The HUD delegate object. 299 | * 300 | * @see MBProgressHUDDelegate 301 | */ 302 | @property (MB_WEAK) id delegate; 303 | 304 | /** 305 | * An optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit 306 | * the entire text. If the text is too long it will get clipped by displaying "..." at the end. If left unchanged or 307 | * set to @"", then no message is displayed. 308 | */ 309 | @property (copy) NSString *labelText; 310 | 311 | /** 312 | * An optional details message displayed below the labelText message. This message is displayed only if the labelText 313 | * property is also set and is different from an empty string (@""). The details text can span multiple lines. 314 | */ 315 | @property (copy) NSString *detailsLabelText; 316 | 317 | /** 318 | * The opacity of the HUD window. Defaults to 0.8 (80% opacity). 319 | */ 320 | @property (assign) float opacity; 321 | 322 | /** 323 | * The color of the HUD window. Defaults to black. If this property is set, color is set using 324 | * this UIColor and the opacity property is not used. using retain because performing copy on 325 | * UIColor base colors (like [UIColor greenColor]) cause problems with the copyZone. 326 | */ 327 | @property (MB_STRONG) UIColor *color; 328 | 329 | /** 330 | * The x-axis offset of the HUD relative to the centre of the superview. 331 | */ 332 | @property (assign) float xOffset; 333 | 334 | /** 335 | * The y-ayis offset of the HUD relative to the centre of the superview. 336 | */ 337 | @property (assign) float yOffset; 338 | 339 | /** 340 | * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). 341 | * Defaults to 20.0 342 | */ 343 | @property (assign) float margin; 344 | 345 | /** 346 | * Cover the HUD background view with a radial gradient. 347 | */ 348 | @property (assign) BOOL dimBackground; 349 | 350 | /* 351 | * Grace period is the time (in seconds) that the invoked method may be run without 352 | * showing the HUD. If the task finishes before the grace time runs out, the HUD will 353 | * not be shown at all. 354 | * This may be used to prevent HUD display for very short tasks. 355 | * Defaults to 0 (no grace time). 356 | * Grace time functionality is only supported when the task status is known! 357 | * @see taskInProgress 358 | */ 359 | @property (assign) float graceTime; 360 | 361 | /** 362 | * The minimum time (in seconds) that the HUD is shown. 363 | * This avoids the problem of the HUD being shown and than instantly hidden. 364 | * Defaults to 0 (no minimum show time). 365 | */ 366 | @property (assign) float minShowTime; 367 | 368 | /** 369 | * Indicates that the executed operation is in progress. Needed for correct graceTime operation. 370 | * If you don't set a graceTime (different than 0.0) this does nothing. 371 | * This property is automatically set when using showWhileExecuting:onTarget:withObject:animated:. 372 | * When threading is done outside of the HUD (i.e., when the show: and hide: methods are used directly), 373 | * you need to set this property when your task starts and completes in order to have normal graceTime 374 | * functionality. 375 | */ 376 | @property (assign) BOOL taskInProgress; 377 | 378 | /** 379 | * Removes the HUD from its parent view when hidden. 380 | * Defaults to NO. 381 | */ 382 | @property (assign) BOOL removeFromSuperViewOnHide; 383 | 384 | /** 385 | * Font to be used for the main label. Set this property if the default is not adequate. 386 | */ 387 | @property (MB_STRONG) UIFont* labelFont; 388 | 389 | /** 390 | * Font to be used for the details label. Set this property if the default is not adequate. 391 | */ 392 | @property (MB_STRONG) UIFont* detailsLabelFont; 393 | 394 | /** 395 | * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. 396 | */ 397 | @property (assign) float progress; 398 | 399 | /** 400 | * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). 401 | */ 402 | @property (assign) CGSize minSize; 403 | 404 | /** 405 | * Force the HUD dimensions to be equal if possible. 406 | */ 407 | @property (assign, getter = isSquare) BOOL square; 408 | 409 | @end 410 | 411 | 412 | @protocol MBProgressHUDDelegate 413 | 414 | @optional 415 | 416 | /** 417 | * Called after the HUD was fully hidden from the screen. 418 | */ 419 | - (void)hudWasHidden:(MBProgressHUD *)hud; 420 | 421 | @end 422 | 423 | 424 | /** 425 | * A progress view for showing definite progress by filling up a circle (pie chart). 426 | */ 427 | @interface MBRoundProgressView : UIView 428 | 429 | /** 430 | * Progress (0.0 to 1.0) 431 | */ 432 | @property (nonatomic, assign) float progress; 433 | 434 | /** 435 | * Indicator progress color. 436 | * Defaults to white [UIColor whiteColor] 437 | */ 438 | @property (nonatomic, MB_STRONG) UIColor *progressTintColor; 439 | 440 | /** 441 | * Indicator background (non-progress) color. 442 | * Defaults to translucent white (alpha 0.1) 443 | */ 444 | @property (nonatomic, MB_STRONG) UIColor *backgroundTintColor; 445 | 446 | /* 447 | * Display mode - NO = round or YES = annular. Defaults to round. 448 | */ 449 | @property (nonatomic, assign, getter = isAnnular) BOOL annular; 450 | 451 | @end 452 | -------------------------------------------------------------------------------- /LocalCache/MBProgressHUD.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBProgressHUD.m 3 | // Version 0.5 4 | // Created by Matej Bukovinski on 2.4.09. 5 | // 6 | 7 | #import "MBProgressHUD.h" 8 | 9 | 10 | #if __has_feature(objc_arc) 11 | #define MB_AUTORELEASE(exp) exp 12 | #define MB_RELEASE(exp) exp 13 | #define MB_RETAIN(exp) exp 14 | #else 15 | #define MB_AUTORELEASE(exp) [exp autorelease] 16 | #define MB_RELEASE(exp) [exp release] 17 | #define MB_RETAIN(exp) [exp retain] 18 | #endif 19 | 20 | 21 | static const CGFloat kPadding = 4.f; 22 | static const CGFloat kLabelFontSize = 16.f; 23 | static const CGFloat kDetailsLabelFontSize = 12.f; 24 | 25 | 26 | @interface MBProgressHUD () 27 | 28 | - (void)setupLabels; 29 | - (void)registerForKVO; 30 | - (void)unregisterFromKVO; 31 | - (NSArray *)observableKeypaths; 32 | - (void)registerForNotifications; 33 | - (void)unregisterFromNotifications; 34 | - (void)updateUIForKeypath:(NSString *)keyPath; 35 | - (void)hideUsingAnimation:(BOOL)animated; 36 | - (void)showUsingAnimation:(BOOL)animated; 37 | - (void)done; 38 | - (void)updateIndicators; 39 | - (void)handleGraceTimer:(NSTimer *)theTimer; 40 | - (void)handleMinShowTimer:(NSTimer *)theTimer; 41 | - (void)setTransformForCurrentOrientation:(BOOL)animated; 42 | - (void)cleanUp; 43 | - (void)launchExecution; 44 | - (void)deviceOrientationDidChange:(NSNotification *)notification; 45 | - (void)hideDelayed:(NSNumber *)animated; 46 | 47 | @property (atomic, MB_STRONG) UIView *indicator; 48 | @property (atomic, MB_STRONG) NSTimer *graceTimer; 49 | @property (atomic, MB_STRONG) NSTimer *minShowTimer; 50 | @property (atomic, MB_STRONG) NSDate *showStarted; 51 | @property (atomic, assign) CGSize size; 52 | 53 | @end 54 | 55 | 56 | @implementation MBProgressHUD { 57 | BOOL useAnimation; 58 | SEL methodForExecution; 59 | id targetForExecution; 60 | id objectForExecution; 61 | UILabel *label; 62 | UILabel *detailsLabel; 63 | BOOL isFinished; 64 | CGAffineTransform rotationTransform; 65 | } 66 | 67 | #pragma mark - Properties 68 | 69 | @synthesize animationType; 70 | @synthesize delegate; 71 | @synthesize opacity; 72 | @synthesize color; 73 | @synthesize labelFont; 74 | @synthesize detailsLabelFont; 75 | @synthesize indicator; 76 | @synthesize xOffset; 77 | @synthesize yOffset; 78 | @synthesize minSize; 79 | @synthesize square; 80 | @synthesize margin; 81 | @synthesize dimBackground; 82 | @synthesize graceTime; 83 | @synthesize minShowTime; 84 | @synthesize graceTimer; 85 | @synthesize minShowTimer; 86 | @synthesize taskInProgress; 87 | @synthesize removeFromSuperViewOnHide; 88 | @synthesize customView; 89 | @synthesize showStarted; 90 | @synthesize mode; 91 | @synthesize labelText; 92 | @synthesize detailsLabelText; 93 | @synthesize progress; 94 | @synthesize size; 95 | #if NS_BLOCKS_AVAILABLE 96 | @synthesize completionBlock; 97 | #endif 98 | 99 | #pragma mark - Class methods 100 | 101 | + (MBProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { 102 | MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:view]; 103 | [view addSubview:hud]; 104 | [hud show:animated]; 105 | return MB_AUTORELEASE(hud); 106 | } 107 | 108 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { 109 | MBProgressHUD *hud = [MBProgressHUD HUDForView:view]; 110 | if (hud != nil) { 111 | hud.removeFromSuperViewOnHide = YES; 112 | [hud hide:animated]; 113 | return YES; 114 | } 115 | return NO; 116 | } 117 | 118 | + (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated { 119 | NSArray *huds = [self allHUDsForView:view]; 120 | for (MBProgressHUD *hud in huds) { 121 | hud.removeFromSuperViewOnHide = YES; 122 | [hud hide:animated]; 123 | } 124 | return [huds count]; 125 | } 126 | 127 | + (MBProgressHUD *)HUDForView:(UIView *)view { 128 | Class hudClass = [MBProgressHUD class]; 129 | NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator]; 130 | for (UIView *subview in subviewsEnum) { 131 | if ([subview isKindOfClass:hudClass]) { 132 | return (MBProgressHUD *)subview; 133 | } 134 | } 135 | return nil; 136 | } 137 | 138 | + (NSArray *)allHUDsForView:(UIView *)view { 139 | NSMutableArray *huds = [NSMutableArray array]; 140 | NSArray *subviews = view.subviews; 141 | Class hudClass = [MBProgressHUD class]; 142 | for (UIView *aView in subviews) { 143 | if ([aView isKindOfClass:hudClass]) { 144 | [huds addObject:aView]; 145 | } 146 | } 147 | return [NSArray arrayWithArray:huds]; 148 | } 149 | 150 | #pragma mark - Lifecycle 151 | 152 | - (id)initWithFrame:(CGRect)frame { 153 | self = [super initWithFrame:frame]; 154 | if (self) { 155 | // Set default values for properties 156 | self.animationType = MBProgressHUDAnimationFade; 157 | self.mode = MBProgressHUDModeIndeterminate; 158 | self.labelText = nil; 159 | self.detailsLabelText = nil; 160 | self.opacity = 0.8f; 161 | self.color = nil; 162 | self.labelFont = [UIFont boldSystemFontOfSize:kLabelFontSize]; 163 | self.detailsLabelFont = [UIFont boldSystemFontOfSize:kDetailsLabelFontSize]; 164 | self.xOffset = 0.0f; 165 | self.yOffset = 0.0f; 166 | self.dimBackground = NO; 167 | self.margin = 20.0f; 168 | self.graceTime = 0.0f; 169 | self.minShowTime = 0.0f; 170 | self.removeFromSuperViewOnHide = NO; 171 | self.minSize = CGSizeZero; 172 | self.square = NO; 173 | self.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin 174 | | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 175 | 176 | // Transparent background 177 | self.opaque = NO; 178 | self.backgroundColor = [UIColor clearColor]; 179 | // Make it invisible for now 180 | self.alpha = 0.0f; 181 | 182 | taskInProgress = NO; 183 | rotationTransform = CGAffineTransformIdentity; 184 | 185 | [self setupLabels]; 186 | [self updateIndicators]; 187 | [self registerForKVO]; 188 | [self registerForNotifications]; 189 | } 190 | return self; 191 | } 192 | 193 | - (id)initWithView:(UIView *)view { 194 | NSAssert(view, @"View must not be nil."); 195 | return [self initWithFrame:view.bounds]; 196 | } 197 | 198 | - (id)initWithWindow:(UIWindow *)window { 199 | return [self initWithView:window]; 200 | } 201 | 202 | - (void)dealloc { 203 | [self unregisterFromNotifications]; 204 | [self unregisterFromKVO]; 205 | #if !__has_feature(objc_arc) 206 | [color release]; 207 | [indicator release]; 208 | [label release]; 209 | [detailsLabel release]; 210 | [labelText release]; 211 | [detailsLabelText release]; 212 | [graceTimer release]; 213 | [minShowTimer release]; 214 | [showStarted release]; 215 | [customView release]; 216 | #if NS_BLOCKS_AVAILABLE 217 | [completionBlock release]; 218 | #endif 219 | [super dealloc]; 220 | #endif 221 | } 222 | 223 | #pragma mark - Show & hide 224 | 225 | - (void)show:(BOOL)animated { 226 | useAnimation = animated; 227 | // If the grace time is set postpone the HUD display 228 | if (self.graceTime > 0.0) { 229 | self.graceTimer = [NSTimer scheduledTimerWithTimeInterval:self.graceTime target:self 230 | selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO]; 231 | } 232 | // ... otherwise show the HUD imediately 233 | else { 234 | [self setNeedsDisplay]; 235 | [self showUsingAnimation:useAnimation]; 236 | } 237 | } 238 | 239 | - (void)hide:(BOOL)animated { 240 | useAnimation = animated; 241 | // If the minShow time is set, calculate how long the hud was shown, 242 | // and pospone the hiding operation if necessary 243 | if (self.minShowTime > 0.0 && showStarted) { 244 | NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:showStarted]; 245 | if (interv < self.minShowTime) { 246 | self.minShowTimer = [NSTimer scheduledTimerWithTimeInterval:(self.minShowTime - interv) target:self 247 | selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO]; 248 | return; 249 | } 250 | } 251 | // ... otherwise hide the HUD immediately 252 | [self hideUsingAnimation:useAnimation]; 253 | } 254 | 255 | - (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { 256 | [self performSelector:@selector(hideDelayed:) withObject:[NSNumber numberWithBool:animated] afterDelay:delay]; 257 | } 258 | 259 | - (void)hideDelayed:(NSNumber *)animated { 260 | [self hide:[animated boolValue]]; 261 | } 262 | 263 | #pragma mark - Timer callbacks 264 | 265 | - (void)handleGraceTimer:(NSTimer *)theTimer { 266 | // Show the HUD only if the task is still running 267 | if (taskInProgress) { 268 | [self setNeedsDisplay]; 269 | [self showUsingAnimation:useAnimation]; 270 | } 271 | } 272 | 273 | - (void)handleMinShowTimer:(NSTimer *)theTimer { 274 | [self hideUsingAnimation:useAnimation]; 275 | } 276 | 277 | #pragma mark - View Hierrarchy 278 | 279 | - (void)didMoveToSuperview { 280 | // We need to take care of rotation ourselfs if we're adding the HUD to a window 281 | if ([self.superview isKindOfClass:[UIWindow class]]) { 282 | [self setTransformForCurrentOrientation:NO]; 283 | } 284 | } 285 | 286 | #pragma mark - Internal show & hide operations 287 | 288 | - (void)showUsingAnimation:(BOOL)animated { 289 | if (animated && animationType == MBProgressHUDAnimationZoomIn) { 290 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f)); 291 | } else if (animated && animationType == MBProgressHUDAnimationZoomOut) { 292 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f)); 293 | } 294 | self.showStarted = [NSDate date]; 295 | // Fade in 296 | if (animated) { 297 | [UIView beginAnimations:nil context:NULL]; 298 | [UIView setAnimationDuration:0.30]; 299 | self.alpha = 1.0f; 300 | if (animationType == MBProgressHUDAnimationZoomIn || animationType == MBProgressHUDAnimationZoomOut) { 301 | self.transform = rotationTransform; 302 | } 303 | [UIView commitAnimations]; 304 | } 305 | else { 306 | self.alpha = 1.0f; 307 | } 308 | } 309 | 310 | - (void)hideUsingAnimation:(BOOL)animated { 311 | // Fade out 312 | if (animated && showStarted) { 313 | [UIView beginAnimations:nil context:NULL]; 314 | [UIView setAnimationDuration:0.30]; 315 | [UIView setAnimationDelegate:self]; 316 | [UIView setAnimationDidStopSelector:@selector(animationFinished:finished:context:)]; 317 | // 0.02 prevents the hud from passing through touches during the animation the hud will get completely hidden 318 | // in the done method 319 | if (animationType == MBProgressHUDAnimationZoomIn) { 320 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(1.5f, 1.5f)); 321 | } else if (animationType == MBProgressHUDAnimationZoomOut) { 322 | self.transform = CGAffineTransformConcat(rotationTransform, CGAffineTransformMakeScale(0.5f, 0.5f)); 323 | } 324 | 325 | self.alpha = 0.02f; 326 | [UIView commitAnimations]; 327 | } 328 | else { 329 | self.alpha = 0.0f; 330 | [self done]; 331 | } 332 | self.showStarted = nil; 333 | } 334 | 335 | - (void)animationFinished:(NSString *)animationID finished:(BOOL)finished context:(void*)context { 336 | [self done]; 337 | } 338 | 339 | - (void)done { 340 | isFinished = YES; 341 | self.alpha = 0.0f; 342 | if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { 343 | [delegate performSelector:@selector(hudWasHidden:) withObject:self]; 344 | } 345 | #if NS_BLOCKS_AVAILABLE 346 | if (self.completionBlock) { 347 | self.completionBlock(); 348 | self.completionBlock = NULL; 349 | } 350 | #endif 351 | if (removeFromSuperViewOnHide) { 352 | [self removeFromSuperview]; 353 | } 354 | } 355 | 356 | #pragma mark - Threading 357 | 358 | - (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { 359 | methodForExecution = method; 360 | targetForExecution = MB_RETAIN(target); 361 | objectForExecution = MB_RETAIN(object); 362 | // Launch execution in new thread 363 | self.taskInProgress = YES; 364 | [NSThread detachNewThreadSelector:@selector(launchExecution) toTarget:self withObject:nil]; 365 | // Show HUD view 366 | [self show:animated]; 367 | } 368 | 369 | #if NS_BLOCKS_AVAILABLE 370 | 371 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block { 372 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 373 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; 374 | } 375 | 376 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)())completion { 377 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 378 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion]; 379 | } 380 | 381 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue { 382 | [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; 383 | } 384 | 385 | - (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue 386 | completionBlock:(MBProgressHUDCompletionBlock)completion { 387 | self.taskInProgress = YES; 388 | self.completionBlock = completion; 389 | dispatch_async(queue, ^(void) { 390 | block(); 391 | dispatch_async(dispatch_get_main_queue(), ^(void) { 392 | [self cleanUp]; 393 | }); 394 | }); 395 | [self show:animated]; 396 | } 397 | 398 | #endif 399 | 400 | - (void)launchExecution { 401 | @autoreleasepool { 402 | #pragma clang diagnostic push 403 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 404 | // Start executing the requested task 405 | [targetForExecution performSelector:methodForExecution withObject:objectForExecution]; 406 | #pragma clang diagnostic pop 407 | // Task completed, update view in main thread (note: view operations should 408 | // be done only in the main thread) 409 | [self performSelectorOnMainThread:@selector(cleanUp) withObject:nil waitUntilDone:NO]; 410 | } 411 | } 412 | 413 | - (void)cleanUp { 414 | taskInProgress = NO; 415 | self.indicator = nil; 416 | #if !__has_feature(objc_arc) 417 | [targetForExecution release]; 418 | [objectForExecution release]; 419 | #else 420 | targetForExecution = nil; 421 | objectForExecution = nil; 422 | #endif 423 | [self hide:useAnimation]; 424 | } 425 | 426 | #pragma mark - UI 427 | 428 | - (void)setupLabels { 429 | label = [[UILabel alloc] initWithFrame:self.bounds]; 430 | label.adjustsFontSizeToFitWidth = NO; 431 | label.textAlignment = MBLabelAlignmentCenter; 432 | label.opaque = NO; 433 | label.backgroundColor = [UIColor clearColor]; 434 | label.textColor = [UIColor whiteColor]; 435 | label.font = self.labelFont; 436 | label.text = self.labelText; 437 | [self addSubview:label]; 438 | 439 | detailsLabel = [[UILabel alloc] initWithFrame:self.bounds]; 440 | detailsLabel.font = self.detailsLabelFont; 441 | detailsLabel.adjustsFontSizeToFitWidth = NO; 442 | detailsLabel.textAlignment = MBLabelAlignmentCenter; 443 | detailsLabel.opaque = NO; 444 | detailsLabel.backgroundColor = [UIColor clearColor]; 445 | detailsLabel.textColor = [UIColor whiteColor]; 446 | detailsLabel.numberOfLines = 0; 447 | detailsLabel.font = self.detailsLabelFont; 448 | detailsLabel.text = self.detailsLabelText; 449 | [self addSubview:detailsLabel]; 450 | } 451 | 452 | - (void)updateIndicators { 453 | 454 | BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; 455 | BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]]; 456 | 457 | if (mode == MBProgressHUDModeIndeterminate && !isActivityIndicator) { 458 | // Update to indeterminate indicator 459 | [indicator removeFromSuperview]; 460 | self.indicator = MB_AUTORELEASE([[UIActivityIndicatorView alloc] 461 | initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]); 462 | [(UIActivityIndicatorView *)indicator startAnimating]; 463 | [self addSubview:indicator]; 464 | } 465 | else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { 466 | if (!isRoundIndicator) { 467 | // Update to determinante indicator 468 | [indicator removeFromSuperview]; 469 | self.indicator = MB_AUTORELEASE([[MBRoundProgressView alloc] init]); 470 | [self addSubview:indicator]; 471 | } 472 | if (mode == MBProgressHUDModeAnnularDeterminate) { 473 | [(MBRoundProgressView *)indicator setAnnular:YES]; 474 | } 475 | } 476 | else if (mode == MBProgressHUDModeCustomView && customView != indicator) { 477 | // Update custom view indicator 478 | [indicator removeFromSuperview]; 479 | self.indicator = customView; 480 | [self addSubview:indicator]; 481 | } else if (mode == MBProgressHUDModeText) { 482 | [indicator removeFromSuperview]; 483 | self.indicator = nil; 484 | } 485 | } 486 | 487 | #pragma mark - Layout 488 | 489 | - (void)layoutSubviews { 490 | 491 | // Entirely cover the parent view 492 | UIView *parent = self.superview; 493 | if (parent) { 494 | self.frame = parent.bounds; 495 | } 496 | CGRect bounds = self.bounds; 497 | 498 | // Determine the total widt and height needed 499 | CGFloat maxWidth = bounds.size.width - 4 * margin; 500 | CGSize totalSize = CGSizeZero; 501 | 502 | CGRect indicatorF = indicator.bounds; 503 | indicatorF.size.width = MIN(indicatorF.size.width, maxWidth); 504 | totalSize.width = MAX(totalSize.width, indicatorF.size.width); 505 | totalSize.height += indicatorF.size.height; 506 | 507 | CGSize labelSize = [label.text sizeWithFont:label.font]; 508 | labelSize.width = MIN(labelSize.width, maxWidth); 509 | totalSize.width = MAX(totalSize.width, labelSize.width); 510 | totalSize.height += labelSize.height; 511 | if (labelSize.height > 0.f && indicatorF.size.height > 0.f) { 512 | totalSize.height += kPadding; 513 | } 514 | 515 | CGFloat remainingHeight = bounds.size.height - totalSize.height - kPadding - 4 * margin; 516 | CGSize maxSize = CGSizeMake(maxWidth, remainingHeight); 517 | CGSize detailsLabelSize = [detailsLabel.text sizeWithFont:detailsLabel.font 518 | constrainedToSize:maxSize lineBreakMode:detailsLabel.lineBreakMode]; 519 | totalSize.width = MAX(totalSize.width, detailsLabelSize.width); 520 | totalSize.height += detailsLabelSize.height; 521 | if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) { 522 | totalSize.height += kPadding; 523 | } 524 | 525 | totalSize.width += 2 * margin; 526 | totalSize.height += 2 * margin; 527 | 528 | // Position elements 529 | CGFloat yPos = roundf(((bounds.size.height - totalSize.height) / 2)) + margin + yOffset; 530 | CGFloat xPos = xOffset; 531 | indicatorF.origin.y = yPos; 532 | indicatorF.origin.x = roundf((bounds.size.width - indicatorF.size.width) / 2) + xPos; 533 | indicator.frame = indicatorF; 534 | yPos += indicatorF.size.height; 535 | 536 | if (labelSize.height > 0.f && indicatorF.size.height > 0.f) { 537 | yPos += kPadding; 538 | } 539 | CGRect labelF; 540 | labelF.origin.y = yPos; 541 | labelF.origin.x = roundf((bounds.size.width - labelSize.width) / 2) + xPos; 542 | labelF.size = labelSize; 543 | label.frame = labelF; 544 | yPos += labelF.size.height; 545 | 546 | if (detailsLabelSize.height > 0.f && (indicatorF.size.height > 0.f || labelSize.height > 0.f)) { 547 | yPos += kPadding; 548 | } 549 | CGRect detailsLabelF; 550 | detailsLabelF.origin.y = yPos; 551 | detailsLabelF.origin.x = roundf((bounds.size.width - detailsLabelSize.width) / 2) + xPos; 552 | detailsLabelF.size = detailsLabelSize; 553 | detailsLabel.frame = detailsLabelF; 554 | 555 | // Enforce minsize and quare rules 556 | if (square) { 557 | CGFloat max = MAX(totalSize.width, totalSize.height); 558 | if (max <= bounds.size.width - 2 * margin) { 559 | totalSize.width = max; 560 | } 561 | if (max <= bounds.size.height - 2 * margin) { 562 | totalSize.height = max; 563 | } 564 | } 565 | if (totalSize.width < minSize.width) { 566 | totalSize.width = minSize.width; 567 | } 568 | if (totalSize.height < minSize.height) { 569 | totalSize.height = minSize.height; 570 | } 571 | 572 | self.size = totalSize; 573 | } 574 | 575 | #pragma mark BG Drawing 576 | 577 | - (void)drawRect:(CGRect)rect { 578 | 579 | CGContextRef context = UIGraphicsGetCurrentContext(); 580 | UIGraphicsPushContext(context); 581 | 582 | if (self.dimBackground) { 583 | //Gradient colours 584 | size_t gradLocationsNum = 2; 585 | CGFloat gradLocations[2] = {0.0f, 1.0f}; 586 | CGFloat gradColors[8] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.75f}; 587 | CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 588 | CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, gradColors, gradLocations, gradLocationsNum); 589 | CGColorSpaceRelease(colorSpace); 590 | //Gradient center 591 | CGPoint gradCenter= CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 592 | //Gradient radius 593 | float gradRadius = MIN(self.bounds.size.width , self.bounds.size.height) ; 594 | //Gradient draw 595 | CGContextDrawRadialGradient (context, gradient, gradCenter, 596 | 0, gradCenter, gradRadius, 597 | kCGGradientDrawsAfterEndLocation); 598 | CGGradientRelease(gradient); 599 | } 600 | 601 | // Set background rect color 602 | if (self.color) { 603 | CGContextSetFillColorWithColor(context, self.color.CGColor); 604 | } else { 605 | CGContextSetGrayFillColor(context, 0.0f, self.opacity); 606 | } 607 | 608 | 609 | // Center HUD 610 | CGRect allRect = self.bounds; 611 | // Draw rounded HUD backgroud rect 612 | CGRect boxRect = CGRectMake(roundf((allRect.size.width - size.width) / 2) + self.xOffset, 613 | roundf((allRect.size.height - size.height) / 2) + self.yOffset, size.width, size.height); 614 | float radius = 10.0f; 615 | CGContextBeginPath(context); 616 | CGContextMoveToPoint(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect)); 617 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMinY(boxRect) + radius, radius, 3 * (float)M_PI / 2, 0, 0); 618 | CGContextAddArc(context, CGRectGetMaxX(boxRect) - radius, CGRectGetMaxY(boxRect) - radius, radius, 0, (float)M_PI / 2, 0); 619 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMaxY(boxRect) - radius, radius, (float)M_PI / 2, (float)M_PI, 0); 620 | CGContextAddArc(context, CGRectGetMinX(boxRect) + radius, CGRectGetMinY(boxRect) + radius, radius, (float)M_PI, 3 * (float)M_PI / 2, 0); 621 | CGContextClosePath(context); 622 | CGContextFillPath(context); 623 | 624 | UIGraphicsPopContext(); 625 | } 626 | 627 | #pragma mark - KVO 628 | 629 | - (void)registerForKVO { 630 | for (NSString *keyPath in [self observableKeypaths]) { 631 | [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; 632 | } 633 | } 634 | 635 | - (void)unregisterFromKVO { 636 | for (NSString *keyPath in [self observableKeypaths]) { 637 | [self removeObserver:self forKeyPath:keyPath]; 638 | } 639 | } 640 | 641 | - (NSArray *)observableKeypaths { 642 | return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", 643 | @"detailsLabelText", @"detailsLabelFont", @"progress", nil]; 644 | } 645 | 646 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 647 | if (![NSThread isMainThread]) { 648 | [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO]; 649 | } else { 650 | [self updateUIForKeypath:keyPath]; 651 | } 652 | } 653 | 654 | - (void)updateUIForKeypath:(NSString *)keyPath { 655 | if ([keyPath isEqualToString:@"mode"] || [keyPath isEqualToString:@"customView"]) { 656 | [self updateIndicators]; 657 | } else if ([keyPath isEqualToString:@"labelText"]) { 658 | label.text = self.labelText; 659 | } else if ([keyPath isEqualToString:@"labelFont"]) { 660 | label.font = self.labelFont; 661 | } else if ([keyPath isEqualToString:@"detailsLabelText"]) { 662 | detailsLabel.text = self.detailsLabelText; 663 | } else if ([keyPath isEqualToString:@"detailsLabelFont"]) { 664 | detailsLabel.font = self.detailsLabelFont; 665 | } else if ([keyPath isEqualToString:@"progress"]) { 666 | if ([indicator respondsToSelector:@selector(setProgress:)]) { 667 | [(id)indicator setProgress:progress]; 668 | } 669 | return; 670 | } 671 | [self setNeedsLayout]; 672 | [self setNeedsDisplay]; 673 | } 674 | 675 | #pragma mark - Notifications 676 | 677 | - (void)registerForNotifications { 678 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 679 | [nc addObserver:self selector:@selector(deviceOrientationDidChange:) 680 | name:UIDeviceOrientationDidChangeNotification object:nil]; 681 | } 682 | 683 | - (void)unregisterFromNotifications { 684 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 685 | } 686 | 687 | - (void)deviceOrientationDidChange:(NSNotification *)notification { 688 | UIView *superview = self.superview; 689 | if (!superview) { 690 | return; 691 | } else if ([superview isKindOfClass:[UIWindow class]]) { 692 | [self setTransformForCurrentOrientation:YES]; 693 | } else { 694 | self.bounds = self.superview.bounds; 695 | [self setNeedsDisplay]; 696 | } 697 | } 698 | 699 | - (void)setTransformForCurrentOrientation:(BOOL)animated { 700 | // Stay in sync with the superview 701 | if (self.superview) { 702 | self.bounds = self.superview.bounds; 703 | [self setNeedsDisplay]; 704 | } 705 | 706 | UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; 707 | CGFloat radians = 0; 708 | if (UIInterfaceOrientationIsLandscape(orientation)) { 709 | if (orientation == UIInterfaceOrientationLandscapeLeft) { radians = -(CGFloat)M_PI_2; } 710 | else { radians = (CGFloat)M_PI_2; } 711 | // Window coordinates differ! 712 | self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); 713 | } else { 714 | if (orientation == UIInterfaceOrientationPortraitUpsideDown) { radians = (CGFloat)M_PI; } 715 | else { radians = 0; } 716 | } 717 | rotationTransform = CGAffineTransformMakeRotation(radians); 718 | 719 | if (animated) { 720 | [UIView beginAnimations:nil context:nil]; 721 | } 722 | [self setTransform:rotationTransform]; 723 | if (animated) { 724 | [UIView commitAnimations]; 725 | } 726 | } 727 | 728 | @end 729 | 730 | 731 | @implementation MBRoundProgressView { 732 | float _progress; 733 | BOOL _annular; 734 | } 735 | 736 | #pragma mark - Properties 737 | 738 | @synthesize progressTintColor = _progressTintColor; 739 | @synthesize backgroundTintColor = _backgroundTintColor; 740 | 741 | #pragma mark - Accessors 742 | 743 | - (float)progress { 744 | return _progress; 745 | } 746 | 747 | - (void)setProgress:(float)progress { 748 | _progress = progress; 749 | [self setNeedsDisplay]; 750 | } 751 | 752 | - (BOOL)isAnnular { 753 | return _annular; 754 | } 755 | 756 | - (void)setAnnular:(BOOL)annular { 757 | _annular = annular; 758 | [self setNeedsDisplay]; 759 | } 760 | 761 | #pragma mark - Lifecycle 762 | 763 | - (id)init { 764 | return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)]; 765 | } 766 | 767 | - (id)initWithFrame:(CGRect)frame { 768 | self = [super initWithFrame:frame]; 769 | if (self) { 770 | self.backgroundColor = [UIColor clearColor]; 771 | self.opaque = NO; 772 | _progress = 0.f; 773 | _annular = NO; 774 | _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f]; 775 | _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f]; 776 | [self registerForKVO]; 777 | } 778 | return self; 779 | } 780 | 781 | - (void)dealloc { 782 | [self unregisterFromKVO]; 783 | #if !__has_feature(objc_arc) 784 | [_progressTintColor release]; 785 | [_backgroundTintColor release]; 786 | [super dealloc]; 787 | #endif 788 | } 789 | 790 | #pragma mark - Drawing 791 | 792 | - (void)drawRect:(CGRect)rect { 793 | 794 | CGRect allRect = self.bounds; 795 | CGRect circleRect = CGRectInset(allRect, 2.0f, 2.0f); 796 | CGContextRef context = UIGraphicsGetCurrentContext(); 797 | 798 | if (_annular) { 799 | // Draw background 800 | CGFloat lineWidth = 5.f; 801 | UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; 802 | processBackgroundPath.lineWidth = lineWidth; 803 | processBackgroundPath.lineCapStyle = kCGLineCapRound; 804 | CGPoint center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 805 | CGFloat radius = (self.bounds.size.width - lineWidth)/2; 806 | CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees 807 | CGFloat endAngle = (2 * (float)M_PI) + startAngle; 808 | [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 809 | [_backgroundTintColor set]; 810 | [processBackgroundPath stroke]; 811 | // Draw progress 812 | UIBezierPath *processPath = [UIBezierPath bezierPath]; 813 | processPath.lineCapStyle = kCGLineCapRound; 814 | processPath.lineWidth = lineWidth; 815 | endAngle = (self.progress * 2 * (float)M_PI) + startAngle; 816 | [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; 817 | [_progressTintColor set]; 818 | [processPath stroke]; 819 | } else { 820 | // Draw background 821 | [_progressTintColor setStroke]; 822 | [_backgroundTintColor setFill]; 823 | CGContextSetLineWidth(context, 2.0f); 824 | CGContextFillEllipseInRect(context, circleRect); 825 | CGContextStrokeEllipseInRect(context, circleRect); 826 | // Draw progress 827 | CGPoint center = CGPointMake(allRect.size.width / 2, allRect.size.height / 2); 828 | CGFloat radius = (allRect.size.width - 4) / 2; 829 | CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees 830 | CGFloat endAngle = (self.progress * 2 * (float)M_PI) + startAngle; 831 | CGContextSetRGBFillColor(context, 1.0f, 1.0f, 1.0f, 1.0f); // white 832 | CGContextMoveToPoint(context, center.x, center.y); 833 | CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); 834 | CGContextClosePath(context); 835 | CGContextFillPath(context); 836 | } 837 | } 838 | 839 | #pragma mark - KVO 840 | 841 | - (void)registerForKVO { 842 | for (NSString *keyPath in [self observableKeypaths]) { 843 | [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; 844 | } 845 | } 846 | 847 | - (void)unregisterFromKVO { 848 | for (NSString *keyPath in [self observableKeypaths]) { 849 | [self removeObserver:self forKeyPath:keyPath]; 850 | } 851 | } 852 | 853 | - (NSArray *)observableKeypaths { 854 | return [NSArray arrayWithObjects:@"progressTintColor", @"backgroundTintColor", nil]; 855 | } 856 | 857 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 858 | [self setNeedsDisplay]; 859 | } 860 | 861 | @end 862 | -------------------------------------------------------------------------------- /LocalCache/Reachability.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Tony Million. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import 29 | #import 30 | 31 | #import 32 | #import 33 | #import 34 | #import 35 | #import 36 | #import 37 | 38 | /** 39 | * Does ARC support GCD objects? 40 | * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+ 41 | * 42 | * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h 43 | **/ 44 | #if OS_OBJECT_USE_OBJC 45 | #define NEEDS_DISPATCH_RETAIN_RELEASE 0 46 | #else 47 | #define NEEDS_DISPATCH_RETAIN_RELEASE 1 48 | #endif 49 | 50 | /** 51 | * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X. 52 | * 53 | * @see http://nshipster.com/ns_enum-ns_options/ 54 | **/ 55 | #ifndef NS_ENUM 56 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type 57 | #endif 58 | 59 | extern NSString *const kReachabilityChangedNotification; 60 | 61 | typedef NS_ENUM(NSInteger, NetworkStatus) { 62 | // Apple NetworkStatus Compatible Names. 63 | NotReachable = 0, 64 | ReachableViaWiFi = 2, 65 | ReachableViaWWAN = 1 66 | }; 67 | 68 | @class Reachability; 69 | 70 | typedef void (^NetworkReachable)(Reachability * reachability); 71 | typedef void (^NetworkUnreachable)(Reachability * reachability); 72 | 73 | @interface Reachability : NSObject 74 | 75 | @property (nonatomic, copy) NetworkReachable reachableBlock; 76 | @property (nonatomic, copy) NetworkUnreachable unreachableBlock; 77 | 78 | 79 | @property (nonatomic, assign) BOOL reachableOnWWAN; 80 | 81 | +(Reachability*)reachabilityWithHostname:(NSString*)hostname; 82 | // This is identical to the function above, but is here to maintain 83 | //compatibility with Apples original code. (see .m) 84 | +(Reachability*)reachabilityWithHostName:(NSString*)hostname; 85 | +(Reachability*)reachabilityForInternetConnection; 86 | +(Reachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress; 87 | +(Reachability*)reachabilityForLocalWiFi; 88 | +(BOOL)networkAvailable; 89 | -(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref; 90 | 91 | -(BOOL)startNotifier; 92 | -(void)stopNotifier; 93 | 94 | -(BOOL)isReachable; 95 | -(BOOL)isReachableViaWWAN; 96 | -(BOOL)isReachableViaWiFi; 97 | 98 | // WWAN may be available, but not active until a connection has been established. 99 | // WiFi may require a connection for VPN on Demand. 100 | -(BOOL)isConnectionRequired; // Identical DDG variant. 101 | -(BOOL)connectionRequired; // Apple's routine. 102 | // Dynamic, on demand connection? 103 | -(BOOL)isConnectionOnDemand; 104 | // Is user intervention required? 105 | -(BOOL)isInterventionRequired; 106 | 107 | -(NetworkStatus)currentReachabilityStatus; 108 | -(SCNetworkReachabilityFlags)reachabilityFlags; 109 | -(NSString*)currentReachabilityString; 110 | -(NSString*)currentReachabilityFlags; 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /LocalCache/Reachability.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Tony Million. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #import "Reachability.h" 29 | 30 | 31 | NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification"; 32 | 33 | @interface Reachability () 34 | 35 | @property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef; 36 | 37 | 38 | #if NEEDS_DISPATCH_RETAIN_RELEASE 39 | @property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue; 40 | #else 41 | @property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue; 42 | #endif 43 | 44 | 45 | @property (nonatomic, strong) id reachabilityObject; 46 | 47 | -(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags; 48 | -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags; 49 | 50 | @end 51 | 52 | static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags) 53 | { 54 | return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c", 55 | #if TARGET_OS_IPHONE 56 | (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', 57 | #else 58 | 'X', 59 | #endif 60 | (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', 61 | (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', 62 | (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', 63 | (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', 64 | (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', 65 | (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', 66 | (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', 67 | (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; 68 | } 69 | 70 | // Start listening for reachability notifications on the current run loop 71 | static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) 72 | { 73 | #pragma unused (target) 74 | #if __has_feature(objc_arc) 75 | Reachability *reachability = ((__bridge Reachability*)info); 76 | #else 77 | Reachability *reachability = ((Reachability*)info); 78 | #endif 79 | 80 | // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool, 81 | // but what the heck eh? 82 | @autoreleasepool 83 | { 84 | [reachability reachabilityChanged:flags]; 85 | } 86 | } 87 | 88 | 89 | @implementation Reachability 90 | 91 | @synthesize reachabilityRef; 92 | @synthesize reachabilitySerialQueue; 93 | 94 | @synthesize reachableOnWWAN; 95 | 96 | @synthesize reachableBlock; 97 | @synthesize unreachableBlock; 98 | 99 | @synthesize reachabilityObject; 100 | 101 | #pragma mark - Class Constructor Methods 102 | 103 | +(BOOL)networkAvailable 104 | { 105 | if ([[Reachability reachabilityForLocalWiFi] currentReachabilityStatus] != NotReachable) { 106 | return YES; 107 | } 108 | if ([[Reachability reachabilityForInternetConnection] currentReachabilityStatus] != NotReachable) { 109 | return YES; 110 | } 111 | return NO; 112 | } 113 | +(Reachability*)reachabilityWithHostName:(NSString*)hostname 114 | { 115 | return [Reachability reachabilityWithHostname:hostname]; 116 | } 117 | 118 | +(Reachability*)reachabilityWithHostname:(NSString*)hostname 119 | { 120 | SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]); 121 | if (ref) 122 | { 123 | id reachability = [[self alloc] initWithReachabilityRef:ref]; 124 | 125 | #if __has_feature(objc_arc) 126 | return reachability; 127 | #else 128 | return [reachability autorelease]; 129 | #endif 130 | 131 | } 132 | 133 | return nil; 134 | } 135 | 136 | +(Reachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress 137 | { 138 | SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); 139 | if (ref) 140 | { 141 | id reachability = [[self alloc] initWithReachabilityRef:ref]; 142 | 143 | #if __has_feature(objc_arc) 144 | return reachability; 145 | #else 146 | return [reachability autorelease]; 147 | #endif 148 | } 149 | 150 | return nil; 151 | } 152 | 153 | +(Reachability *)reachabilityForInternetConnection 154 | { 155 | struct sockaddr_in zeroAddress; 156 | bzero(&zeroAddress, sizeof(zeroAddress)); 157 | zeroAddress.sin_len = sizeof(zeroAddress); 158 | zeroAddress.sin_family = AF_INET; 159 | 160 | return [self reachabilityWithAddress:&zeroAddress]; 161 | } 162 | 163 | +(Reachability*)reachabilityForLocalWiFi 164 | { 165 | struct sockaddr_in localWifiAddress; 166 | bzero(&localWifiAddress, sizeof(localWifiAddress)); 167 | localWifiAddress.sin_len = sizeof(localWifiAddress); 168 | localWifiAddress.sin_family = AF_INET; 169 | // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 170 | localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); 171 | 172 | return [self reachabilityWithAddress:&localWifiAddress]; 173 | } 174 | 175 | 176 | // Initialization methods 177 | 178 | -(Reachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref 179 | { 180 | self = [super init]; 181 | if (self != nil) 182 | { 183 | self.reachableOnWWAN = YES; 184 | self.reachabilityRef = ref; 185 | } 186 | 187 | return self; 188 | } 189 | 190 | -(void)dealloc 191 | { 192 | [self stopNotifier]; 193 | 194 | if(self.reachabilityRef) 195 | { 196 | CFRelease(self.reachabilityRef); 197 | self.reachabilityRef = nil; 198 | } 199 | 200 | self.reachableBlock = nil; 201 | self.unreachableBlock = nil; 202 | 203 | #if !(__has_feature(objc_arc)) 204 | [super dealloc]; 205 | #endif 206 | 207 | 208 | } 209 | 210 | #pragma mark - Notifier Methods 211 | 212 | // Notifier 213 | // NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD 214 | // - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS. 215 | // INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want) 216 | 217 | -(BOOL)startNotifier 218 | { 219 | SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; 220 | 221 | // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves 222 | // woah 223 | self.reachabilityObject = self; 224 | 225 | 226 | 227 | // First, we need to create a serial queue. 228 | // We allocate this once for the lifetime of the notifier. 229 | self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL); 230 | if(!self.reachabilitySerialQueue) 231 | { 232 | return NO; 233 | } 234 | 235 | #if __has_feature(objc_arc) 236 | context.info = (__bridge void *)self; 237 | #else 238 | context.info = (void *)self; 239 | #endif 240 | 241 | if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)) 242 | { 243 | #ifdef DEBUG 244 | NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError())); 245 | #endif 246 | 247 | // Clear out the dispatch queue 248 | if(self.reachabilitySerialQueue) 249 | { 250 | #if NEEDS_DISPATCH_RETAIN_RELEASE 251 | dispatch_release(self.reachabilitySerialQueue); 252 | #endif 253 | self.reachabilitySerialQueue = nil; 254 | } 255 | 256 | self.reachabilityObject = nil; 257 | 258 | return NO; 259 | } 260 | 261 | // Set it as our reachability queue, which will retain the queue 262 | if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)) 263 | { 264 | #ifdef DEBUG 265 | NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError())); 266 | #endif 267 | 268 | // UH OH - FAILURE! 269 | 270 | // First stop, any callbacks! 271 | SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); 272 | 273 | // Then clear out the dispatch queue. 274 | if(self.reachabilitySerialQueue) 275 | { 276 | #if NEEDS_DISPATCH_RETAIN_RELEASE 277 | dispatch_release(self.reachabilitySerialQueue); 278 | #endif 279 | self.reachabilitySerialQueue = nil; 280 | } 281 | 282 | self.reachabilityObject = nil; 283 | 284 | return NO; 285 | } 286 | 287 | return YES; 288 | } 289 | 290 | -(void)stopNotifier 291 | { 292 | // First stop, any callbacks! 293 | SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); 294 | 295 | // Unregister target from the GCD serial dispatch queue. 296 | SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL); 297 | 298 | if(self.reachabilitySerialQueue) 299 | { 300 | #if NEEDS_DISPATCH_RETAIN_RELEASE 301 | dispatch_release(self.reachabilitySerialQueue); 302 | #endif 303 | self.reachabilitySerialQueue = nil; 304 | } 305 | 306 | self.reachabilityObject = nil; 307 | } 308 | 309 | #pragma mark - reachability tests 310 | 311 | // This is for the case where you flick the airplane mode; 312 | // you end up getting something like this: 313 | //Reachability: WR ct----- 314 | //Reachability: -- ------- 315 | //Reachability: WR ct----- 316 | //Reachability: -- ------- 317 | // We treat this as 4 UNREACHABLE triggers - really apple should do better than this 318 | 319 | #define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection) 320 | 321 | -(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags 322 | { 323 | BOOL connectionUP = YES; 324 | 325 | if(!(flags & kSCNetworkReachabilityFlagsReachable)) 326 | connectionUP = NO; 327 | 328 | if( (flags & testcase) == testcase ) 329 | connectionUP = NO; 330 | 331 | #if TARGET_OS_IPHONE 332 | if(flags & kSCNetworkReachabilityFlagsIsWWAN) 333 | { 334 | // We're on 3G. 335 | if(!self.reachableOnWWAN) 336 | { 337 | // We don't want to connect when on 3G. 338 | connectionUP = NO; 339 | } 340 | } 341 | #endif 342 | 343 | return connectionUP; 344 | } 345 | 346 | -(BOOL)isReachable 347 | { 348 | SCNetworkReachabilityFlags flags; 349 | 350 | if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) 351 | return NO; 352 | 353 | return [self isReachableWithFlags:flags]; 354 | } 355 | 356 | -(BOOL)isReachableViaWWAN 357 | { 358 | #if TARGET_OS_IPHONE 359 | 360 | SCNetworkReachabilityFlags flags = 0; 361 | 362 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 363 | { 364 | // Check we're REACHABLE 365 | if(flags & kSCNetworkReachabilityFlagsReachable) 366 | { 367 | // Now, check we're on WWAN 368 | if(flags & kSCNetworkReachabilityFlagsIsWWAN) 369 | { 370 | return YES; 371 | } 372 | } 373 | } 374 | #endif 375 | 376 | return NO; 377 | } 378 | 379 | -(BOOL)isReachableViaWiFi 380 | { 381 | SCNetworkReachabilityFlags flags = 0; 382 | 383 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 384 | { 385 | // Check we're reachable 386 | if((flags & kSCNetworkReachabilityFlagsReachable)) 387 | { 388 | #if TARGET_OS_IPHONE 389 | // Check we're NOT on WWAN 390 | if((flags & kSCNetworkReachabilityFlagsIsWWAN)) 391 | { 392 | return NO; 393 | } 394 | #endif 395 | return YES; 396 | } 397 | } 398 | 399 | return NO; 400 | } 401 | 402 | 403 | // WWAN may be available, but not active until a connection has been established. 404 | // WiFi may require a connection for VPN on Demand. 405 | -(BOOL)isConnectionRequired 406 | { 407 | return [self connectionRequired]; 408 | } 409 | 410 | -(BOOL)connectionRequired 411 | { 412 | SCNetworkReachabilityFlags flags; 413 | 414 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 415 | { 416 | return (flags & kSCNetworkReachabilityFlagsConnectionRequired); 417 | } 418 | 419 | return NO; 420 | } 421 | 422 | // Dynamic, on demand connection? 423 | -(BOOL)isConnectionOnDemand 424 | { 425 | SCNetworkReachabilityFlags flags; 426 | 427 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 428 | { 429 | return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && 430 | (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand))); 431 | } 432 | 433 | return NO; 434 | } 435 | 436 | // Is user intervention required? 437 | -(BOOL)isInterventionRequired 438 | { 439 | SCNetworkReachabilityFlags flags; 440 | 441 | if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 442 | { 443 | return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && 444 | (flags & kSCNetworkReachabilityFlagsInterventionRequired)); 445 | } 446 | 447 | return NO; 448 | } 449 | 450 | 451 | #pragma mark - reachability status stuff 452 | 453 | -(NetworkStatus)currentReachabilityStatus 454 | { 455 | if([self isReachable]) 456 | { 457 | if([self isReachableViaWiFi]) 458 | return ReachableViaWiFi; 459 | 460 | #if TARGET_OS_IPHONE 461 | return ReachableViaWWAN; 462 | #endif 463 | } 464 | 465 | return NotReachable; 466 | } 467 | 468 | -(SCNetworkReachabilityFlags)reachabilityFlags 469 | { 470 | SCNetworkReachabilityFlags flags = 0; 471 | 472 | if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) 473 | { 474 | return flags; 475 | } 476 | 477 | return 0; 478 | } 479 | 480 | -(NSString*)currentReachabilityString 481 | { 482 | NetworkStatus temp = [self currentReachabilityStatus]; 483 | 484 | if(temp == reachableOnWWAN) 485 | { 486 | // Updated for the fact that we have CDMA phones now! 487 | return NSLocalizedString(@"Cellular", @""); 488 | } 489 | if (temp == ReachableViaWiFi) 490 | { 491 | return NSLocalizedString(@"WiFi", @""); 492 | } 493 | 494 | return NSLocalizedString(@"No Connection", @""); 495 | } 496 | 497 | -(NSString*)currentReachabilityFlags 498 | { 499 | return reachabilityFlags([self reachabilityFlags]); 500 | } 501 | 502 | #pragma mark - Callback function calls this method 503 | 504 | -(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags 505 | { 506 | if([self isReachableWithFlags:flags]) 507 | { 508 | if(self.reachableBlock) 509 | { 510 | self.reachableBlock(self); 511 | } 512 | } 513 | else 514 | { 515 | if(self.unreachableBlock) 516 | { 517 | self.unreachableBlock(self); 518 | } 519 | } 520 | 521 | // this makes sure the change notification happens on the MAIN THREAD 522 | dispatch_async(dispatch_get_main_queue(), ^{ 523 | [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification 524 | object:self]; 525 | }); 526 | } 527 | 528 | #pragma mark - Debug Description 529 | 530 | - (NSString *) description 531 | { 532 | NSString *description = [NSString stringWithFormat:@"<%@: %#x>", 533 | NSStringFromClass([self class]), (unsigned int) self]; 534 | return description; 535 | } 536 | 537 | 538 | @end 539 | -------------------------------------------------------------------------------- /LocalCache/Util.h: -------------------------------------------------------------------------------- 1 | // 2 | // Util.h 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Util : NSObject 12 | 13 | + (NSString *)sha1:(NSString *)str; 14 | + (NSString *)md5Hash:(NSString *)str; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /LocalCache/Util.m: -------------------------------------------------------------------------------- 1 | // 2 | // Util.m 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import "Util.h" 10 | #import 11 | 12 | @implementation Util 13 | 14 | + (NSString *)sha1:(NSString *)str { 15 | const char *cstr = [str cStringUsingEncoding:NSUTF8StringEncoding]; 16 | NSData *data = [NSData dataWithBytes:cstr length:str.length]; 17 | 18 | uint8_t digest[CC_SHA1_DIGEST_LENGTH]; 19 | 20 | CC_SHA1(data.bytes, data.length, digest); 21 | 22 | NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; 23 | 24 | for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { 25 | [output appendFormat:@"%02x", digest[i]]; 26 | } 27 | 28 | return output; 29 | } 30 | 31 | + (NSString *)md5Hash:(NSString *)str { 32 | const char *cStr = [str UTF8String]; 33 | unsigned char result[16]; 34 | CC_MD5( cStr, strlen(cStr), result ); 35 | NSString *md5Result = [NSString stringWithFormat: 36 | @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 37 | result[0], result[1], result[2], result[3], 38 | result[4], result[5], result[6], result[7], 39 | result[8], result[9], result[10], result[11], 40 | result[12], result[13], result[14], result[15] 41 | ]; 42 | return md5Result; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /LocalCache/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @property(nonatomic, retain) UIWebView *webView; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /LocalCache/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "CustomURLCache.h" 11 | #import "MBProgressHUD.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | @synthesize webView = _webView; 20 | 21 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 22 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 23 | CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024 24 | diskCapacity:200 * 1024 * 1024 25 | diskPath:nil 26 | cacheTime:0]; 27 | [CustomURLCache setSharedURLCache:urlCache]; 28 | [urlCache release]; 29 | } 30 | return self; 31 | } 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | // Test pull request 36 | UILabel * label = [[UILabel alloc]init]; 37 | label.text = @"aaa"; 38 | UIWebView *webView = [[UIWebView alloc] initWithFrame:self.view.frame]; 39 | webView.delegate = self; 40 | self.webView = webView; 41 | [webView release]; 42 | [self.view addSubview:_webView]; 43 | 44 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com/"]]]; 45 | } 46 | 47 | - (void)didReceiveMemoryWarning 48 | { 49 | [super didReceiveMemoryWarning]; 50 | // Dispose of any resources that can be recreated. 51 | 52 | CustomURLCache *urlCache = (CustomURLCache *)[NSURLCache sharedURLCache]; 53 | [urlCache removeAllCachedResponses]; 54 | } 55 | 56 | - (void)dealloc { 57 | [_webView release]; 58 | [super dealloc]; 59 | } 60 | 61 | #pragma mark - webview 62 | 63 | - (void)webViewDidFinishLoad:(UIWebView *)webView { 64 | [MBProgressHUD hideHUDForView:self.view animated:YES]; 65 | } 66 | 67 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 68 | [MBProgressHUD hideHUDForView:self.view animated:YES]; 69 | } 70 | 71 | - (void)webViewDidStartLoad:(UIWebView *)webView { 72 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; 73 | hud.mode = MBProgressHUDModeIndeterminate; 74 | hud.labelText = @"Loading..."; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /LocalCache/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LocalCache/en.lproj/ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /LocalCache/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LocalCache 4 | // 5 | // Created by tan on 13-2-6. 6 | // Copyright (c) 2013年 adways. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIWebViewLocalCache 2 | UIWebView离线缓存Demo 3 | --------------------------------------------------------------------------------