├── README.md └── UIImageView+AsyncLoad ├── ExampleProject ├── DownloadImagesSample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcuserdata │ │ │ └── vgovindswamy.xcuserdatad │ │ │ ├── .dat.nosynccf48.zbjbu9 │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── vgovindswamy.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── DownloadImagesSample.xcscheme │ │ └── xcschememanagement.plist ├── DownloadImagesSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Download_icon-1.png │ │ │ └── Download_icon.png │ │ ├── Contents.json │ │ └── placeHolerImage.imageset │ │ │ ├── Contents.json │ │ │ ├── placeholder.png │ │ │ └── placeholder1.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CollectioViewTransition.swift │ ├── DataManager.swift │ ├── ImageViewController.swift │ ├── ImagesCollectionViewController.swift │ ├── ImagesList.plist │ ├── Info.plist │ ├── Photo.swift │ └── ViewController.swift └── TumbnailCell.swift ├── README.md └── UIImageView+AsyncLoad ├── ImageDownloadManager.swift └── UIImageView+AsyncLoad.swift /README.md: -------------------------------------------------------------------------------- 1 | # UIImageView-AsyncLoad 2 | ======================= 3 | 4 | UIImageView+AsyncLoad is an extension of UIImageView for loading and displaying images asynchronously on iOS. Developer will have a hassle free environment for showing the remote image on imageview without doing much work, more sepcifically not worring about network calls, with out blocking the UI. 5 | 6 | ![solarized vim](https://i.imgsafe.org/19b15a9dea.jpg) 7 | 8 | 9 | Installation 10 | ======================= 11 | 12 | To use the UIImageView+AsyncLoad in an app, just drag the `ImageDownloadManager.swift` & `UIImageView+AsyncLoad.swift` files into your project. 13 | 14 | 15 | Usage 16 | ======================= 17 | 18 | Use the APi `setImageFrom(imageURLString : String, placeHolderImage: UIImage = UIImage(), completionBlock: DownloadHandler?)` on the UIImageView instance, pass the remote image URL to be displayed, check the below snippet for example. 19 | 20 | ![solarized vim](http://i.imgur.com/bhdIa0H.png) 21 | 22 | And "Thats it" 23 | 24 | *Note : Additonaly if you want to pass the placeholder image, just add an argument `placeHolderImage` and pass the image, rest is taken care. 25 | 26 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 87076C3E1E6AD43B00CBCB59 /* Photo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87076C3D1E6AD43B00CBCB59 /* Photo.swift */; }; 11 | 87076C401E6AD57E00CBCB59 /* ImagesList.plist in Resources */ = {isa = PBXBuildFile; fileRef = 87076C3F1E6AD57E00CBCB59 /* ImagesList.plist */; }; 12 | 87076C491E6BCFB000CBCB59 /* CollectioViewTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87076C481E6BCFB000CBCB59 /* CollectioViewTransition.swift */; }; 13 | 87076C4B1E6BD39D00CBCB59 /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87076C4A1E6BD39D00CBCB59 /* ImageViewController.swift */; }; 14 | 87357E811E505ACB00391157 /* ImageDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87357E7F1E505ACB00391157 /* ImageDownloadManager.swift */; }; 15 | 87357E821E505ACB00391157 /* UIImageView+AsyncLoad.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87357E801E505ACB00391157 /* UIImageView+AsyncLoad.swift */; }; 16 | 87551DAF1DF0259500930A88 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87551DAE1DF0259500930A88 /* AppDelegate.swift */; }; 17 | 87551DB41DF0259500930A88 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87551DB21DF0259500930A88 /* Main.storyboard */; }; 18 | 87551DB61DF0259500930A88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 87551DB51DF0259500930A88 /* Assets.xcassets */; }; 19 | 87551DB91DF0259500930A88 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87551DB71DF0259500930A88 /* LaunchScreen.storyboard */; }; 20 | 87551DC11DF0274800930A88 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87551DC01DF0274800930A88 /* DataManager.swift */; }; 21 | 87551DC51DF02DA900930A88 /* TumbnailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87551DC41DF02DA900930A88 /* TumbnailCell.swift */; }; 22 | 87551DC71DF02DEF00930A88 /* ImagesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87551DC61DF02DEF00930A88 /* ImagesCollectionViewController.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 87076C3D1E6AD43B00CBCB59 /* Photo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Photo.swift; sourceTree = ""; }; 27 | 87076C3F1E6AD57E00CBCB59 /* ImagesList.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = ImagesList.plist; sourceTree = ""; }; 28 | 87076C481E6BCFB000CBCB59 /* CollectioViewTransition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectioViewTransition.swift; sourceTree = ""; }; 29 | 87076C4A1E6BD39D00CBCB59 /* ImageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = ""; }; 30 | 87357E7F1E505ACB00391157 /* ImageDownloadManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDownloadManager.swift; sourceTree = ""; }; 31 | 87357E801E505ACB00391157 /* UIImageView+AsyncLoad.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+AsyncLoad.swift"; sourceTree = ""; }; 32 | 87551DAB1DF0259500930A88 /* DownloadImagesSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DownloadImagesSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | 87551DAE1DF0259500930A88 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 34 | 87551DB31DF0259500930A88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 35 | 87551DB51DF0259500930A88 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 87551DB81DF0259500930A88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 37 | 87551DBA1DF0259500930A88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 87551DC01DF0274800930A88 /* DataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = ""; }; 39 | 87551DC41DF02DA900930A88 /* TumbnailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TumbnailCell.swift; path = ../TumbnailCell.swift; sourceTree = ""; }; 40 | 87551DC61DF02DEF00930A88 /* ImagesCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImagesCollectionViewController.swift; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 87551DA81DF0259500930A88 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 87076C411E6BCED200CBCB59 /* AppDelegate */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 87551DAE1DF0259500930A88 /* AppDelegate.swift */, 58 | ); 59 | name = AppDelegate; 60 | sourceTree = ""; 61 | }; 62 | 87076C421E6BCEE500CBCB59 /* ViewControllers */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 87551DC61DF02DEF00930A88 /* ImagesCollectionViewController.swift */, 66 | 87076C4A1E6BD39D00CBCB59 /* ImageViewController.swift */, 67 | ); 68 | name = ViewControllers; 69 | sourceTree = ""; 70 | }; 71 | 87076C441E6BCF0400CBCB59 /* View */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 87551DC41DF02DA900930A88 /* TumbnailCell.swift */, 75 | ); 76 | name = View; 77 | sourceTree = ""; 78 | }; 79 | 87076C461E6BCF4F00CBCB59 /* Model */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | 87076C3D1E6AD43B00CBCB59 /* Photo.swift */, 83 | ); 84 | name = Model; 85 | sourceTree = ""; 86 | }; 87 | 87076C471E6BCF6100CBCB59 /* Presenter */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 87551DC01DF0274800930A88 /* DataManager.swift */, 91 | 87076C481E6BCFB000CBCB59 /* CollectioViewTransition.swift */, 92 | ); 93 | name = Presenter; 94 | sourceTree = ""; 95 | }; 96 | 87357E7E1E505ACB00391157 /* UIImageView+AsyncLoad */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 87357E7F1E505ACB00391157 /* ImageDownloadManager.swift */, 100 | 87357E801E505ACB00391157 /* UIImageView+AsyncLoad.swift */, 101 | ); 102 | name = "UIImageView+AsyncLoad"; 103 | path = "../../UIImageView+AsyncLoad"; 104 | sourceTree = ""; 105 | }; 106 | 87551DA21DF0259500930A88 = { 107 | isa = PBXGroup; 108 | children = ( 109 | 87551DAD1DF0259500930A88 /* DownloadImagesSample */, 110 | 87551DAC1DF0259500930A88 /* Products */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | 87551DAC1DF0259500930A88 /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 87551DAB1DF0259500930A88 /* DownloadImagesSample.app */, 118 | ); 119 | name = Products; 120 | sourceTree = ""; 121 | }; 122 | 87551DAD1DF0259500930A88 /* DownloadImagesSample */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 87357E7E1E505ACB00391157 /* UIImageView+AsyncLoad */, 126 | 87551DB21DF0259500930A88 /* Main.storyboard */, 127 | 87076C471E6BCF6100CBCB59 /* Presenter */, 128 | 87076C461E6BCF4F00CBCB59 /* Model */, 129 | 87076C441E6BCF0400CBCB59 /* View */, 130 | 87076C411E6BCED200CBCB59 /* AppDelegate */, 131 | 87076C421E6BCEE500CBCB59 /* ViewControllers */, 132 | 87551DB51DF0259500930A88 /* Assets.xcassets */, 133 | 87551DB71DF0259500930A88 /* LaunchScreen.storyboard */, 134 | 87551DBA1DF0259500930A88 /* Info.plist */, 135 | 87076C3F1E6AD57E00CBCB59 /* ImagesList.plist */, 136 | ); 137 | path = DownloadImagesSample; 138 | sourceTree = ""; 139 | }; 140 | /* End PBXGroup section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | 87551DAA1DF0259500930A88 /* DownloadImagesSample */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 87551DBD1DF0259500930A88 /* Build configuration list for PBXNativeTarget "DownloadImagesSample" */; 146 | buildPhases = ( 147 | 87551DA71DF0259500930A88 /* Sources */, 148 | 87551DA81DF0259500930A88 /* Frameworks */, 149 | 87551DA91DF0259500930A88 /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = DownloadImagesSample; 156 | productName = DownloadImagesSample; 157 | productReference = 87551DAB1DF0259500930A88 /* DownloadImagesSample.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 87551DA31DF0259500930A88 /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastSwiftUpdateCheck = 0810; 167 | LastUpgradeCheck = 0940; 168 | ORGANIZATIONNAME = "Vinodh Swamy"; 169 | TargetAttributes = { 170 | 87551DAA1DF0259500930A88 = { 171 | CreatedOnToolsVersion = 8.1; 172 | DevelopmentTeam = Q8K276U645; 173 | LastSwiftMigration = 0940; 174 | ProvisioningStyle = Automatic; 175 | }; 176 | }; 177 | }; 178 | buildConfigurationList = 87551DA61DF0259500930A88 /* Build configuration list for PBXProject "DownloadImagesSample" */; 179 | compatibilityVersion = "Xcode 3.2"; 180 | developmentRegion = English; 181 | hasScannedForEncodings = 0; 182 | knownRegions = ( 183 | en, 184 | Base, 185 | ); 186 | mainGroup = 87551DA21DF0259500930A88; 187 | productRefGroup = 87551DAC1DF0259500930A88 /* Products */; 188 | projectDirPath = ""; 189 | projectRoot = ""; 190 | targets = ( 191 | 87551DAA1DF0259500930A88 /* DownloadImagesSample */, 192 | ); 193 | }; 194 | /* End PBXProject section */ 195 | 196 | /* Begin PBXResourcesBuildPhase section */ 197 | 87551DA91DF0259500930A88 /* Resources */ = { 198 | isa = PBXResourcesBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | 87076C401E6AD57E00CBCB59 /* ImagesList.plist in Resources */, 202 | 87551DB91DF0259500930A88 /* LaunchScreen.storyboard in Resources */, 203 | 87551DB61DF0259500930A88 /* Assets.xcassets in Resources */, 204 | 87551DB41DF0259500930A88 /* Main.storyboard in Resources */, 205 | ); 206 | runOnlyForDeploymentPostprocessing = 0; 207 | }; 208 | /* End PBXResourcesBuildPhase section */ 209 | 210 | /* Begin PBXSourcesBuildPhase section */ 211 | 87551DA71DF0259500930A88 /* Sources */ = { 212 | isa = PBXSourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 87076C3E1E6AD43B00CBCB59 /* Photo.swift in Sources */, 216 | 87076C4B1E6BD39D00CBCB59 /* ImageViewController.swift in Sources */, 217 | 87076C491E6BCFB000CBCB59 /* CollectioViewTransition.swift in Sources */, 218 | 87551DC71DF02DEF00930A88 /* ImagesCollectionViewController.swift in Sources */, 219 | 87551DC11DF0274800930A88 /* DataManager.swift in Sources */, 220 | 87551DC51DF02DA900930A88 /* TumbnailCell.swift in Sources */, 221 | 87357E811E505ACB00391157 /* ImageDownloadManager.swift in Sources */, 222 | 87551DAF1DF0259500930A88 /* AppDelegate.swift in Sources */, 223 | 87357E821E505ACB00391157 /* UIImageView+AsyncLoad.swift in Sources */, 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | }; 227 | /* End PBXSourcesBuildPhase section */ 228 | 229 | /* Begin PBXVariantGroup section */ 230 | 87551DB21DF0259500930A88 /* Main.storyboard */ = { 231 | isa = PBXVariantGroup; 232 | children = ( 233 | 87551DB31DF0259500930A88 /* Base */, 234 | ); 235 | name = Main.storyboard; 236 | sourceTree = ""; 237 | }; 238 | 87551DB71DF0259500930A88 /* LaunchScreen.storyboard */ = { 239 | isa = PBXVariantGroup; 240 | children = ( 241 | 87551DB81DF0259500930A88 /* Base */, 242 | ); 243 | name = LaunchScreen.storyboard; 244 | sourceTree = ""; 245 | }; 246 | /* End PBXVariantGroup section */ 247 | 248 | /* Begin XCBuildConfiguration section */ 249 | 87551DBB1DF0259500930A88 /* Debug */ = { 250 | isa = XCBuildConfiguration; 251 | buildSettings = { 252 | ALWAYS_SEARCH_USER_PATHS = NO; 253 | CLANG_ANALYZER_NONNULL = YES; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 274 | CLANG_WARN_STRICT_PROTOTYPES = YES; 275 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 277 | CLANG_WARN_UNREACHABLE_CODE = YES; 278 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 279 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 299 | MTL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 303 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 304 | TARGETED_DEVICE_FAMILY = "1,2"; 305 | }; 306 | name = Debug; 307 | }; 308 | 87551DBC1DF0259500930A88 /* Release */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | ALWAYS_SEARCH_USER_PATHS = NO; 312 | CLANG_ANALYZER_NONNULL = YES; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | VALIDATE_PRODUCT = YES; 357 | }; 358 | name = Release; 359 | }; 360 | 87551DBE1DF0259500930A88 /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | DEVELOPMENT_TEAM = Q8K276U645; 365 | INFOPLIST_FILE = DownloadImagesSample/Info.plist; 366 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 367 | PRODUCT_BUNDLE_IDENTIFIER = mycom.DownloadImagesSample; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 370 | SWIFT_VERSION = 4.0; 371 | }; 372 | name = Debug; 373 | }; 374 | 87551DBF1DF0259500930A88 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 378 | DEVELOPMENT_TEAM = Q8K276U645; 379 | INFOPLIST_FILE = DownloadImagesSample/Info.plist; 380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 381 | PRODUCT_BUNDLE_IDENTIFIER = mycom.DownloadImagesSample; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 384 | SWIFT_VERSION = 4.0; 385 | }; 386 | name = Release; 387 | }; 388 | /* End XCBuildConfiguration section */ 389 | 390 | /* Begin XCConfigurationList section */ 391 | 87551DA61DF0259500930A88 /* Build configuration list for PBXProject "DownloadImagesSample" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 87551DBB1DF0259500930A88 /* Debug */, 395 | 87551DBC1DF0259500930A88 /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | 87551DBD1DF0259500930A88 /* Build configuration list for PBXNativeTarget "DownloadImagesSample" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | 87551DBE1DF0259500930A88 /* Debug */, 404 | 87551DBF1DF0259500930A88 /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | /* End XCConfigurationList section */ 410 | }; 411 | rootObject = 87551DA31DF0259500930A88 /* Project object */; 412 | } 413 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/project.xcworkspace/xcuserdata/vgovindswamy.xcuserdatad/.dat.nosynccf48.zbjbu9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/project.xcworkspace/xcuserdata/vgovindswamy.xcuserdatad/.dat.nosynccf48.zbjbu9 -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/project.xcworkspace/xcuserdata/vgovindswamy.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/project.xcworkspace/xcuserdata/vgovindswamy.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/xcuserdata/vgovindswamy.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 35 | 36 | 50 | 51 | 52 | 53 | 54 | 56 | 68 | 69 | 83 | 84 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/xcuserdata/vgovindswamy.xcuserdatad/xcschemes/DownloadImagesSample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample.xcodeproj/xcuserdata/vgovindswamy.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DownloadImagesSample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 87551DAA1DF0259500930A88 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // 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. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "Download_icon-1.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Download_icon.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "idiom" : "ipad", 77 | "size" : "76x76", 78 | "scale" : "1x" 79 | }, 80 | { 81 | "idiom" : "ipad", 82 | "size" : "76x76", 83 | "scale" : "2x" 84 | }, 85 | { 86 | "idiom" : "ipad", 87 | "size" : "83.5x83.5", 88 | "scale" : "2x" 89 | } 90 | ], 91 | "info" : { 92 | "version" : 1, 93 | "author" : "xcode" 94 | } 95 | } -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/AppIcon.appiconset/Download_icon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/AppIcon.appiconset/Download_icon-1.png -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/AppIcon.appiconset/Download_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/AppIcon.appiconset/Download_icon.png -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/placeHolerImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Screen Shot 2015-10-22 at 10.55.40 AM-1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "placeholder.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "placeholder1.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/placeHolerImage.imageset/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/placeHolerImage.imageset/placeholder.png -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/placeHolerImage.imageset/placeholder1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vinodh-G/UIImageView-AsyncLoad/f0fdaeb5de3b1afddee4f3cc454c8ac34435613c/UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Assets.xcassets/placeHolerImage.imageset/placeholder1.png -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/CollectioViewTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TransitionDelegate.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Govind Swamy on 3/5/17. 6 | // Copyright © 2017 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class CollectionViewTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate { 13 | 14 | var openingFrame: CGRect? 15 | 16 | func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 17 | 18 | let presentationAnimator = PresentationAnimator() 19 | presentationAnimator.openingFrame = openingFrame! 20 | return presentationAnimator 21 | } 22 | 23 | func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?{ 24 | 25 | let dismissAnimator = DismissalAnimator() 26 | dismissAnimator.openingFrame = openingFrame! 27 | return dismissAnimator 28 | } 29 | } 30 | 31 | class PresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning { 32 | 33 | var openingFrame: CGRect? 34 | 35 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 36 | return 0.5 37 | } 38 | 39 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 40 | let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! 41 | let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! 42 | let containerView = transitionContext.containerView 43 | 44 | let animationDuration = self .transitionDuration(using: transitionContext) 45 | 46 | // add blurred background to the view 47 | let fromViewFrame = fromViewController.view.frame 48 | 49 | UIGraphicsBeginImageContext(fromViewFrame.size) 50 | fromViewController.view.drawHierarchy(in: fromViewFrame, afterScreenUpdates: true) 51 | let snapshotImage = UIGraphicsGetImageFromCurrentImageContext() 52 | UIGraphicsEndImageContext() 53 | 54 | let snapshotView = toViewController.view.resizableSnapshotView(from: toViewController.view.frame, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero) 55 | snapshotView?.frame = openingFrame! 56 | containerView.addSubview(snapshotView!) 57 | 58 | toViewController.view.alpha = 0.0 59 | containerView.addSubview(toViewController.view) 60 | 61 | UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: 0.8, initialSpringVelocity: 20.0, options: UIViewAnimationOptions.curveLinear, 62 | animations: { () -> Void in 63 | snapshotView?.frame = fromViewController.view.frame 64 | }, completion: { (finished) -> Void in 65 | snapshotView?.removeFromSuperview() 66 | toViewController.view.alpha = 1.0 67 | 68 | transitionContext.completeTransition(finished) 69 | }) 70 | } 71 | } 72 | 73 | class DismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning { 74 | 75 | var openingFrame: CGRect? 76 | 77 | func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 78 | return 0.5 79 | } 80 | 81 | func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 82 | let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! 83 | _ = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! 84 | let containerView = transitionContext.containerView 85 | 86 | let animationDuration = self .transitionDuration(using: transitionContext) 87 | 88 | let snapshotView = fromViewController.view.resizableSnapshotView(from: fromViewController.view.bounds, afterScreenUpdates: true, withCapInsets: UIEdgeInsets.zero) 89 | containerView.addSubview(snapshotView!) 90 | 91 | fromViewController.view.alpha = 0.0 92 | 93 | UIView.animate(withDuration: animationDuration, animations: { () -> Void in 94 | snapshotView?.frame = self.openingFrame! 95 | snapshotView?.alpha = 1.0 96 | }) { (finished) -> Void in 97 | snapshotView?.removeFromSuperview() 98 | fromViewController.view.removeFromSuperview() 99 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/DataManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataManager.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class DataManager: NSObject { 12 | 13 | public static let shared : DataManager = { 14 | let instance = DataManager() 15 | instance.getPhotos() 16 | return instance 17 | }() 18 | 19 | 20 | var photos: NSMutableArray = [] 21 | 22 | func getPhotos(){ 23 | 24 | let filePath = Bundle.main.path(forResource: "ImagesList.plist", ofType: "") 25 | let photoList : NSArray = NSArray(contentsOfFile: filePath!)! 26 | 27 | for photoDetail in photoList { 28 | let inPhotoDetails = photoDetail as! Dictionary 29 | let photo = Photo(name: inPhotoDetails["Name"]!, description: inPhotoDetails["Description"]!, tumbnailUrl: inPhotoDetails["ThumnailUrl"]!, originalURl: inPhotoDetails["OriginalUrl"]!) 30 | 31 | photos.add(photo) 32 | } 33 | } 34 | 35 | func numberOfPhotos() -> Int{ 36 | return photos.count 37 | } 38 | 39 | func photoAt(index: Int) -> Photo?{ 40 | 41 | var photo: Photo? = nil 42 | if index < photos.count 43 | { 44 | photo = photos[index] as? Photo 45 | } 46 | return photo 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/ImageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewController.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Govind Swamy on 3/5/17. 6 | // Copyright © 2017 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ImageViewController: UIViewController { 12 | 13 | @IBOutlet weak var imageView: UIImageView! 14 | @IBOutlet weak var progressView: UIProgressView! 15 | @IBOutlet weak var descriptionTextView: UITextView! 16 | @IBOutlet weak var descContentView: UIView! 17 | var photo : Photo? 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.progressView.setProgress(0.0, animated: false) 22 | if let inPhoto = photo { 23 | 24 | descriptionTextView.text = inPhoto.description 25 | 26 | imageView.setImageFrom(imageURLString: inPhoto.originalURl, placeHolderImage: nil, progressHandler: { (totalEXpetedBytes, downloadedBytes, error) in 27 | 28 | let progress = Float(downloadedBytes) / Float(totalEXpetedBytes) 29 | 30 | self.progressView.setProgress(progress, animated: true) 31 | 32 | }, completionHandler:{(image, error) in 33 | self.progressView.isHidden = true 34 | }) 35 | } 36 | } 37 | 38 | override func viewDidAppear(_ animated: Bool) { 39 | 40 | UIView.animate(withDuration: 0.3) { 41 | self.descContentView.alpha = 1.0; 42 | } 43 | 44 | super.viewDidAppear(animated) 45 | } 46 | 47 | override func didReceiveMemoryWarning() { 48 | super.didReceiveMemoryWarning() 49 | // Dispose of any resources that can be recreated. 50 | } 51 | 52 | @IBAction func dissmissScreen(_ sender: Any) { 53 | self.dismiss(animated: true, completion: nil) 54 | } 55 | 56 | @IBAction func swipeToDissmiss(_ sender: Any) { 57 | self.dismiss(animated: true, completion: nil) 58 | 59 | } 60 | /* 61 | // MARK: - Navigation 62 | 63 | // In a storyboard-based application, you will often want to do a little preparation before navigation 64 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 65 | // Get the new view controller using segue.destinationViewController. 66 | // Pass the selected object to the new view controller. 67 | } 68 | */ 69 | 70 | } 71 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/ImagesCollectionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImagesCollectionViewController.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let reuseIdentifier = "imageCellId" 12 | 13 | class ImagesCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { 14 | 15 | var dataManager = DataManager.shared 16 | let transitionDelegate: CollectionViewTransitioningDelegate = CollectionViewTransitioningDelegate() 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | 22 | override func didReceiveMemoryWarning() { 23 | super.didReceiveMemoryWarning() 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | // MARK: UICollectionViewDataSource 28 | 29 | override func numberOfSections(in collectionView: UICollectionView) -> Int { 30 | 31 | return 1 32 | } 33 | 34 | 35 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 36 | 37 | return dataManager.numberOfPhotos() 38 | } 39 | 40 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 41 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! TumbnailCell 42 | 43 | 44 | configureCell(cell: cell, forPhoto: dataManager.photoAt(index: indexPath.row)!) 45 | 46 | return cell 47 | } 48 | 49 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 50 | 51 | var cellSize = view.bounds.size 52 | cellSize.width = (view.bounds.size.width - 6) / 3 53 | cellSize.height = view.bounds.size.height / 6 54 | return cellSize 55 | 56 | } 57 | 58 | override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 59 | 60 | let attributes = collectionView.layoutAttributesForItem(at: indexPath as IndexPath) 61 | let attributesFrame = attributes?.frame 62 | let frameToOpenFrom = collectionView.convert(attributesFrame!, to: collectionView.superview) 63 | transitionDelegate.openingFrame = frameToOpenFrom 64 | 65 | let storyBoard = UIStoryboard.init(name: "Main", bundle: nil) 66 | 67 | let imageViewController : ImageViewController = storyBoard.instantiateViewController(withIdentifier: "ImageViewController") as! ImageViewController 68 | imageViewController.photo = dataManager.photoAt(index: indexPath.row)! 69 | imageViewController.modalPresentationStyle = .custom 70 | imageViewController.transitioningDelegate = transitionDelegate 71 | present(imageViewController, 72 | animated: true, 73 | completion: nil) 74 | } 75 | 76 | func configureCell(cell : TumbnailCell, forPhoto:Photo){ 77 | 78 | cell.imageContentView.setImageFrom(imageURLString: forPhoto.tumbnailUrl, placeHolderImage: UIImage(named:"placeHolerImage"), completionHandler: nil) 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/ImagesList.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Name 7 | autumn-forest 8 | Description 9 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 10 | ThumnailUrl 11 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Ffall-leaves.jpg&api_key=&width=640&quality=80&encoding=jpg 12 | OriginalUrl 13 | http://freebigpictures.com/wp-content/uploads/2009/09/fall-leaves.jpg 14 | 15 | 16 | Name 17 | veronica-chamaedrys 18 | Description 19 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 20 | ThumnailUrl 21 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fautumn-walk.jpg&api_key=&width=640&quality=80&encoding=jpg 22 | OriginalUrl 23 | http://freebigpictures.com/wp-content/uploads/2009/09/autumn-walk.jpg 24 | 25 | 26 | Name 27 | autumn-forest 28 | Description 29 | On the city streets I am a walking wallet, a consumer, a citizen. There are rules of conduct any which way you go. I must cross the street at the lights on command, I must wait in line-ups in the stores and be courteous. But here in nature I am just another organism, another animal, albeit one with fancy clothes and a cell phone in my pocket. There's something liberating in that. If I want to I can shout my lungs out, there is not a soul to cast a disapproving glance or whisper doubts as to my sanity. 30 | ThumnailUrl 31 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Ffall-yellow-trees.jpg&api_key=&width=640&quality=80&encoding=jpg 32 | OriginalUrl 33 | http://freebigpictures.com/wp-content/uploads/2009/09/fall-yellow-trees.jpg 34 | 35 | 36 | Name 37 | autumn-forest 38 | Description 39 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 40 | ThumnailUrl 41 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Ffall-season.jpg&api_key=&width=640&quality=80&encoding=jpg 42 | OriginalUrl 43 | http://freebigpictures.com/wp-content/uploads/2009/09/fall-season.jpg 44 | 45 | 46 | Name 47 | veronica-chamaedrys 48 | Description 49 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 50 | ThumnailUrl 51 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fyellow-leaves.jpg&api_key=&width=640&quality=80&encoding=jpg 52 | OriginalUrl 53 | http://freebigpictures.com/wp-content/uploads/2009/09/yellow-leaves.jpg 54 | 55 | 56 | Name 57 | veronica-chamaedrys 58 | Description 59 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 60 | ThumnailUrl 61 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2Fautumn-park.jpg&api_key=&width=640&quality=80&encoding=jpg 62 | OriginalUrl 63 | http://freebigpictures.com/wp-content/uploads/autumn-park.jpg 64 | 65 | 66 | Name 67 | veronica-chamaedrys 68 | Description 69 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 70 | ThumnailUrl 71 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fhawthorn-fruits.jpg&api_key=&width=640&quality=80&encoding=jpg 72 | OriginalUrl 73 | http://freebigpictures.com/wp-content/uploads/2009/09/hawthorn-fruits.jpg 74 | 75 | 76 | Name 77 | rainbow-over-forest 78 | Description 79 | Here the nature is more gay than anything on the moors. Back home the beaten and dishevelled greens of the grass mingles with the brighter hue of the mosses and the dullness of the earth. The trees lie lower to the ground than those in the dense woodlands further off. 80 | ThumnailUrl 81 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fmorning-mist.jpg&api_key=&width=640&quality=80&encoding=jpg 82 | OriginalUrl 83 | http://freebigpictures.com/wp-content/uploads/2009/09/morning-mist.jpg 84 | 85 | 86 | Name 87 | yellow-wildflower 88 | Description 89 | Upon the hill I stand as still as the trees, not totally frozen, for just as their budded twigs move so does my long hair. My eyes are closed. 90 | ThumnailUrl 91 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fgolden-leaves.jpg&api_key=&width=640&quality=80&encoding=jpg 92 | OriginalUrl 93 | http://freebigpictures.com/wp-content/uploads/2009/09/golden-leaves.jpg 94 | 95 | 96 | Name 97 | veronica-chamaedrys 98 | Description 99 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 100 | ThumnailUrl 101 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fyellow-foliage.jpg&api_key=&width=640&quality=80&encoding=jpg 102 | OriginalUrl 103 | http://freebigpictures.com/wp-content/uploads/2009/09/yellow-foliage.jpg 104 | 105 | 106 | Name 107 | veronica-chamaedrys 108 | Description 109 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 110 | ThumnailUrl 111 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2F2009%2F09%2Fyellow-beech.jpg&api_key=&width=640&quality=80&encoding=jpg 112 | OriginalUrl 113 | http://freebigpictures.com/wp-content/uploads/2009/09/yellow-beech.jpg 114 | 115 | 116 | Name 117 | veronica-chamaedrys 118 | Description 119 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 120 | ThumnailUrl 121 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2Fcopper-forest.jpg&api_key=&width=640&quality=80&encoding=jpg 122 | OriginalUrl 123 | http://freebigpictures.com/wp-content/uploads/copper-forest.jpg 124 | 125 | 126 | Name 127 | veronica-chamaedrys 128 | Description 129 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 130 | ThumnailUrl 131 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2Fgolden-blue.jpg&api_key=&width=640&quality=80&encoding=jpg 132 | OriginalUrl 133 | http://freebigpictures.com/wp-content/uploads/golden-blue.jpg 134 | 135 | 136 | Name 137 | veronica-chamaedrys 138 | Description 139 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 140 | ThumnailUrl 141 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2Foctober-forest.jpg&api_key=&width=640&quality=80&encoding=jpg 142 | OriginalUrl 143 | http://freebigpictures.com/wp-content/uploads/october-forest.jpg 144 | 145 | 146 | Name 147 | veronica-chamaedrys 148 | Description 149 | Under the mist that swirls thicker than hairspray in a beauty pageant prep-room lies sand that shifts under the pressure of my boot. I can't see it, only feel. Out there, only meters away is the ocean, alive with constant motion and millions of sea-dwellers. Beyond this wall of white I can smell and hear it. The waves are neither the gentle kind that roll up the beach like a overflowing bath tub, nor the crashing kind that turn murky with golden swirling crystals. 150 | ThumnailUrl 151 | https://api.thumbalizr.com/?url=http%3A%2F%2Ffreebigpictures.com%2Fwp-content%2Fuploads%2Flast-leaves.jpg&api_key=&width=640&quality=80&encoding=jpg 152 | OriginalUrl 153 | http://freebigpictures.com/wp-content/uploads/last-leaves.jpg 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/Photo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Photo.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Govind Swamy on 3/4/17. 6 | // Copyright © 2017 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Photo{ 12 | var name : String 13 | var description : String 14 | var tumbnailUrl : String 15 | var originalURl : String 16 | 17 | } 18 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/DownloadImagesSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | 14 | let imageURLs = DataManager.sharedInstance.getImageURLS() 15 | 16 | override func viewDidLoad() 17 | { 18 | super.viewDidLoad() 19 | } 20 | 21 | override func didReceiveMemoryWarning() { 22 | super.didReceiveMemoryWarning() 23 | // Dispose of any resources that can be recreated. 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/ExampleProject/TumbnailCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageViewCell.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class TumbnailCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var imageContentView: UIImageView! 14 | 15 | override func awakeFromNib() { 16 | super.awakeFromNib() 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/README.md: -------------------------------------------------------------------------------- 1 | # UIImageView-AsyncLoad -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/UIImageView+AsyncLoad/ImageDownloadManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageDownloadManager.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/3/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | struct ImageDownloadInfo { 13 | let downloadURLString: String 14 | let downloadTask: URLSessionTask 15 | let progressHandler: DownloadProgressHandler? 16 | let completionHandler: DownloadHandler 17 | } 18 | 19 | class ImageDownloadManager: NSObject { 20 | 21 | static let shared: ImageDownloadManager = ImageDownloadManager() 22 | 23 | var imageLoaderQueue: [String: ImageDownloadInfo] = [:] 24 | var imageCache: NSCache = NSCache() 25 | 26 | lazy var downloadsSession: URLSession = URLSession(configuration: URLSessionConfiguration.default) 27 | lazy var downloadDelegateSession: URLSession = URLSession(configuration: URLSessionConfiguration.default, delegate: ImageDownloadManager.shared, delegateQueue: OperationQueue.main) 28 | 29 | func getImageFromURL(imageURLString:String, 30 | completionHandler:@escaping DownloadHandler) { 31 | 32 | if let cachedImage = imageCache.object(forKey: imageURLString as NSString) as UIImage? { 33 | completionHandler(cachedImage, nil) 34 | return 35 | } 36 | 37 | downloadImageFor(imageURLString: imageURLString, 38 | downloadHandler: completionHandler) 39 | } 40 | 41 | func getImageFromURL(imageURLString:String, 42 | progessHandler: @escaping DownloadProgressHandler, 43 | completionHandler: @escaping DownloadHandler ){ 44 | if let cachedImage = imageCache.object(forKey: imageURLString as NSString) as UIImage? { 45 | completionHandler(cachedImage, nil) 46 | return 47 | } 48 | 49 | downloadImageFor(imageURLString: imageURLString, 50 | progressHandler: progessHandler, 51 | completionHandler: completionHandler) 52 | } 53 | 54 | private func downloadImageFor(imageURLString:String, 55 | downloadHandler: @escaping DownloadHandler) { 56 | 57 | var imageDownloadInfo: ImageDownloadInfo? = imageLoaderQueue[imageURLString] 58 | 59 | if imageDownloadInfo == nil { 60 | 61 | let imageLoaderTask = downloadsSession.dataTask(with: URL(string: imageURLString)!, completionHandler: { (data : Data?, response : URLResponse?, error : Error?) in 62 | 63 | OperationQueue.main.addOperation({ 64 | 65 | guard let validData = data else { 66 | downloadHandler(nil, error) 67 | return; 68 | } 69 | 70 | guard let image = UIImage(data: validData) else { 71 | downloadHandler(nil, error) 72 | return; 73 | } 74 | 75 | self.imageCache.setObject(image, forKey: imageURLString as NSString) 76 | downloadHandler(image, nil) 77 | self.imageLoaderQueue[imageURLString] = nil 78 | }) 79 | }) 80 | 81 | imageDownloadInfo = ImageDownloadInfo(downloadURLString: imageURLString, 82 | downloadTask: imageLoaderTask, 83 | progressHandler: nil, 84 | completionHandler: downloadHandler) 85 | 86 | imageLoaderQueue[imageURLString] = imageDownloadInfo 87 | imageDownloadInfo?.downloadTask.resume() 88 | } 89 | } 90 | 91 | private func downloadImageFor(imageURLString:String, 92 | progressHandler: @escaping DownloadProgressHandler, 93 | completionHandler: @escaping DownloadHandler) { 94 | 95 | var imageDownloadInfo: ImageDownloadInfo? = imageLoaderQueue[imageURLString] 96 | 97 | if imageDownloadInfo == nil { 98 | 99 | let imageLoaderTask = downloadDelegateSession.downloadTask(with: URL(string: imageURLString)!) 100 | 101 | imageDownloadInfo = ImageDownloadInfo(downloadURLString: imageURLString, 102 | downloadTask: imageLoaderTask, 103 | progressHandler: progressHandler, 104 | completionHandler:completionHandler) 105 | 106 | imageLoaderQueue[imageURLString] = imageDownloadInfo 107 | imageDownloadInfo?.downloadTask.resume() 108 | } 109 | 110 | } 111 | } 112 | 113 | extension ImageDownloadManager : URLSessionDownloadDelegate { 114 | 115 | func urlSession(_ session: URLSession, 116 | downloadTask: URLSessionDownloadTask, 117 | didFinishDownloadingTo location: URL){ 118 | 119 | 120 | guard let imageUrl: String = downloadTask.originalRequest?.url?.absoluteString else { 121 | return 122 | } 123 | 124 | guard let imageDownloadInfo: ImageDownloadInfo = imageLoaderQueue[imageUrl] else { 125 | return 126 | } 127 | do { 128 | let data = try Data(contentsOf: location) 129 | guard let image = UIImage(data: data) else { 130 | let handler = imageDownloadInfo.completionHandler 131 | handler(nil, nil) 132 | return 133 | } 134 | ImageDownloadManager.shared.imageCache.setObject(image, forKey: imageUrl as NSString) 135 | let handler = imageDownloadInfo.completionHandler 136 | handler(image, nil) 137 | 138 | } catch { 139 | let handler = imageDownloadInfo.completionHandler 140 | handler(nil, nil) 141 | } 142 | } 143 | 144 | func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){ 145 | 146 | let imageUrl = downloadTask.originalRequest?.url?.absoluteString 147 | let imageDownloadInfo: ImageDownloadInfo? = imageLoaderQueue[imageUrl!] 148 | 149 | if imageDownloadInfo != nil { 150 | 151 | if let progressHandler = imageDownloadInfo?.progressHandler { 152 | progressHandler(totalBytesExpectedToWrite, totalBytesWritten, nil) 153 | } 154 | } 155 | } 156 | 157 | } 158 | 159 | -------------------------------------------------------------------------------- /UIImageView+AsyncLoad/UIImageView+AsyncLoad/UIImageView+AsyncLoad.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+AsyncLoad.swift 3 | // DownloadImagesSample 4 | // 5 | // Created by Vinodh Swamy on 12/1/16. 6 | // Copyright © 2016 Vinodh Swamy. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | protocol AsyncLoad { 14 | func setImageFrom(imageURLString: String, 15 | placeHolderImage: UIImage?, 16 | completionHandler: DownloadHandler?) 17 | 18 | func setImageFrom(imageURLString : String, 19 | placeHolderImage: UIImage?, 20 | progressHandler: @escaping DownloadProgressHandler, 21 | completionHandler: @escaping DownloadHandler) 22 | } 23 | 24 | typealias DownloadHandler = (_ image: UIImage?, _ error: Error?) -> Void 25 | typealias DownloadProgressHandler = (_ totalBytesExpected : Int64, _ bytesDownloaded: Int64, _ error : Error?) -> Void 26 | 27 | private var kImageURLKey : String = "imageURLKey" 28 | 29 | extension UIImageView: AsyncLoad { 30 | 31 | var imageURLId : String{ 32 | 33 | get{ 34 | return objc_getAssociatedObject(self, &kImageURLKey) as! String 35 | } 36 | set(newValue){ 37 | objc_setAssociatedObject(self, &kImageURLKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) 38 | } 39 | } 40 | 41 | func setImageFrom(imageURLString : String, 42 | placeHolderImage: UIImage? = nil, 43 | completionHandler: DownloadHandler?) { 44 | 45 | guard imageURLString.count > 0 else { 46 | if let handler = completionHandler { 47 | handler(nil, nil) 48 | } 49 | return 50 | } 51 | 52 | if placeHolderImage != nil { 53 | image = placeHolderImage; 54 | } 55 | 56 | imageURLId = imageURLString 57 | ImageDownloadManager.shared.getImageFromURL(imageURLString: imageURLString) { (image : UIImage?, error :Error?) in 58 | 59 | guard let inImage = image else { 60 | if let handler = completionHandler { 61 | handler(nil, error) 62 | } 63 | return 64 | } 65 | 66 | self.updateImage(image: inImage, imageUrl: imageURLString) 67 | if let handler = completionHandler { 68 | handler(inImage, nil); 69 | } 70 | } 71 | } 72 | 73 | func setImageFrom(imageURLString: String, 74 | placeHolderImage: UIImage? = nil, 75 | progressHandler: @escaping DownloadProgressHandler, 76 | completionHandler: @escaping DownloadHandler) { 77 | 78 | guard imageURLString.count > 0 else { 79 | completionHandler(nil, nil) 80 | return 81 | } 82 | 83 | if ((placeHolderImage) != nil){ 84 | self.image = placeHolderImage; 85 | } 86 | self.imageURLId = imageURLString 87 | 88 | ImageDownloadManager.shared.getImageFromURL(imageURLString: imageURLString, 89 | progessHandler: { (expectedBytes: Int64, downloadedBytes: Int64, error: Error?) in 90 | progressHandler(expectedBytes, downloadedBytes, error) 91 | 92 | }) { (image: UIImage?, error: Error?) in 93 | guard let inImage = image else { 94 | completionHandler(nil, nil) 95 | return 96 | } 97 | 98 | self.updateImage(image: inImage, imageUrl: imageURLString) 99 | completionHandler(inImage, error) 100 | } 101 | } 102 | 103 | private func updateImage(image:UIImage, imageUrl:String) { 104 | 105 | if (imageUrl == imageURLId) 106 | { 107 | UIView.transition(with: self, 108 | duration: 0.4, 109 | options: .transitionCrossDissolve, 110 | animations: { 111 | self.image = image; 112 | }, 113 | completion: nil) 114 | } 115 | } 116 | } 117 | --------------------------------------------------------------------------------