diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..98f50f943 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Vendors/libwebp"] + path = Vendors/libwebp +url=https://chromium.googlesource.com/webm/libwebp diff --git a/Examples/CustomPathImages/4ad9ae8eabfec60b40bf48f0bfc2d120 b/Examples/CustomPathImages/4ad9ae8eabfec60b40bf48f0bfc2d120 new file mode 100644 index 000000000..f57c8e1be Binary files /dev/null and b/Examples/CustomPathImages/4ad9ae8eabfec60b40bf48f0bfc2d120 differ diff --git a/Examples/Default-568h@2x.png b/Examples/Default-568h@2x.png new file mode 100644 index 000000000..0891b7aab Binary files /dev/null and b/Examples/Default-568h@2x.png differ diff --git a/Examples/SDWebImage Demo.xcodeproj/project.pbxproj b/Examples/SDWebImage Demo.xcodeproj/project.pbxproj index ca684c89f..682f88d5c 100644 --- a/Examples/SDWebImage Demo.xcodeproj/project.pbxproj +++ b/Examples/SDWebImage Demo.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3E75A9861742DBE700DA412D /* CustomPathImages in Resources */ = {isa = PBXBuildFile; fileRef = 3E75A9851742DBE700DA412D /* CustomPathImages */; }; 531041C1157EAC8F00BBABC3 /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 537612E6155ABA44005750A4 /* ImageIO.framework */; }; 5376129A155AB74D005750A4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53761299155AB74D005750A4 /* UIKit.framework */; }; 5376129C155AB74D005750A4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5376129B155AB74D005750A4 /* Foundation.framework */; }; @@ -17,43 +18,38 @@ 537612B0155AB74D005750A4 /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 537612AF155AB74D005750A4 /* DetailViewController.m */; }; 537612B3155AB74D005750A4 /* MasterViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 537612B1155AB74D005750A4 /* MasterViewController.xib */; }; 537612B6155AB74D005750A4 /* DetailViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 537612B4155AB74D005750A4 /* DetailViewController.xib */; }; - 53980816164349C6003B5191 /* libSDWebImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5398080416434977003B5191 /* libSDWebImage.a */; }; 53A2B50D155B155A00B12423 /* placeholder.png in Resources */ = {isa = PBXBuildFile; fileRef = 53A2B50B155B155A00B12423 /* placeholder.png */; }; 53A2B50E155B155A00B12423 /* placeholder@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 53A2B50C155B155A00B12423 /* placeholder@2x.png */; }; + 53EEC18916484553007601E1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 53EEC18816484553007601E1 /* Default-568h@2x.png */; }; + 53EEC1B2164845E9007601E1 /* libSDWebImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53EEC1AD164845D9007601E1 /* libSDWebImage.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 5398080116434977003B5191 /* PBXContainerItemProxy */ = { + 53EEC1AC164845D9007601E1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 539807FA16434977003B5191 /* SDWebImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 53922D6F148C55820056699D; - remoteInfo = SDWebImage; - }; - 5398080316434977003B5191 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 539807FA16434977003B5191 /* SDWebImage.xcodeproj */; + containerPortal = 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 53761325155AD0D5005750A4; - remoteInfo = "SDWebImage ARC"; + remoteInfo = SDWebImage; }; - 5398080516434977003B5191 /* PBXContainerItemProxy */ = { + 53EEC1AE164845D9007601E1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 539807FA16434977003B5191 /* SDWebImage.xcodeproj */; + containerPortal = 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */; proxyType = 2; remoteGlobalIDString = 531041E0157EAFA400BBABC3; - remoteInfo = "SDWebImage ARC+MKAnnotation"; + remoteInfo = "SDWebImage+MKAnnotation"; }; - 53980814164349BD003B5191 /* PBXContainerItemProxy */ = { + 53EEC1B0164845E3007601E1 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 539807FA16434977003B5191 /* SDWebImage.xcodeproj */; + containerPortal = 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */; proxyType = 1; remoteGlobalIDString = 53761307155AD0D5005750A4; - remoteInfo = "SDWebImage ARC"; + remoteInfo = SDWebImage; }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3E75A9851742DBE700DA412D /* CustomPathImages */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CustomPathImages; sourceTree = SOURCE_ROOT; }; 53761295155AB74D005750A4 /* SDWebImage Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SDWebImage Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 53761299155AB74D005750A4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 5376129B155AB74D005750A4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -71,9 +67,10 @@ 537612B5155AB74D005750A4 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DetailViewController.xib; sourceTree = ""; }; 537612E3155ABA3C005750A4 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 537612E6155ABA44005750A4 /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; }; - 539807FA16434977003B5191 /* SDWebImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDWebImage.xcodeproj; path = ../SDWebImage.xcodeproj; sourceTree = ""; }; 53A2B50B155B155A00B12423 /* placeholder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = placeholder.png; sourceTree = ""; }; 53A2B50C155B155A00B12423 /* placeholder@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "placeholder@2x.png"; sourceTree = ""; }; + 53EEC18816484553007601E1 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDWebImage.xcodeproj; path = ../SDWebImage.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -81,7 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 53980816164349C6003B5191 /* libSDWebImage.a in Frameworks */, + 53EEC1B2164845E9007601E1 /* libSDWebImage.a in Frameworks */, 531041C1157EAC8F00BBABC3 /* ImageIO.framework in Frameworks */, 5376129A155AB74D005750A4 /* UIKit.framework in Frameworks */, 5376129C155AB74D005750A4 /* Foundation.framework in Frameworks */, @@ -112,7 +109,7 @@ 53761298155AB74D005750A4 /* Frameworks */ = { isa = PBXGroup; children = ( - 539807FA16434977003B5191 /* SDWebImage.xcodeproj */, + 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */, 537612E6155ABA44005750A4 /* ImageIO.framework */, 537612E3155ABA3C005750A4 /* MapKit.framework */, 53761299155AB74D005750A4 /* UIKit.framework */, @@ -125,6 +122,7 @@ 5376129F155AB74D005750A4 /* SDWebImage Demo */ = { isa = PBXGroup; children = ( + 3E75A9851742DBE700DA412D /* CustomPathImages */, 537612A8155AB74D005750A4 /* AppDelegate.h */, 537612A9155AB74D005750A4 /* AppDelegate.m */, 537612AB155AB74D005750A4 /* MasterViewController.h */, @@ -141,6 +139,7 @@ 537612A0155AB74D005750A4 /* Supporting Files */ = { isa = PBXGroup; children = ( + 53EEC18816484553007601E1 /* Default-568h@2x.png */, 537612A1155AB74D005750A4 /* SDWebImage Demo-Info.plist */, 537612A5155AB74D005750A4 /* main.m */, 537612A7155AB74D005750A4 /* SDWebImage Demo-Prefix.pch */, @@ -150,12 +149,11 @@ name = "Supporting Files"; sourceTree = ""; }; - 539807FB16434977003B5191 /* Products */ = { + 53EEC1A4164845D8007601E1 /* Products */ = { isa = PBXGroup; children = ( - 5398080216434977003B5191 /* libSDWebImage.a */, - 5398080416434977003B5191 /* libSDWebImage.a */, - 5398080616434977003B5191 /* libSDWebImageARC+MKAnnotation.a */, + 53EEC1AD164845D9007601E1 /* libSDWebImage.a */, + 53EEC1AF164845D9007601E1 /* libSDWebImage+MKAnnotation.a */, ); name = Products; sourceTree = ""; @@ -174,7 +172,7 @@ buildRules = ( ); dependencies = ( - 53980815164349BD003B5191 /* PBXTargetDependency */, + 53EEC1B1164845E3007601E1 /* PBXTargetDependency */, ); name = "SDWebImage Demo"; productName = "SDWebImage Demo"; @@ -202,8 +200,8 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 539807FB16434977003B5191 /* Products */; - ProjectRef = 539807FA16434977003B5191 /* SDWebImage.xcodeproj */; + ProductGroup = 53EEC1A4164845D8007601E1 /* Products */; + ProjectRef = 53EEC1A3164845D8007601E1 /* SDWebImage.xcodeproj */; }, ); projectRoot = ""; @@ -214,25 +212,18 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 5398080216434977003B5191 /* libSDWebImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libSDWebImage.a; - remoteRef = 5398080116434977003B5191 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 5398080416434977003B5191 /* libSDWebImage.a */ = { + 53EEC1AD164845D9007601E1 /* libSDWebImage.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; path = libSDWebImage.a; - remoteRef = 5398080316434977003B5191 /* PBXContainerItemProxy */; + remoteRef = 53EEC1AC164845D9007601E1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 5398080616434977003B5191 /* libSDWebImageARC+MKAnnotation.a */ = { + 53EEC1AF164845D9007601E1 /* libSDWebImage+MKAnnotation.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libSDWebImageARC+MKAnnotation.a"; - remoteRef = 5398080516434977003B5191 /* PBXContainerItemProxy */; + path = "libSDWebImage+MKAnnotation.a"; + remoteRef = 53EEC1AE164845D9007601E1 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXReferenceProxy section */ @@ -246,6 +237,8 @@ 537612B6155AB74D005750A4 /* DetailViewController.xib in Resources */, 53A2B50D155B155A00B12423 /* placeholder.png in Resources */, 53A2B50E155B155A00B12423 /* placeholder@2x.png in Resources */, + 53EEC18916484553007601E1 /* Default-568h@2x.png in Resources */, + 3E75A9861742DBE700DA412D /* CustomPathImages in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -266,10 +259,10 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 53980815164349BD003B5191 /* PBXTargetDependency */ = { + 53EEC1B1164845E3007601E1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - name = "SDWebImage ARC"; - targetProxy = 53980814164349BD003B5191 /* PBXContainerItemProxy */; + name = SDWebImage; + targetProxy = 53EEC1B0164845E3007601E1 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -313,7 +306,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; SDKROOT = iphoneos; }; name = Debug; @@ -331,7 +324,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/Examples/SDWebImage Demo/AppDelegate.m b/Examples/SDWebImage Demo/AppDelegate.m index 721d4ac34..3e5f51e08 100644 --- a/Examples/SDWebImage Demo/AppDelegate.m +++ b/Examples/SDWebImage Demo/AppDelegate.m @@ -10,6 +10,8 @@ #import "MasterViewController.h" +#import + @implementation AppDelegate @synthesize window = _window; @@ -17,6 +19,10 @@ @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + //Add a custom read-only cache path + NSString *bundledPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CustomPathImages"]; + [[SDImageCache sharedImageCache] addReadOnlyCachePath:bundledPath]; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. diff --git a/Examples/SDWebImage Demo/DetailViewController.m b/Examples/SDWebImage Demo/DetailViewController.m index 4d6423b5c..18952975f 100644 --- a/Examples/SDWebImage Demo/DetailViewController.m +++ b/Examples/SDWebImage Demo/DetailViewController.m @@ -33,7 +33,22 @@ - (void)configureView { if (self.imageURL) { - [self.imageView setImageWithURL:self.imageURL placeholderImage:nil options:SDWebImageProgressiveDownload]; + __block UIActivityIndicatorView *activityIndicator; + __weak UIImageView *weakImageView = self.imageView; + [self.imageView setImageWithURL:self.imageURL placeholderImage:nil options:SDWebImageProgressiveDownload progress:^(NSUInteger receivedSize, long long expectedSize) + { + if (!activityIndicator) + { + [weakImageView addSubview:activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]]; + activityIndicator.center = weakImageView.center; + [activityIndicator startAnimating]; + } + } + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) + { + [activityIndicator removeFromSuperview]; + activityIndicator = nil; + }]; } } diff --git a/Examples/SDWebImage Demo/MasterViewController.m b/Examples/SDWebImage Demo/MasterViewController.m index 0ed86250f..2eab33643 100644 --- a/Examples/SDWebImage Demo/MasterViewController.m +++ b/Examples/SDWebImage Demo/MasterViewController.m @@ -25,8 +25,14 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil if (self) { self.title = @"SDWebImage"; + self.navigationItem.rightBarButtonItem = [UIBarButtonItem.alloc initWithTitle:@"Clear Cache" + style:UIBarButtonItemStylePlain + target:self + action:@selector(flushCache)]; _objects = [NSArray arrayWithObjects: - @"http://static2.dmcdn.net/static/video/451/838/44838154:jpeg_preview_small.jpg?20120509163826", + @"http://assets.sbnation.com/assets/2512203/dogflops.gif", + @"http://www.ioncannon.net/wp-content/uploads/2011/06/test2.webp", + @"http://www.ioncannon.net/wp-content/uploads/2011/06/test9.webp", @"http://static2.dmcdn.net/static/video/656/177/44771656:jpeg_preview_small.jpg?20120509154705", @"http://static2.dmcdn.net/static/video/629/228/44822926:jpeg_preview_small.jpg?20120509181018", @"http://static2.dmcdn.net/static/video/116/367/44763611:jpeg_preview_small.jpg?20120509101749", @@ -126,10 +132,218 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil @"http://static2.dmcdn.net/static/video/406/148/43841604:jpeg_preview_small.jpg?20120416123145", @"http://static2.dmcdn.net/static/video/463/885/43588364:jpeg_preview_small.jpg?20120409130206", @"http://static2.dmcdn.net/static/video/176/845/38548671:jpeg_preview_small.jpg?20120414200742", + @"http://static2.dmcdn.net/static/video/447/848/51848744:jpeg_preview_small.jpg?20121105223446", + @"http://static2.dmcdn.net/static/video/337/848/51848733:jpeg_preview_small.jpg?20121105223433", + @"http://static2.dmcdn.net/static/video/707/848/51848707:jpeg_preview_small.jpg?20121105223428", + @"http://static2.dmcdn.net/static/video/102/848/51848201:jpeg_preview_small.jpg?20121105223411", + @"http://static2.dmcdn.net/static/video/817/848/51848718:jpeg_preview_small.jpg?20121105223402", + @"http://static2.dmcdn.net/static/video/007/848/51848700:jpeg_preview_small.jpg?20121105223345", + @"http://static2.dmcdn.net/static/video/696/848/51848696:jpeg_preview_small.jpg?20121105223355", + @"http://static2.dmcdn.net/static/video/296/848/51848692:jpeg_preview_small.jpg?20121105223337", + @"http://static2.dmcdn.net/static/video/080/848/51848080:jpeg_preview_small.jpg?20121105223653", + @"http://static2.dmcdn.net/static/video/386/848/51848683:jpeg_preview_small.jpg?20121105223343", + @"http://static2.dmcdn.net/static/video/876/848/51848678:jpeg_preview_small.jpg?20121105223301", + @"http://static2.dmcdn.net/static/video/866/848/51848668:jpeg_preview_small.jpg?20121105223244", + @"http://static2.dmcdn.net/static/video/572/548/51845275:jpeg_preview_small.jpg?20121105223229", + @"http://static2.dmcdn.net/static/video/972/548/51845279:jpeg_preview_small.jpg?20121105223227", + @"http://static2.dmcdn.net/static/video/112/548/51845211:jpeg_preview_small.jpg?20121105223226", + @"http://static2.dmcdn.net/static/video/549/448/51844945:jpeg_preview_small.jpg?20121105223223", + @"http://static2.dmcdn.net/static/video/166/848/51848661:jpeg_preview_small.jpg?20121105223228", + @"http://static2.dmcdn.net/static/video/856/848/51848658:jpeg_preview_small.jpg?20121105223223", + @"http://static2.dmcdn.net/static/video/746/848/51848647:jpeg_preview_small.jpg?20121105223204", + @"http://static2.dmcdn.net/static/video/446/848/51848644:jpeg_preview_small.jpg?20121105223204", + @"http://static2.dmcdn.net/static/video/726/848/51848627:jpeg_preview_small.jpg?20121105223221", + @"http://static2.dmcdn.net/static/video/436/848/51848634:jpeg_preview_small.jpg?20121105223445", + @"http://static2.dmcdn.net/static/video/836/848/51848638:jpeg_preview_small.jpg?20121105223144", + @"http://static2.dmcdn.net/static/video/036/848/51848630:jpeg_preview_small.jpg?20121105223125", + @"http://static2.dmcdn.net/static/video/026/848/51848620:jpeg_preview_small.jpg?20121105223102", + @"http://static2.dmcdn.net/static/video/895/848/51848598:jpeg_preview_small.jpg?20121105223112", + @"http://static2.dmcdn.net/static/video/116/848/51848611:jpeg_preview_small.jpg?20121105223052", + @"http://static2.dmcdn.net/static/video/006/848/51848600:jpeg_preview_small.jpg?20121105223043", + @"http://static2.dmcdn.net/static/video/432/548/51845234:jpeg_preview_small.jpg?20121105223022", + @"http://static2.dmcdn.net/static/video/785/848/51848587:jpeg_preview_small.jpg?20121105223031", + @"http://static2.dmcdn.net/static/video/975/848/51848579:jpeg_preview_small.jpg?20121105223012", + @"http://static2.dmcdn.net/static/video/965/848/51848569:jpeg_preview_small.jpg?20121105222952", + @"http://static2.dmcdn.net/static/video/365/848/51848563:jpeg_preview_small.jpg?20121105222943", + @"http://static2.dmcdn.net/static/video/755/848/51848557:jpeg_preview_small.jpg?20121105222943", + @"http://static2.dmcdn.net/static/video/722/248/51842227:jpeg_preview_small.jpg?20121105222908", + @"http://static2.dmcdn.net/static/video/155/848/51848551:jpeg_preview_small.jpg?20121105222913", + @"http://static2.dmcdn.net/static/video/345/848/51848543:jpeg_preview_small.jpg?20121105222907", + @"http://static2.dmcdn.net/static/video/535/848/51848535:jpeg_preview_small.jpg?20121105222848", + @"http://static2.dmcdn.net/static/video/035/848/51848530:jpeg_preview_small.jpg?20121105222837", + @"http://static2.dmcdn.net/static/video/525/848/51848525:jpeg_preview_small.jpg?20121105222826", + @"http://static2.dmcdn.net/static/video/233/848/51848332:jpeg_preview_small.jpg?20121105223414", + @"http://static2.dmcdn.net/static/video/125/848/51848521:jpeg_preview_small.jpg?20121105222809", + @"http://static2.dmcdn.net/static/video/005/848/51848500:jpeg_preview_small.jpg?20121105222802", + @"http://static2.dmcdn.net/static/video/015/848/51848510:jpeg_preview_small.jpg?20121105222755", + @"http://static2.dmcdn.net/static/video/121/548/51845121:jpeg_preview_small.jpg?20121105222850", + @"http://static2.dmcdn.net/static/video/205/848/51848502:jpeg_preview_small.jpg?20121105222737", + @"http://static2.dmcdn.net/static/video/697/448/51844796:jpeg_preview_small.jpg?20121105222818", + @"http://static2.dmcdn.net/static/video/494/848/51848494:jpeg_preview_small.jpg?20121105222724", + @"http://static2.dmcdn.net/static/video/806/448/51844608:jpeg_preview_small.jpg?20121105222811", + @"http://static2.dmcdn.net/static/video/729/348/51843927:jpeg_preview_small.jpg?20121105222805", + @"http://static2.dmcdn.net/static/video/865/148/51841568:jpeg_preview_small.jpg?20121105222803", + @"http://static2.dmcdn.net/static/video/481/548/51845184:jpeg_preview_small.jpg?20121105222700", + @"http://static2.dmcdn.net/static/video/190/548/51845091:jpeg_preview_small.jpg?20121105222656", + @"http://static2.dmcdn.net/static/video/128/448/51844821:jpeg_preview_small.jpg?20121105222656", + @"http://static2.dmcdn.net/static/video/784/848/51848487:jpeg_preview_small.jpg?20121105222704", + @"http://static2.dmcdn.net/static/video/182/448/51844281:jpeg_preview_small.jpg?20121105222652", + @"http://static2.dmcdn.net/static/video/801/848/51848108:jpeg_preview_small.jpg?20121105222907", + @"http://static2.dmcdn.net/static/video/974/848/51848479:jpeg_preview_small.jpg?20121105222657", + @"http://static2.dmcdn.net/static/video/274/848/51848472:jpeg_preview_small.jpg?20121105222644", + @"http://static2.dmcdn.net/static/video/954/848/51848459:jpeg_preview_small.jpg?20121105222637", + @"http://static2.dmcdn.net/static/video/554/848/51848455:jpeg_preview_small.jpg?20121105222615", + @"http://static2.dmcdn.net/static/video/944/848/51848449:jpeg_preview_small.jpg?20121105222558", + @"http://static2.dmcdn.net/static/video/144/848/51848441:jpeg_preview_small.jpg?20121105222556", + @"http://static2.dmcdn.net/static/video/134/848/51848431:jpeg_preview_small.jpg?20121105222539", + @"http://static2.dmcdn.net/static/video/624/848/51848426:jpeg_preview_small.jpg?20121105222523", + @"http://static2.dmcdn.net/static/video/281/448/51844182:jpeg_preview_small.jpg?20121105222502", + @"http://static2.dmcdn.net/static/video/414/848/51848414:jpeg_preview_small.jpg?20121105222516", + @"http://static2.dmcdn.net/static/video/171/848/51848171:jpeg_preview_small.jpg?20121105223449", + @"http://static2.dmcdn.net/static/video/904/848/51848409:jpeg_preview_small.jpg?20121105222514", + @"http://static2.dmcdn.net/static/video/004/848/51848400:jpeg_preview_small.jpg?20121105222443", + @"http://static2.dmcdn.net/static/video/693/848/51848396:jpeg_preview_small.jpg?20121105222439", + @"http://static2.dmcdn.net/static/video/401/848/51848104:jpeg_preview_small.jpg?20121105222832", + @"http://static2.dmcdn.net/static/video/957/648/51846759:jpeg_preview_small.jpg?20121105223109", + @"http://static2.dmcdn.net/static/video/603/848/51848306:jpeg_preview_small.jpg?20121105222324", + @"http://static2.dmcdn.net/static/video/990/848/51848099:jpeg_preview_small.jpg?20121105222807", + @"http://static2.dmcdn.net/static/video/929/448/51844929:jpeg_preview_small.jpg?20121105222216", + @"http://static2.dmcdn.net/static/video/320/548/51845023:jpeg_preview_small.jpg?20121105222214", + @"http://static2.dmcdn.net/static/video/387/448/51844783:jpeg_preview_small.jpg?20121105222212", + @"http://static2.dmcdn.net/static/video/833/248/51842338:jpeg_preview_small.jpg?20121105222209", + @"http://static2.dmcdn.net/static/video/993/348/51843399:jpeg_preview_small.jpg?20121105222012", + @"http://static2.dmcdn.net/static/video/660/848/51848066:jpeg_preview_small.jpg?20121105222754", + @"http://static2.dmcdn.net/static/video/471/848/51848174:jpeg_preview_small.jpg?20121105223419", + @"http://static2.dmcdn.net/static/video/255/748/51847552:jpeg_preview_small.jpg?20121105222420", + @"http://static2.dmcdn.net/static/video/257/448/51844752:jpeg_preview_small.jpg?20121105221728", + @"http://static2.dmcdn.net/static/video/090/848/51848090:jpeg_preview_small.jpg?20121105223020", + @"http://static2.dmcdn.net/static/video/807/448/51844708:jpeg_preview_small.jpg?20121105221654", + @"http://static2.dmcdn.net/static/video/196/448/51844691:jpeg_preview_small.jpg?20121105222110", + @"http://static2.dmcdn.net/static/video/406/448/51844604:jpeg_preview_small.jpg?20121105221647", + @"http://static2.dmcdn.net/static/video/865/348/51843568:jpeg_preview_small.jpg?20121105221644", + @"http://static2.dmcdn.net/static/video/932/448/51844239:jpeg_preview_small.jpg?20121105221309", + @"http://static2.dmcdn.net/static/video/967/438/51834769:jpeg_preview_small.jpg?20121105221020", + @"http://static2.dmcdn.net/static/video/362/348/51843263:jpeg_preview_small.jpg?20121105220855", + @"http://static2.dmcdn.net/static/video/777/448/51844777:jpeg_preview_small.jpg?20121105220715", + @"http://static2.dmcdn.net/static/video/567/348/51843765:jpeg_preview_small.jpg?20121105220708", + @"http://static2.dmcdn.net/static/video/905/248/51842509:jpeg_preview_small.jpg?20121105220640", + @"http://static2.dmcdn.net/static/video/857/748/51847758:jpeg_preview_small.jpg?20121105221003", + @"http://static2.dmcdn.net/static/video/578/348/51843875:jpeg_preview_small.jpg?20121105220350", + @"http://static2.dmcdn.net/static/video/214/448/51844412:jpeg_preview_small.jpg?20121105220415", + @"http://static2.dmcdn.net/static/video/264/748/51847462:jpeg_preview_small.jpg?20121105220635", + @"http://static2.dmcdn.net/static/video/817/748/51847718:jpeg_preview_small.jpg?20121105220233", + @"http://static2.dmcdn.net/static/video/784/848/51848487:jpeg_preview_small.jpg?20121105222704", + @"http://static2.dmcdn.net/static/video/182/448/51844281:jpeg_preview_small.jpg?20121105222652", + @"http://static2.dmcdn.net/static/video/801/848/51848108:jpeg_preview_small.jpg?20121105222907", + @"http://static2.dmcdn.net/static/video/974/848/51848479:jpeg_preview_small.jpg?20121105222657", + @"http://static2.dmcdn.net/static/video/274/848/51848472:jpeg_preview_small.jpg?20121105222644", + @"http://static2.dmcdn.net/static/video/954/848/51848459:jpeg_preview_small.jpg?20121105222637", + @"http://static2.dmcdn.net/static/video/554/848/51848455:jpeg_preview_small.jpg?20121105222615", + @"http://static2.dmcdn.net/static/video/944/848/51848449:jpeg_preview_small.jpg?20121105222558", + @"http://static2.dmcdn.net/static/video/144/848/51848441:jpeg_preview_small.jpg?20121105222556", + @"http://static2.dmcdn.net/static/video/134/848/51848431:jpeg_preview_small.jpg?20121105222539", + @"http://static2.dmcdn.net/static/video/624/848/51848426:jpeg_preview_small.jpg?20121105222523", + @"http://static2.dmcdn.net/static/video/281/448/51844182:jpeg_preview_small.jpg?20121105222502", + @"http://static2.dmcdn.net/static/video/414/848/51848414:jpeg_preview_small.jpg?20121105222516", + @"http://static2.dmcdn.net/static/video/171/848/51848171:jpeg_preview_small.jpg?20121105223449", + @"http://static2.dmcdn.net/static/video/904/848/51848409:jpeg_preview_small.jpg?20121105222514", + @"http://static2.dmcdn.net/static/video/004/848/51848400:jpeg_preview_small.jpg?20121105222443", + @"http://static2.dmcdn.net/static/video/693/848/51848396:jpeg_preview_small.jpg?20121105222439", + @"http://static2.dmcdn.net/static/video/401/848/51848104:jpeg_preview_small.jpg?20121105222832", + @"http://static2.dmcdn.net/static/video/957/648/51846759:jpeg_preview_small.jpg?20121105223109", + @"http://static2.dmcdn.net/static/video/603/848/51848306:jpeg_preview_small.jpg?20121105222324", + @"http://static2.dmcdn.net/static/video/990/848/51848099:jpeg_preview_small.jpg?20121105222807", + @"http://static2.dmcdn.net/static/video/929/448/51844929:jpeg_preview_small.jpg?20121105222216", + @"http://static2.dmcdn.net/static/video/320/548/51845023:jpeg_preview_small.jpg?20121105222214", + @"http://static2.dmcdn.net/static/video/387/448/51844783:jpeg_preview_small.jpg?20121105222212", + @"http://static2.dmcdn.net/static/video/833/248/51842338:jpeg_preview_small.jpg?20121105222209", + @"http://static2.dmcdn.net/static/video/993/348/51843399:jpeg_preview_small.jpg?20121105222012", + @"http://static2.dmcdn.net/static/video/660/848/51848066:jpeg_preview_small.jpg?20121105222754", + @"http://static2.dmcdn.net/static/video/471/848/51848174:jpeg_preview_small.jpg?20121105223419", + @"http://static2.dmcdn.net/static/video/255/748/51847552:jpeg_preview_small.jpg?20121105222420", + @"http://static2.dmcdn.net/static/video/257/448/51844752:jpeg_preview_small.jpg?20121105221728", + @"http://static2.dmcdn.net/static/video/090/848/51848090:jpeg_preview_small.jpg?20121105224514", + @"http://static2.dmcdn.net/static/video/807/448/51844708:jpeg_preview_small.jpg?20121105221654", + @"http://static2.dmcdn.net/static/video/196/448/51844691:jpeg_preview_small.jpg?20121105222110", + @"http://static2.dmcdn.net/static/video/406/448/51844604:jpeg_preview_small.jpg?20121105221647", + @"http://static2.dmcdn.net/static/video/865/348/51843568:jpeg_preview_small.jpg?20121105221644", + @"http://static2.dmcdn.net/static/video/932/448/51844239:jpeg_preview_small.jpg?20121105221309", + @"http://static2.dmcdn.net/static/video/967/438/51834769:jpeg_preview_small.jpg?20121105221020", + @"http://static2.dmcdn.net/static/video/362/348/51843263:jpeg_preview_small.jpg?20121105220855", + @"http://static2.dmcdn.net/static/video/777/448/51844777:jpeg_preview_small.jpg?20121105220715", + @"http://static2.dmcdn.net/static/video/567/348/51843765:jpeg_preview_small.jpg?20121105220708", + @"http://static2.dmcdn.net/static/video/905/248/51842509:jpeg_preview_small.jpg?20121105220640", + @"http://static2.dmcdn.net/static/video/857/748/51847758:jpeg_preview_small.jpg?20121105221003", + @"http://static2.dmcdn.net/static/video/578/348/51843875:jpeg_preview_small.jpg?20121105220350", + @"http://static2.dmcdn.net/static/video/214/448/51844412:jpeg_preview_small.jpg?20121105220415", + @"http://static2.dmcdn.net/static/video/264/748/51847462:jpeg_preview_small.jpg?20121105220635", + @"http://static2.dmcdn.net/static/video/817/748/51847718:jpeg_preview_small.jpg?20121105220233", + @"http://static2.dmcdn.net/static/video/807/748/51847708:jpeg_preview_small.jpg?20121105220241", + @"http://static2.dmcdn.net/static/video/199/838/51838991:jpeg_preview_small.jpg?20121105220605", + @"http://static2.dmcdn.net/static/video/776/748/51847677:jpeg_preview_small.jpg?20121105220150", + @"http://static2.dmcdn.net/static/video/986/748/51847689:jpeg_preview_small.jpg?20121105224514", + @"http://static2.dmcdn.net/static/video/915/748/51847519:jpeg_preview_small.jpg?20121105222216", + @"http://static2.dmcdn.net/static/video/983/448/51844389:jpeg_preview_small.jpg?20121105220116", + @"http://static2.dmcdn.net/static/video/751/348/51843157:jpeg_preview_small.jpg?20121105220104", + @"http://static2.dmcdn.net/static/video/192/538/51835291:jpeg_preview_small.jpg?20121105220103", + @"http://static2.dmcdn.net/static/video/596/448/51844695:jpeg_preview_small.jpg?20121105220033", + @"http://static2.dmcdn.net/static/video/750/648/51846057:jpeg_preview_small.jpg?20121105221259", + @"http://static2.dmcdn.net/static/video/441/148/51841144:jpeg_preview_small.jpg?20121105215911", + @"http://static2.dmcdn.net/static/video/860/448/51844068:jpeg_preview_small.jpg?20121105215905", + @"http://static2.dmcdn.net/static/video/995/748/51847599:jpeg_preview_small.jpg?20121105215939", + @"http://static2.dmcdn.net/static/video/774/748/51847477:jpeg_preview_small.jpg?20121105223414", + @"http://static2.dmcdn.net/static/video/498/648/51846894:jpeg_preview_small.jpg?20121105221807", + @"http://static2.dmcdn.net/static/video/011/748/51847110:jpeg_preview_small.jpg?20121105221118", + @"http://static2.dmcdn.net/static/video/794/748/51847497:jpeg_preview_small.jpg?20121105220829", + @"http://static2.dmcdn.net/static/video/988/648/51846889:jpeg_preview_small.jpg?20121105222149", + @"http://static2.dmcdn.net/static/video/769/548/51845967:jpeg_preview_small.jpg?20121105215601", + @"http://static2.dmcdn.net/static/video/225/448/51844522:jpeg_preview_small.jpg?20121105215552", + @"http://static2.dmcdn.net/static/video/172/308/51803271:jpeg_preview_small.jpg?20121105215455", + @"http://static2.dmcdn.net/static/video/994/748/51847499:jpeg_preview_small.jpg?20121105220343", + @"http://static2.dmcdn.net/static/video/852/748/51847258:jpeg_preview_small.jpg?20121105221031", + @"http://static2.dmcdn.net/static/video/671/838/51838176:jpeg_preview_small.jpg?20121105215421", + @"http://static2.dmcdn.net/static/video/172/448/51844271:jpeg_preview_small.jpg?20121105215420", + @"http://static2.dmcdn.net/static/video/735/448/51844537:jpeg_preview_small.jpg?20121105215437", + @"http://static2.dmcdn.net/static/video/834/448/51844438:jpeg_preview_small.jpg?20121105215431", + @"http://static2.dmcdn.net/static/video/613/448/51844316:jpeg_preview_small.jpg?20121105215431", + @"http://static2.dmcdn.net/static/video/581/748/51847185:jpeg_preview_small.jpg?20121105220637", + @"http://static2.dmcdn.net/static/video/407/648/51846704:jpeg_preview_small.jpg?20121105220316", + @"http://static2.dmcdn.net/static/video/460/448/51844064:jpeg_preview_small.jpg?20121105215245", + @"http://static2.dmcdn.net/static/video/298/648/51846892:jpeg_preview_small.jpg?20121105220953", + @"http://static2.dmcdn.net/static/video/053/748/51847350:jpeg_preview_small.jpg?20121105221113", + @"http://static2.dmcdn.net/static/video/996/448/51844699:jpeg_preview_small.jpg?20121105222807", + @"http://static2.dmcdn.net/static/video/451/448/51844154:jpeg_preview_small.jpg?20121105221955", + @"http://static2.dmcdn.net/static/video/049/648/51846940:jpeg_preview_small.jpg?20121105215910", + @"http://static2.dmcdn.net/static/video/091/748/51847190:jpeg_preview_small.jpg?20121105215617", + @"http://static2.dmcdn.net/static/video/573/748/51847375:jpeg_preview_small.jpg?20121105223420", + @"http://static2.dmcdn.net/static/video/103/248/51842301:jpeg_preview_small.jpg?20121105215014", + @"http://static2.dmcdn.net/static/video/991/548/51845199:jpeg_preview_small.jpg?20121105215407", + @"http://static2.dmcdn.net/static/video/872/648/51846278:jpeg_preview_small.jpg?20121105220635", + @"http://static2.dmcdn.net/static/video/813/748/51847318:jpeg_preview_small.jpg?20121105214729", + @"http://static2.dmcdn.net/static/video/153/448/51844351:jpeg_preview_small.jpg?20121105214622", + @"http://static2.dmcdn.net/static/video/328/648/51846823:jpeg_preview_small.jpg?20121105214944", + @"http://static2.dmcdn.net/static/video/892/748/51847298:jpeg_preview_small.jpg?20121105224514", + @"http://static2.dmcdn.net/static/video/640/048/51840046:jpeg_preview_small.jpg?20121105214430", + @"http://static2.dmcdn.net/static/video/153/648/51846351:jpeg_preview_small.jpg?20121105214426", + @"http://static2.dmcdn.net/static/video/769/248/51842967:jpeg_preview_small.jpg?20121105214255", + @"http://static2.dmcdn.net/static/video/720/448/51844027:jpeg_preview_small.jpg?20121105214248", + @"http://static2.dmcdn.net/static/video/895/048/51840598:jpeg_preview_small.jpg?20121105214234", + @"http://static2.dmcdn.net/static/video/893/348/51843398:jpeg_preview_small.jpg?20121105214157", + @"http://static2.dmcdn.net/static/video/351/748/51847153:jpeg_preview_small.jpg?20121105214106", + @"http://static2.dmcdn.net/static/video/364/648/51846463:jpeg_preview_small.jpg?20121105215005", + @"http://static2.dmcdn.net/static/video/269/938/51839962:jpeg_preview_small.jpg?20121105214014", nil]; } + [SDWebImageManager.sharedManager.imageDownloader setValue:@"SDWebImage Demo" forHTTPHeaderField:@"AppName"]; + SDWebImageManager.sharedManager.imageDownloader.executionOrder = SDWebImageDownloaderLIFOExecutionOrder; return self; } + +- (void)flushCache +{ + [SDWebImageManager.sharedManager.imageCache clearMemory]; + [SDWebImageManager.sharedManager.imageCache clearDisk]; +} - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { @@ -159,8 +373,9 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } cell.textLabel.text = [NSString stringWithFormat:@"Image #%d", indexPath.row]; + cell.imageView.contentMode = UIViewContentModeScaleAspectFill; [cell.imageView setImageWithURL:[NSURL URLWithString:[_objects objectAtIndex:indexPath.row]] - placeholderImage:[UIImage imageNamed:@"placeholder"]]; + placeholderImage:[UIImage imageNamed:@"placeholder"] options:indexPath.row == 0 ? SDWebImageRefreshCached : 0]; return cell; } diff --git a/README.md b/README.md index 0469ab25e..864bb77e4 100644 --- a/README.md +++ b/README.md @@ -8,55 +8,27 @@ It provides: - An UIImageView category adding web image and cache management to the Cocoa Touch framework - An asynchronous image downloader - An asynchronous memory + disk image caching with automatic cache expiration handling +- Animated GIF support +- WebP format support +- A background image decompression - A guarantee that the same URL won't be downloaded several times - A guarantee that bogus URLs won't be retried again and again +- A guarantee that main thread will never be blocked - Performances! +- Use GCD and ARC -Motivation +NOTE: The version 3.0 of SDWebImage isn't fully backward compatible with 2.0 and requires iOS 5.0 +minimum deployement version. If you need iOS < 5.0 support, please use the last [2.0 version](https://github.com/rs/SDWebImage/tree/2.0-compat). + +[How is SDWebImage better than X?](https://github.com/rs/SDWebImage/wiki/How-is-SDWebImage-better-than-X%3F) + +Who Use It ---------- -As a dummy Objective-C developer working on my first iPhone application for my company -([Dailymotion][]), I've been very frustrated by the lack of support in the Cocoa Touch framework for -UITableView with remote images. After some Googling, I found lot of forums and blogs coming up with -their solution, most of the time based on asynchronous usage with NSURLConnection, but none provided -a simple library doing the work of async image grabbing + caching for you. - -Actually there is one in the famous [Three20][] framework by [Joe Hewitt][], but it's a massive -and undocumented piece of code. You can't import just the the libraries you want without taking the -whole framework (damn #import "TTGlobal.h"). Anyway, the [Three20][] implementation is based on -NSURLConnection, and I soon discovered this solution wasn't ideal. Keep reading to find out why. - -As a hurried beginner in iPhone development, I couldn't attempt to implement my own async image -grabber with caching support as my first steps in this new world. Thus, I asked for help from my good -friend Sebastien Flory ([Fraggle][]), who was working on his great iPhone game ([Urban Rivals][], a -future app-store hit) for almost a year. He spent quite an amount of time implementing the very -same solution for his needs, and was kind enough to give me his implementation for my own use. This -worked quite well and allowed me to concentrate on other parts of my application. But when I started -to compare my application with its direct competitor - the built-in Youtube application - I was very -unhappy with the loading speed of the images. After some network sniffing, I found that every HTTP -requests for my images was 10 times slower than Youtube's... On my own network, Youtube was 10 -time faster than my own servers... WTF?? - -In fact, my servers were fine but a lot of latency was added to the requests, certainly because my -application wasn't responsive enough to handle the requests at full speed. Right then, I -understood something important, asynchronous NSURLConnections are tied to the main runloop in the -NSEventTrackingRunLoopMode. As explained in the documentation, this runloop mode is affected by -UI events: - -> Cocoa uses this mode to restrict incoming events during mouse-dragging loops and other sorts of -> user interface tracking loops. - -A simple test to recognize an application using NSURLConnection in its default mode to load -remote images is to scroll the UITableView with your finger to disclose an unloaded image, and to -keep your finger pressed on the screen. If the image doesn't load until you release you finger, -you've got one (try with the Facebook app for instance). It took me quite some time to understand -the reason for this lagging issue. Actually I first used NSOperation to workaround this issue. - -This technique combined with an image cache instantly gave a lot of responsiveness to my app. -I thought this library could benefit other Cocoa Touch applications so I open-sourced it. - -How To Use It -------------- +Find out [who use SDWebImage](https://github.com/rs/SDWebImage/wiki/Who-Use-SDWebImage) and add your app to the list. + +How To Use +---------- API documentation is available at [http://hackemist.com/SDWebImage/doc/](http://hackemist.com/SDWebImage/doc/) @@ -94,15 +66,14 @@ handled for you, from async downloads to caching management. ### Using blocks -If your project's deployement target is set to iOS 4+, you may want to use the success/failure blocks to be -notified when image have been retrieved from cache. +With blocks, you can be notified about the image download progress and whenever the image retrival +has completed with success or not: + ```objective-c // Here we use the new provided setImageWithURL: method to load the web image [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder.png"] - success:^(UIImage *image, BOOL cached) {... success code here ...} - failure:^(NSError *error) {... failure code here ...}]; -]; + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {... completion code here ...}]; ``` Note: neither your success nor failure block will be call if your image request is canceled before completion. @@ -118,29 +89,43 @@ Here is a simple example of how to use SDWebImageManager: ```objective-c SDWebImageManager *manager = [SDWebImageManager sharedManager]; [manager downloadWithURL:imageURL - delegate:self options:0 - success:^(UIImage *image, BOOL cached) + progress:^(NSUInteger receivedSize, long long expectedSize) { - // do something with image + // progression tracking code } - failure:nil]; + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) + { + if (image) + { + // do something with image + } + }]; ``` ### Using Asynchronous Image Downloader Independently -It is possible to use the async image downloader independently. You just have to create an instance -of SDWebImageDownloader using its convenience constructor downloaderWithURL:delegate:. +It's also possible to use the async image downloader independently: + ```objective-c -downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self]; +[SDWebImageDownloader.sharedDownloader downloadImageWithURL:imageURL + options:0 + progress:^(NSUInteger receivedSize, long long expectedSize) + { + // progression tracking code + } + completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) + { + if (image && finished) + { + // do something with image + } + }]; ``` -The download will start immediately and the imageDownloader:didFinishWithImage: method from the -SDWebImageDownloaderDelegate protocol will be called as soon as the download of image is completed. - ### Using Asynchronous Image Caching Independently -It is also possible to use the NSOperation based image cache store independently. SDImageCache +It is also possible to use the aync based image cache store independently. SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed asynchronous so it doesn't add unnecessary latency to the UI. @@ -153,12 +138,15 @@ key is an application unique identifier for the image to cache. It is generally the image. ```objective-c -UIImage *myCachedImage = [[SDImageCache sharedImageCache] imageFromKey:myCacheKey]; +SDImageCache *imageCache = [SDImageCache.alloc initWithNamespace:@"myNamespace"]; +[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) +{ + // image is not nil if image was found +}]; ``` By default SDImageCache will lookup the disk cache if an image can't be found in the memory cache. -You can prevent this from happening by calling the alternative method imageFromKey:fromDisk: with a -negative second argument. +You can prevent this from happening by calling the alternative method `imageFromMemoryCacheForKey:`. To store an image into the cache, you use the storeImage:forKey: method: @@ -182,11 +170,11 @@ the URL before to use it as a cache key: ```objective-c - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) + SDWebImageManager.sharedManager.cacheKeyFilter:^(NSURL *url) { url = [[[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path] autorelease]; return [url absoluteString]; - }]; + }; // Your app init code... return YES; @@ -205,12 +193,22 @@ The following article gives a way to workaround this issue: [http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/](http://www.wrichards.com/blog/2011/11/sdwebimage-fixed-width-cell-images/) -Automatic Reference Counting (ARC) ----------------------------------- -You can use either style in your Cocoa project. SDWebImage Will figure out which you are using at compile -time and do the right thing. +### Handle image refresh + +SDWebImage does very aggressive caching by default. It ignores all kind of caching control header returned by the HTTP server and cache the returned images with no time restriction. It implies your images URLs are static URLs pointing to images that never change. If the pointed image happen to change, some parts of the URL should change accordingly. +If you don't control the image server you're using, you may not be able to change the URL when its content is updated. This is the case for Facebook avatar URLs for instance. In such case, you may use the `SDWebImageRefreshCached` flag. This will slightly degrade the performance but will respect the HTTP caching control headers: + +``` objective-c +[imageView setImageWithURL:[NSURL URLWithString:@"https://graph.facebook.com/olivier.poitrey/picture"] + placeholderImage:[UIImage imageNamed:@"avatar-placeholder.png"] + options:SDWebImageRefreshCached]; +``` + +### Add a progress indicator + +See this category: https://github.com/JJSaccolo/UIActivityIndicator-for-SDWebImage Installation ------------ @@ -219,7 +217,7 @@ There are two ways to use this in your project: copy all the files into your pro ### Add the SDWebImage project to your project -- Download and unzip the last version of the framework from the [download page](https://github.com/rs/SDWebImage/downloads) +- Download and unzip the last version of the framework from the [download page](https://github.com/rs/SDWebImage/releases) - Right-click on the project navigator and select "Add Files to "Your Project": - In the dialog, select SDWebImage.framework: - Check the "Copy items into destination group's folder (if needed)" checkbox @@ -253,9 +251,6 @@ Future Enhancements - LRU memory cache cleanup instead of reset on memory warning -[Dailymotion]: http://www.dailymotion.com -[Fraggle]: http://fraggle.squarespace.com -[Urban Rivals]: http://fraggle.squarespace.com/blog/2009/9/15/almost-done-here-is-urban-rivals-iphone-trailer.html -[Three20]: http://groups.google.com/group/three20 -[Joe Hewitt]: http://www.joehewitt.com -[tutorial]: http://blog.carbonfive.com/2011/04/04/using-open-source-static-libraries-in-xcode-4 +## Licenses + +All source code is licensed under the [MIT License](https://raw.github.com/rs/SDWebImage/master/LICENSE). diff --git a/SDWebImage.podspec b/SDWebImage.podspec index f4de3522c..6e5381410 100644 --- a/SDWebImage.podspec +++ b/SDWebImage.podspec @@ -1,27 +1,41 @@ Pod::Spec.new do |s| - s.name = 'SDWebImage' - s.version = '2.7' - s.platform = :ios - s.license = 'MIT' - s.summary = 'Asynchronous image downloader with cache support with an UIImageView category.' + s.name = 'SDWebImage' + s.version = '3.4' + s.platform = :ios, '5.0' + s.license = 'MIT' + s.summary = 'Asynchronous image downloader with cache support with an UIImageView category.' s.homepage = 'https://github.com/rs/SDWebImage' - s.author = { 'Olivier Poitrey' => 'rs@dailymotion.com' } - s.source = { :git => 'https://github.com/rs/SDWebImage.git', :tag => '2.7' } + s.author = { 'Olivier Poitrey' => 'rs@dailymotion.com' } + s.source = { :git => 'https://github.com/rs/SDWebImage.git', :tag => '3.4' } - s.description = 'This library provides a category for UIImageVIew with support for remote ' \ - 'images coming from the web. It provides an UIImageView category adding web ' \ - 'image and cache management to the Cocoa Touch framework, an asynchronous ' \ - 'image downloader, an asynchronous memory + disk image caching with automatic ' \ - 'cache expiration handling, a guarantee that the same URL won\'t be downloaded ' \ - 'several times, a guarantee that bogus URLs won\'t be retried again and again, ' \ - 'and performances!' + s.description = 'This library provides a category for UIImageView with support for remote ' \ + 'images coming from the web. It provides an UIImageView category adding web ' \ + 'image and cache management to the Cocoa Touch framework, an asynchronous ' \ + 'image downloader, an asynchronous memory + disk image caching with automatic ' \ + 'cache expiration handling, a guarantee that the same URL won\'t be downloaded ' \ + 'several times, a guarantee that bogus URLs won\'t be retried again and again, ' \ + 'and performances!' - s.source_files = 'SDWebImage/{MK,SD,UI}*.{h,m}' + s.requires_arc = true s.framework = 'ImageIO' + + s.default_subspec = 'Core' - # TODO currently CocoaPods always tries to install the subspec even if the dependency is on just 'SDWebImage' - #s.subspec 'MapKit' do - #s.source_files = 'MKAnnotationView+WebCache.*' - #s.framework = 'MapKit' - #end + s.subspec 'Core' do |core| + core.source_files = 'SDWebImage/{NS,SD,UI}*.{h,m}' + core.exclude_files = 'SDWebImage/UIImage+WebP.{h,m}' + end + + s.subspec 'MapKit' do |mk| + mk.source_files = 'SDWebImage/MKAnnotationView+WebCache.*' + mk.framework = 'MapKit' + mk.dependency 'SDWebImage/Core' + end + + s.subspec 'WebP' do |webp| + webp.source_files = 'SDWebImage/UIImage+WebP.{h,m}' + webp.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SD_WEBP=1' } + webp.dependency 'SDWebImage/Core' + webp.dependency 'libwebp' + end end diff --git a/SDWebImage.xcodeproj/project.pbxproj b/SDWebImage.xcodeproj/project.pbxproj index 66a7784b8..704859dd3 100644 --- a/SDWebImage.xcodeproj/project.pbxproj +++ b/SDWebImage.xcodeproj/project.pbxproj @@ -22,6 +22,12 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E71646388E002868E7 /* SDWebImageOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 530E49E916464C26002868E7 /* SDWebImageOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E71646388E002868E7 /* SDWebImageOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 530E49EB16464C7F002868E7 /* SDWebImageDownloaderOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */; }; + 530E49ED16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */; }; 531041C4157EAFA400BBABC3 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; }; 531041C5157EAFA400BBABC3 /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8A148C56230056699D /* SDWebImageDecoder.m */; }; 531041C6157EAFA400BBABC3 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; }; @@ -34,17 +40,15 @@ 531041CE157EAFA400BBABC3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; }; 531041CF157EAFA400BBABC3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; }; 531041D1157EAFA400BBABC3 /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 531041D2157EAFA400BBABC3 /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041D3157EAFA400BBABC3 /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041D4157EAFA400BBABC3 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041D5157EAFA400BBABC3 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 531041D6157EAFA400BBABC3 /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041D7157EAFA400BBABC3 /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 531041D8157EAFA400BBABC3 /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041D9157EAFA400BBABC3 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041DA157EAFA400BBABC3 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041DB157EAFA400BBABC3 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 531041DC157EAFA400BBABC3 /* MKAnnotationView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 53406750167780C40042B59E /* SDWebImageCompat.m in Sources */ = {isa = PBXBuildFile; fileRef = 5340674F167780C40042B59E /* SDWebImageCompat.m */; }; 53761309155AD0D5005750A4 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; }; 5376130A155AD0D5005750A4 /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8A148C56230056699D /* SDWebImageDecoder.m */; }; 5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; }; @@ -56,37 +60,109 @@ 53761313155AD0D5005750A4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; }; 53761314155AD0D5005750A4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; }; 53761316155AD0D5005750A4 /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53761317155AD0D5005750A4 /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53761318155AD0D5005750A4 /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5376131B155AD0D5005750A4 /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5376131D155AD0D5005750A4 /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; 53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D73148C55820056699D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53922D72148C55820056699D /* Foundation.framework */; }; - 53922D97148C56230056699D /* SDImageCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D85148C56230056699D /* SDImageCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D98148C56230056699D /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; }; - 53922D99148C56230056699D /* SDImageCacheDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D87148C56230056699D /* SDImageCacheDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D9A148C56230056699D /* SDWebImageCompat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D88148C56230056699D /* SDWebImageCompat.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D9B148C56230056699D /* SDWebImageDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D89148C56230056699D /* SDWebImageDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D9C148C56230056699D /* SDWebImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8A148C56230056699D /* SDWebImageDecoder.m */; }; - 53922D9D148C56230056699D /* SDWebImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8B148C56230056699D /* SDWebImageDownloader.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922D9E148C56230056699D /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; }; - 53922D9F148C56230056699D /* SDWebImageDownloaderDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA0148C56230056699D /* SDWebImageManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D8E148C56230056699D /* SDWebImageManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA1148C56230056699D /* SDWebImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8F148C56230056699D /* SDWebImageManager.m */; }; - 53922DA2148C56230056699D /* SDWebImageManagerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA3148C56230056699D /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA4148C56230056699D /* SDWebImagePrefetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D92148C56230056699D /* SDWebImagePrefetcher.m */; }; - 53922DA5148C56230056699D /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA6148C56230056699D /* UIButton+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D94148C56230056699D /* UIButton+WebCache.m */; }; - 53922DA7148C56230056699D /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 53922DA8148C56230056699D /* UIImageView+WebCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D96148C56230056699D /* UIImageView+WebCache.m */; }; - 53FB894714D35E820020B787 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */; }; - 53FB894914D35E9E0020B787 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53FB894814D35E9E0020B787 /* UIKit.framework */; }; + 53E481DD17C300F6003E8957 /* alpha_processing.c in Sources */ = {isa = PBXBuildFile; fileRef = 53E481DB17C300F6003E8957 /* alpha_processing.c */; }; + 53E481DE17C300F6003E8957 /* alpha_processing.c in Sources */ = {isa = PBXBuildFile; fileRef = 53E481DB17C300F6003E8957 /* alpha_processing.c */; }; + 53E481DF17C300F6003E8957 /* alpha_processing.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E481DC17C300F6003E8957 /* alpha_processing.h */; }; + 53E481E017C300F6003E8957 /* alpha_processing.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E481DC17C300F6003E8957 /* alpha_processing.h */; }; + 53EDFB8A17623F7C00698166 /* UIImage+MultiFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB8817623F7C00698166 /* UIImage+MultiFormat.h */; }; + 53EDFB8B17623F7C00698166 /* UIImage+MultiFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB8817623F7C00698166 /* UIImage+MultiFormat.h */; }; + 53EDFB8C17623F7C00698166 /* UIImage+MultiFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFB8917623F7C00698166 /* UIImage+MultiFormat.m */; }; + 53EDFB8D17623F7C00698166 /* UIImage+MultiFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFB8917623F7C00698166 /* UIImage+MultiFormat.m */; }; + 53EDFB931762547D00698166 /* UIImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB911762547C00698166 /* UIImage+WebP.h */; }; + 53EDFB941762547D00698166 /* UIImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFB911762547C00698166 /* UIImage+WebP.h */; }; + 53EDFB951762547D00698166 /* UIImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFB921762547C00698166 /* UIImage+WebP.m */; }; + 53EDFB961762547D00698166 /* UIImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFB921762547C00698166 /* UIImage+WebP.m */; }; + 53EDFC9217625BE300698166 /* webpi.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9017625BE300698166 /* webpi.h */; }; + 53EDFC9317625BE300698166 /* webpi.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9017625BE300698166 /* webpi.h */; }; + 53EDFC9417625BE300698166 /* webp.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9117625BE300698166 /* webp.c */; }; + 53EDFC9517625BE300698166 /* webp.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9117625BE300698166 /* webp.c */; }; + 53EDFC9A17625C1100698166 /* vp8li.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9617625C1100698166 /* vp8li.h */; }; + 53EDFC9B17625C1100698166 /* vp8li.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9617625C1100698166 /* vp8li.h */; }; + 53EDFC9C17625C1100698166 /* vp8l.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9717625C1100698166 /* vp8l.c */; }; + 53EDFC9D17625C1100698166 /* vp8l.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9717625C1100698166 /* vp8l.c */; }; + 53EDFC9E17625C1100698166 /* vp8i.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9817625C1100698166 /* vp8i.h */; }; + 53EDFC9F17625C1100698166 /* vp8i.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFC9817625C1100698166 /* vp8i.h */; }; + 53EDFCA017625C1100698166 /* vp8.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9917625C1100698166 /* vp8.c */; }; + 53EDFCA117625C1100698166 /* vp8.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFC9917625C1100698166 /* vp8.c */; }; + 53EDFCA617625C5B00698166 /* huffman.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCA417625C5B00698166 /* huffman.h */; }; + 53EDFCA717625C5B00698166 /* huffman.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCA417625C5B00698166 /* huffman.h */; }; + 53EDFCA817625C5B00698166 /* huffman.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCA517625C5B00698166 /* huffman.c */; }; + 53EDFCA917625C5B00698166 /* huffman.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCA517625C5B00698166 /* huffman.c */; }; + 53EDFCAB17625C8700698166 /* frame.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCAA17625C8700698166 /* frame.c */; }; + 53EDFCAC17625C8700698166 /* frame.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCAA17625C8700698166 /* frame.c */; }; + 53EDFCAE17625CA600698166 /* alpha.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCAD17625CA600698166 /* alpha.c */; }; + 53EDFCAF17625CA600698166 /* alpha.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCAD17625CA600698166 /* alpha.c */; }; + 53EDFCB217625CD800698166 /* quant_levels_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCB017625CD800698166 /* quant_levels_dec.h */; }; + 53EDFCB317625CD800698166 /* quant_levels_dec.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCB017625CD800698166 /* quant_levels_dec.h */; }; + 53EDFCB417625CD800698166 /* quant_levels_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCB117625CD800698166 /* quant_levels_dec.c */; }; + 53EDFCB517625CD800698166 /* quant_levels_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCB117625CD800698166 /* quant_levels_dec.c */; }; + 53EDFCB917625D1900698166 /* dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCB717625D1900698166 /* dsp.h */; }; + 53EDFCBA17625D1900698166 /* dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCB717625D1900698166 /* dsp.h */; }; + 53EDFCBB17625D1900698166 /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCB817625D1900698166 /* dec.c */; }; + 53EDFCBC17625D1900698166 /* dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCB817625D1900698166 /* dec.c */; }; + 53EDFCBE17625D3F00698166 /* dec_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCBD17625D3F00698166 /* dec_sse2.c */; }; + 53EDFCBF17625D3F00698166 /* dec_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCBD17625D3F00698166 /* dec_sse2.c */; }; + 53EDFCC117625D8400698166 /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCC017625D8400698166 /* cpu.c */; }; + 53EDFCC217625D8400698166 /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCC017625D8400698166 /* cpu.c */; }; + 53EDFCC517625DB200698166 /* bit_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCC317625DB200698166 /* bit_reader.h */; }; + 53EDFCC617625DB200698166 /* bit_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCC317625DB200698166 /* bit_reader.h */; }; + 53EDFCC717625DB200698166 /* bit_reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCC417625DB200698166 /* bit_reader.c */; }; + 53EDFCC817625DB200698166 /* bit_reader.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCC417625DB200698166 /* bit_reader.c */; }; + 53EDFCCB17625DD700698166 /* color_cache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCC917625DD700698166 /* color_cache.h */; }; + 53EDFCCC17625DD700698166 /* color_cache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCC917625DD700698166 /* color_cache.h */; }; + 53EDFCCD17625DD700698166 /* color_cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCCA17625DD700698166 /* color_cache.c */; }; + 53EDFCCE17625DD700698166 /* color_cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCCA17625DD700698166 /* color_cache.c */; }; + 53EDFCD117625DFA00698166 /* lossless.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCCF17625DFA00698166 /* lossless.h */; }; + 53EDFCD217625DFA00698166 /* lossless.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCCF17625DFA00698166 /* lossless.h */; }; + 53EDFCD317625DFA00698166 /* lossless.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD017625DFA00698166 /* lossless.c */; }; + 53EDFCD417625DFA00698166 /* lossless.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD017625DFA00698166 /* lossless.c */; }; + 53EDFCD617625E1A00698166 /* tree.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD517625E1A00698166 /* tree.c */; }; + 53EDFCD717625E1A00698166 /* tree.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD517625E1A00698166 /* tree.c */; }; + 53EDFCD917625E3000698166 /* quant.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD817625E3000698166 /* quant.c */; }; + 53EDFCDA17625E3000698166 /* quant.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCD817625E3000698166 /* quant.c */; }; + 53EDFCDC17625E6000698166 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCDB17625E6000698166 /* buffer.c */; }; + 53EDFCDD17625E6000698166 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCDB17625E6000698166 /* buffer.c */; }; + 53EDFCDF17625E8D00698166 /* upsampling.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCDE17625E8D00698166 /* upsampling.c */; }; + 53EDFCE017625E8D00698166 /* upsampling.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCDE17625E8D00698166 /* upsampling.c */; }; + 53EDFCE317625EB100698166 /* yuv.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCE117625EB100698166 /* yuv.h */; }; + 53EDFCE417625EB100698166 /* yuv.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCE117625EB100698166 /* yuv.h */; }; + 53EDFCE517625EB100698166 /* yuv.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCE217625EB100698166 /* yuv.c */; }; + 53EDFCE617625EB100698166 /* yuv.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCE217625EB100698166 /* yuv.c */; }; + 53EDFCE817625EC800698166 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCE717625EC800698166 /* io.c */; }; + 53EDFCE917625EC800698166 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCE717625EC800698166 /* io.c */; }; + 53EDFCEB17625EE200698166 /* upsampling_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCEA17625EE200698166 /* upsampling_sse2.c */; }; + 53EDFCEC17625EE200698166 /* upsampling_sse2.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCEA17625EE200698166 /* upsampling_sse2.c */; }; + 53EDFCEF17625F1D00698166 /* rescaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCED17625F1D00698166 /* rescaler.h */; }; + 53EDFCF017625F1D00698166 /* rescaler.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCED17625F1D00698166 /* rescaler.h */; }; + 53EDFCF117625F1D00698166 /* rescaler.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCEE17625F1D00698166 /* rescaler.c */; }; + 53EDFCF217625F1D00698166 /* rescaler.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCEE17625F1D00698166 /* rescaler.c */; }; + 53EDFCF517625F4100698166 /* utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCF317625F4100698166 /* utils.h */; }; + 53EDFCF617625F4100698166 /* utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCF317625F4100698166 /* utils.h */; }; + 53EDFCF717625F4100698166 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCF417625F4100698166 /* utils.c */; }; + 53EDFCF817625F4100698166 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCF417625F4100698166 /* utils.c */; }; + 53EDFCFB17625F5F00698166 /* filters.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCF917625F5F00698166 /* filters.h */; }; + 53EDFCFC17625F5F00698166 /* filters.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCF917625F5F00698166 /* filters.h */; }; + 53EDFCFD17625F5F00698166 /* filters.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCFA17625F5F00698166 /* filters.c */; }; + 53EDFCFE17625F5F00698166 /* filters.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFCFA17625F5F00698166 /* filters.c */; }; + 53EDFD0117625F7900698166 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCFF17625F7900698166 /* thread.h */; }; + 53EDFD0217625F7900698166 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EDFCFF17625F7900698166 /* thread.h */; }; + 53EDFD0317625F7900698166 /* thread.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFD0017625F7900698166 /* thread.c */; }; + 53EDFD0417625F7900698166 /* thread.c in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFD0017625F7900698166 /* thread.c */; }; + A18A6CC7172DC28500419892 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; }; + A18A6CC8172DC28500419892 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; }; + A18A6CC9172DC28500419892 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CC6172DC28500419892 /* UIImage+GIF.m */; }; + A18A6CCA172DC28500419892 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CC6172DC28500419892 /* UIImage+GIF.m */; }; + A18A6CCD172DC33A00419892 /* NSData+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CCB172DC33A00419892 /* NSData+GIF.h */; }; + A18A6CCE172DC33A00419892 /* NSData+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CCB172DC33A00419892 /* NSData+GIF.h */; }; + A18A6CCF172DC33A00419892 /* NSData+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CCC172DC33A00419892 /* NSData+GIF.m */; }; + A18A6CD0172DC33A00419892 /* NSData+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CCC172DC33A00419892 /* NSData+GIF.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -100,32 +176,80 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImageARC+MKAnnotation.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDWebImageDownloaderOperation.h; sourceTree = ""; }; + 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDownloaderOperation.m; sourceTree = ""; }; + 530E49E71646388E002868E7 /* SDWebImageOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageOperation.h; sourceTree = ""; }; + 531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImage+MKAnnotation.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5340674F167780C40042B59E /* SDWebImageCompat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCompat.m; sourceTree = ""; }; 535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MKAnnotationView+WebCache.h"; path = "SDWebImage/MKAnnotationView+WebCache.h"; sourceTree = SOURCE_ROOT; }; 535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MKAnnotationView+WebCache.m"; path = "SDWebImage/MKAnnotationView+WebCache.m"; sourceTree = SOURCE_ROOT; }; 53761325155AD0D5005750A4 /* libSDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDWebImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 53922D6F148C55820056699D /* libSDWebImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSDWebImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 53922D72148C55820056699D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 53922D85148C56230056699D /* SDImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageCache.h; path = SDWebImage/SDImageCache.h; sourceTree = SOURCE_ROOT; }; 53922D86148C56230056699D /* SDImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDImageCache.m; path = SDWebImage/SDImageCache.m; sourceTree = SOURCE_ROOT; }; - 53922D87148C56230056699D /* SDImageCacheDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDImageCacheDelegate.h; path = SDWebImage/SDImageCacheDelegate.h; sourceTree = SOURCE_ROOT; }; 53922D88148C56230056699D /* SDWebImageCompat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageCompat.h; path = SDWebImage/SDWebImageCompat.h; sourceTree = SOURCE_ROOT; }; 53922D89148C56230056699D /* SDWebImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDecoder.h; path = SDWebImage/SDWebImageDecoder.h; sourceTree = SOURCE_ROOT; }; 53922D8A148C56230056699D /* SDWebImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDecoder.m; path = SDWebImage/SDWebImageDecoder.m; sourceTree = SOURCE_ROOT; }; 53922D8B148C56230056699D /* SDWebImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloader.h; path = SDWebImage/SDWebImageDownloader.h; sourceTree = SOURCE_ROOT; }; 53922D8C148C56230056699D /* SDWebImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageDownloader.m; path = SDWebImage/SDWebImageDownloader.m; sourceTree = SOURCE_ROOT; }; - 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageDownloaderDelegate.h; path = SDWebImage/SDWebImageDownloaderDelegate.h; sourceTree = SOURCE_ROOT; }; 53922D8E148C56230056699D /* SDWebImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageManager.h; path = SDWebImage/SDWebImageManager.h; sourceTree = SOURCE_ROOT; }; 53922D8F148C56230056699D /* SDWebImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImageManager.m; path = SDWebImage/SDWebImageManager.m; sourceTree = SOURCE_ROOT; }; - 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImageManagerDelegate.h; path = SDWebImage/SDWebImageManagerDelegate.h; sourceTree = SOURCE_ROOT; }; 53922D91148C56230056699D /* SDWebImagePrefetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDWebImagePrefetcher.h; path = SDWebImage/SDWebImagePrefetcher.h; sourceTree = SOURCE_ROOT; }; 53922D92148C56230056699D /* SDWebImagePrefetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDWebImagePrefetcher.m; path = SDWebImage/SDWebImagePrefetcher.m; sourceTree = SOURCE_ROOT; }; 53922D93148C56230056699D /* UIButton+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIButton+WebCache.h"; path = "SDWebImage/UIButton+WebCache.h"; sourceTree = SOURCE_ROOT; }; 53922D94148C56230056699D /* UIButton+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIButton+WebCache.m"; path = "SDWebImage/UIButton+WebCache.m"; sourceTree = SOURCE_ROOT; }; 53922D95148C56230056699D /* UIImageView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImageView+WebCache.h"; path = "SDWebImage/UIImageView+WebCache.h"; sourceTree = SOURCE_ROOT; }; 53922D96148C56230056699D /* UIImageView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WebCache.m"; path = "SDWebImage/UIImageView+WebCache.m"; sourceTree = SOURCE_ROOT; }; + 53E481DB17C300F6003E8957 /* alpha_processing.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = alpha_processing.c; path = Vendors/libwebp/src/utils/alpha_processing.c; sourceTree = ""; }; + 53E481DC17C300F6003E8957 /* alpha_processing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = alpha_processing.h; path = Vendors/libwebp/src/utils/alpha_processing.h; sourceTree = ""; }; + 53EDFB8817623F7C00698166 /* UIImage+MultiFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+MultiFormat.h"; sourceTree = ""; }; + 53EDFB8917623F7C00698166 /* UIImage+MultiFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+MultiFormat.m"; sourceTree = ""; }; + 53EDFB911762547C00698166 /* UIImage+WebP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+WebP.h"; sourceTree = ""; }; + 53EDFB921762547C00698166 /* UIImage+WebP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+WebP.m"; sourceTree = ""; }; + 53EDFC9017625BE300698166 /* webpi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = webpi.h; path = Vendors/libwebp/src/dec/webpi.h; sourceTree = ""; }; + 53EDFC9117625BE300698166 /* webp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = webp.c; path = Vendors/libwebp/src/dec/webp.c; sourceTree = ""; }; + 53EDFC9617625C1100698166 /* vp8li.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vp8li.h; path = Vendors/libwebp/src/dec/vp8li.h; sourceTree = ""; }; + 53EDFC9717625C1100698166 /* vp8l.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vp8l.c; path = Vendors/libwebp/src/dec/vp8l.c; sourceTree = ""; }; + 53EDFC9817625C1100698166 /* vp8i.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vp8i.h; path = Vendors/libwebp/src/dec/vp8i.h; sourceTree = ""; }; + 53EDFC9917625C1100698166 /* vp8.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vp8.c; path = Vendors/libwebp/src/dec/vp8.c; sourceTree = ""; }; + 53EDFCA417625C5B00698166 /* huffman.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = huffman.h; path = Vendors/libwebp/src/utils/huffman.h; sourceTree = ""; }; + 53EDFCA517625C5B00698166 /* huffman.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = huffman.c; path = Vendors/libwebp/src/utils/huffman.c; sourceTree = ""; }; + 53EDFCAA17625C8700698166 /* frame.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = frame.c; path = Vendors/libwebp/src/dec/frame.c; sourceTree = ""; }; + 53EDFCAD17625CA600698166 /* alpha.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = alpha.c; path = Vendors/libwebp/src/dec/alpha.c; sourceTree = ""; }; + 53EDFCB017625CD800698166 /* quant_levels_dec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quant_levels_dec.h; path = Vendors/libwebp/src/utils/quant_levels_dec.h; sourceTree = ""; }; + 53EDFCB117625CD800698166 /* quant_levels_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant_levels_dec.c; path = Vendors/libwebp/src/utils/quant_levels_dec.c; sourceTree = ""; }; + 53EDFCB717625D1900698166 /* dsp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dsp.h; path = Vendors/libwebp/src/dsp/dsp.h; sourceTree = ""; }; + 53EDFCB817625D1900698166 /* dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dec.c; path = Vendors/libwebp/src/dsp/dec.c; sourceTree = ""; }; + 53EDFCBD17625D3F00698166 /* dec_sse2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dec_sse2.c; path = Vendors/libwebp/src/dsp/dec_sse2.c; sourceTree = ""; }; + 53EDFCC017625D8400698166 /* cpu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cpu.c; path = Vendors/libwebp/src/dsp/cpu.c; sourceTree = ""; }; + 53EDFCC317625DB200698166 /* bit_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bit_reader.h; path = Vendors/libwebp/src/utils/bit_reader.h; sourceTree = ""; }; + 53EDFCC417625DB200698166 /* bit_reader.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = bit_reader.c; path = Vendors/libwebp/src/utils/bit_reader.c; sourceTree = ""; }; + 53EDFCC917625DD700698166 /* color_cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = color_cache.h; path = Vendors/libwebp/src/utils/color_cache.h; sourceTree = ""; }; + 53EDFCCA17625DD700698166 /* color_cache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = color_cache.c; path = Vendors/libwebp/src/utils/color_cache.c; sourceTree = ""; }; + 53EDFCCF17625DFA00698166 /* lossless.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = lossless.h; path = Vendors/libwebp/src/dsp/lossless.h; sourceTree = ""; }; + 53EDFCD017625DFA00698166 /* lossless.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = lossless.c; path = Vendors/libwebp/src/dsp/lossless.c; sourceTree = ""; }; + 53EDFCD517625E1A00698166 /* tree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tree.c; path = Vendors/libwebp/src/dec/tree.c; sourceTree = ""; }; + 53EDFCD817625E3000698166 /* quant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = quant.c; path = Vendors/libwebp/src/dec/quant.c; sourceTree = ""; }; + 53EDFCDB17625E6000698166 /* buffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = Vendors/libwebp/src/dec/buffer.c; sourceTree = ""; }; + 53EDFCDE17625E8D00698166 /* upsampling.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = upsampling.c; path = Vendors/libwebp/src/dsp/upsampling.c; sourceTree = ""; }; + 53EDFCE117625EB100698166 /* yuv.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = yuv.h; path = Vendors/libwebp/src/dsp/yuv.h; sourceTree = ""; }; + 53EDFCE217625EB100698166 /* yuv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = yuv.c; path = Vendors/libwebp/src/dsp/yuv.c; sourceTree = ""; }; + 53EDFCE717625EC800698166 /* io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = io.c; path = Vendors/libwebp/src/dec/io.c; sourceTree = ""; }; + 53EDFCEA17625EE200698166 /* upsampling_sse2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = upsampling_sse2.c; path = Vendors/libwebp/src/dsp/upsampling_sse2.c; sourceTree = ""; }; + 53EDFCED17625F1D00698166 /* rescaler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rescaler.h; path = Vendors/libwebp/src/utils/rescaler.h; sourceTree = ""; }; + 53EDFCEE17625F1D00698166 /* rescaler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rescaler.c; path = Vendors/libwebp/src/utils/rescaler.c; sourceTree = ""; }; + 53EDFCF317625F4100698166 /* utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = utils.h; path = Vendors/libwebp/src/utils/utils.h; sourceTree = ""; }; + 53EDFCF417625F4100698166 /* utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = utils.c; path = Vendors/libwebp/src/utils/utils.c; sourceTree = ""; }; + 53EDFCF917625F5F00698166 /* filters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = filters.h; path = Vendors/libwebp/src/utils/filters.h; sourceTree = ""; }; + 53EDFCFA17625F5F00698166 /* filters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = filters.c; path = Vendors/libwebp/src/utils/filters.c; sourceTree = ""; }; + 53EDFCFF17625F7900698166 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = thread.h; path = Vendors/libwebp/src/utils/thread.h; sourceTree = ""; }; + 53EDFD0017625F7900698166 /* thread.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = thread.c; path = Vendors/libwebp/src/utils/thread.c; sourceTree = ""; }; 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 53FB894814D35E9E0020B787 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + A18A6CC5172DC28500419892 /* UIImage+GIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+GIF.h"; sourceTree = ""; }; + A18A6CC6172DC28500419892 /* UIImage+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+GIF.m"; sourceTree = ""; }; + A18A6CCB172DC33A00419892 /* NSData+GIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+GIF.h"; sourceTree = ""; }; + A18A6CCC172DC33A00419892 /* NSData+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+GIF.m"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -149,16 +273,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 53922D6C148C55810056699D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 53FB894914D35E9E0020B787 /* UIKit.framework in Frameworks */, - 53922D73148C55820056699D /* Foundation.framework in Frameworks */, - 53FB894714D35E820020B787 /* CoreGraphics.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -170,13 +284,13 @@ 53922D70148C55820056699D /* Products */, ); sourceTree = ""; + usesTabs = 0; }; 53922D70148C55820056699D /* Products */ = { isa = PBXGroup; children = ( - 53922D6F148C55820056699D /* libSDWebImage.a */, 53761325155AD0D5005750A4 /* libSDWebImage.a */, - 531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */, + 531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */, ); name = Products; sourceTree = ""; @@ -184,6 +298,7 @@ 53922D71148C55820056699D /* Frameworks */ = { isa = PBXGroup; children = ( + 53EDFBE417625A2800698166 /* WebP */, 53FB893F14D35D1A0020B787 /* CoreGraphics.framework */, 53922D72148C55820056699D /* Foundation.framework */, 53FB894814D35E9E0020B787 /* UIKit.framework */, @@ -195,6 +310,8 @@ isa = PBXGroup; children = ( 53922D88148C56230056699D /* SDWebImageCompat.h */, + 5340674F167780C40042B59E /* SDWebImageCompat.m */, + 530E49E71646388E002868E7 /* SDWebImageOperation.h */, 53922DAB148C56810056699D /* Downloader */, 53922DAA148C56470056699D /* Cache */, 53922DAC148C56DD0056699D /* Utils */, @@ -206,6 +323,14 @@ 53922DA9148C562D0056699D /* Categories */ = { isa = PBXGroup; children = ( + 53EDFB8817623F7C00698166 /* UIImage+MultiFormat.h */, + 53EDFB8917623F7C00698166 /* UIImage+MultiFormat.m */, + A18A6CCB172DC33A00419892 /* NSData+GIF.h */, + A18A6CCC172DC33A00419892 /* NSData+GIF.m */, + A18A6CC5172DC28500419892 /* UIImage+GIF.h */, + A18A6CC6172DC28500419892 /* UIImage+GIF.m */, + 53EDFB911762547C00698166 /* UIImage+WebP.h */, + 53EDFB921762547C00698166 /* UIImage+WebP.m */, 535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */, 535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */, 53922D93148C56230056699D /* UIButton+WebCache.h */, @@ -221,7 +346,6 @@ children = ( 53922D85148C56230056699D /* SDImageCache.h */, 53922D86148C56230056699D /* SDImageCache.m */, - 53922D87148C56230056699D /* SDImageCacheDelegate.h */, ); name = Cache; sourceTree = ""; @@ -231,7 +355,8 @@ children = ( 53922D8B148C56230056699D /* SDWebImageDownloader.h */, 53922D8C148C56230056699D /* SDWebImageDownloader.m */, - 53922D8D148C56230056699D /* SDWebImageDownloaderDelegate.h */, + 530E49E316460AE2002868E7 /* SDWebImageDownloaderOperation.h */, + 530E49E416460AE2002868E7 /* SDWebImageDownloaderOperation.m */, ); name = Downloader; sourceTree = ""; @@ -241,7 +366,6 @@ children = ( 53922D8E148C56230056699D /* SDWebImageManager.h */, 53922D8F148C56230056699D /* SDWebImageManager.m */, - 53922D90148C56230056699D /* SDWebImageManagerDelegate.h */, 53922D89148C56230056699D /* SDWebImageDecoder.h */, 53922D8A148C56230056699D /* SDWebImageDecoder.m */, 53922D91148C56230056699D /* SDWebImagePrefetcher.h */, @@ -250,6 +374,77 @@ name = Utils; sourceTree = ""; }; + 53EDFBE417625A2800698166 /* WebP */ = { + isa = PBXGroup; + children = ( + 53EDFCB617625D1200698166 /* dsp */, + 53EDFCA317625C4600698166 /* utils */, + 53EDFCA217625C3C00698166 /* dec */, + ); + name = WebP; + sourceTree = ""; + }; + 53EDFCA217625C3C00698166 /* dec */ = { + isa = PBXGroup; + children = ( + 53EDFCE717625EC800698166 /* io.c */, + 53EDFCDB17625E6000698166 /* buffer.c */, + 53EDFCD817625E3000698166 /* quant.c */, + 53EDFCD517625E1A00698166 /* tree.c */, + 53EDFCAD17625CA600698166 /* alpha.c */, + 53EDFCAA17625C8700698166 /* frame.c */, + 53EDFC9017625BE300698166 /* webpi.h */, + 53EDFC9117625BE300698166 /* webp.c */, + 53EDFC9617625C1100698166 /* vp8li.h */, + 53EDFC9717625C1100698166 /* vp8l.c */, + 53EDFC9817625C1100698166 /* vp8i.h */, + 53EDFC9917625C1100698166 /* vp8.c */, + ); + name = dec; + sourceTree = ""; + }; + 53EDFCA317625C4600698166 /* utils */ = { + isa = PBXGroup; + children = ( + 53E481DB17C300F6003E8957 /* alpha_processing.c */, + 53E481DC17C300F6003E8957 /* alpha_processing.h */, + 53EDFCFF17625F7900698166 /* thread.h */, + 53EDFD0017625F7900698166 /* thread.c */, + 53EDFCF917625F5F00698166 /* filters.h */, + 53EDFCFA17625F5F00698166 /* filters.c */, + 53EDFCF317625F4100698166 /* utils.h */, + 53EDFCF417625F4100698166 /* utils.c */, + 53EDFCED17625F1D00698166 /* rescaler.h */, + 53EDFCEE17625F1D00698166 /* rescaler.c */, + 53EDFCC917625DD700698166 /* color_cache.h */, + 53EDFCCA17625DD700698166 /* color_cache.c */, + 53EDFCC317625DB200698166 /* bit_reader.h */, + 53EDFCC417625DB200698166 /* bit_reader.c */, + 53EDFCB017625CD800698166 /* quant_levels_dec.h */, + 53EDFCB117625CD800698166 /* quant_levels_dec.c */, + 53EDFCA417625C5B00698166 /* huffman.h */, + 53EDFCA517625C5B00698166 /* huffman.c */, + ); + name = utils; + sourceTree = ""; + }; + 53EDFCB617625D1200698166 /* dsp */ = { + isa = PBXGroup; + children = ( + 53EDFCEA17625EE200698166 /* upsampling_sse2.c */, + 53EDFCE117625EB100698166 /* yuv.h */, + 53EDFCE217625EB100698166 /* yuv.c */, + 53EDFCDE17625E8D00698166 /* upsampling.c */, + 53EDFCCF17625DFA00698166 /* lossless.h */, + 53EDFCD017625DFA00698166 /* lossless.c */, + 53EDFCB717625D1900698166 /* dsp.h */, + 53EDFCB817625D1900698166 /* dec.c */, + 53EDFCBD17625D3F00698166 /* dec_sse2.c */, + 53EDFCC017625D8400698166 /* cpu.c */, + ); + name = dsp; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -258,17 +453,35 @@ buildActionMask = 2147483647; files = ( 531041D1157EAFA400BBABC3 /* SDImageCache.h in Headers */, - 531041D2157EAFA400BBABC3 /* SDImageCacheDelegate.h in Headers */, 531041D3157EAFA400BBABC3 /* SDWebImageCompat.h in Headers */, 531041D4157EAFA400BBABC3 /* SDWebImageDecoder.h in Headers */, 531041D5157EAFA400BBABC3 /* SDWebImageDownloader.h in Headers */, - 531041D6157EAFA400BBABC3 /* SDWebImageDownloaderDelegate.h in Headers */, 531041D7157EAFA400BBABC3 /* SDWebImageManager.h in Headers */, - 531041D8157EAFA400BBABC3 /* SDWebImageManagerDelegate.h in Headers */, 531041D9157EAFA400BBABC3 /* SDWebImagePrefetcher.h in Headers */, 531041DA157EAFA400BBABC3 /* UIButton+WebCache.h in Headers */, 531041DB157EAFA400BBABC3 /* UIImageView+WebCache.h in Headers */, 531041DC157EAFA400BBABC3 /* MKAnnotationView+WebCache.h in Headers */, + 530E49E916464C26002868E7 /* SDWebImageOperation.h in Headers */, + 530E49EB16464C7F002868E7 /* SDWebImageDownloaderOperation.h in Headers */, + A18A6CC8172DC28500419892 /* UIImage+GIF.h in Headers */, + A18A6CCE172DC33A00419892 /* NSData+GIF.h in Headers */, + 53EDFB8B17623F7C00698166 /* UIImage+MultiFormat.h in Headers */, + 53E481E017C300F6003E8957 /* alpha_processing.h in Headers */, + 53EDFB941762547D00698166 /* UIImage+WebP.h in Headers */, + 53EDFC9317625BE300698166 /* webpi.h in Headers */, + 53EDFC9B17625C1100698166 /* vp8li.h in Headers */, + 53EDFC9F17625C1100698166 /* vp8i.h in Headers */, + 53EDFCA717625C5B00698166 /* huffman.h in Headers */, + 53EDFCB317625CD800698166 /* quant_levels_dec.h in Headers */, + 53EDFCBA17625D1900698166 /* dsp.h in Headers */, + 53EDFCC617625DB200698166 /* bit_reader.h in Headers */, + 53EDFCCC17625DD700698166 /* color_cache.h in Headers */, + 53EDFCD217625DFA00698166 /* lossless.h in Headers */, + 53EDFCE417625EB100698166 /* yuv.h in Headers */, + 53EDFCF017625F1D00698166 /* rescaler.h in Headers */, + 53EDFCF617625F4100698166 /* utils.h in Headers */, + 53EDFCFC17625F5F00698166 /* filters.h in Headers */, + 53EDFD0217625F7900698166 /* thread.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -277,43 +490,43 @@ buildActionMask = 2147483647; files = ( 53761316155AD0D5005750A4 /* SDImageCache.h in Headers */, - 53761317155AD0D5005750A4 /* SDImageCacheDelegate.h in Headers */, 53761318155AD0D5005750A4 /* SDWebImageCompat.h in Headers */, 53761319155AD0D5005750A4 /* SDWebImageDecoder.h in Headers */, 5376131A155AD0D5005750A4 /* SDWebImageDownloader.h in Headers */, - 5376131B155AD0D5005750A4 /* SDWebImageDownloaderDelegate.h in Headers */, 5376131C155AD0D5005750A4 /* SDWebImageManager.h in Headers */, - 5376131D155AD0D5005750A4 /* SDWebImageManagerDelegate.h in Headers */, 5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */, 5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */, 53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 53922D6D148C55810056699D /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 53922D97148C56230056699D /* SDImageCache.h in Headers */, - 53922D99148C56230056699D /* SDImageCacheDelegate.h in Headers */, - 53922D9A148C56230056699D /* SDWebImageCompat.h in Headers */, - 53922D9B148C56230056699D /* SDWebImageDecoder.h in Headers */, - 53922D9D148C56230056699D /* SDWebImageDownloader.h in Headers */, - 53922D9F148C56230056699D /* SDWebImageDownloaderDelegate.h in Headers */, - 53922DA0148C56230056699D /* SDWebImageManager.h in Headers */, - 53922DA2148C56230056699D /* SDWebImageManagerDelegate.h in Headers */, - 53922DA3148C56230056699D /* SDWebImagePrefetcher.h in Headers */, - 53922DA5148C56230056699D /* UIButton+WebCache.h in Headers */, - 53922DA7148C56230056699D /* UIImageView+WebCache.h in Headers */, + 530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */, + 530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */, + A18A6CC7172DC28500419892 /* UIImage+GIF.h in Headers */, + A18A6CCD172DC33A00419892 /* NSData+GIF.h in Headers */, + 53EDFB8A17623F7C00698166 /* UIImage+MultiFormat.h in Headers */, + 53EDFB931762547D00698166 /* UIImage+WebP.h in Headers */, + 53EDFC9217625BE300698166 /* webpi.h in Headers */, + 53EDFC9A17625C1100698166 /* vp8li.h in Headers */, + 53EDFC9E17625C1100698166 /* vp8i.h in Headers */, + 53EDFCA617625C5B00698166 /* huffman.h in Headers */, + 53EDFCB217625CD800698166 /* quant_levels_dec.h in Headers */, + 53EDFCB917625D1900698166 /* dsp.h in Headers */, + 53EDFCC517625DB200698166 /* bit_reader.h in Headers */, + 53EDFCCB17625DD700698166 /* color_cache.h in Headers */, + 53EDFCD117625DFA00698166 /* lossless.h in Headers */, + 53E481DF17C300F6003E8957 /* alpha_processing.h in Headers */, + 53EDFCE317625EB100698166 /* yuv.h in Headers */, + 53EDFCEF17625F1D00698166 /* rescaler.h in Headers */, + 53EDFCF517625F4100698166 /* utils.h in Headers */, + 53EDFCFB17625F5F00698166 /* filters.h in Headers */, + 53EDFD0117625F7900698166 /* thread.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 531041C2157EAFA400BBABC3 /* SDWebImage ARC+MKAnnotation */ = { + 531041C2157EAFA400BBABC3 /* SDWebImage+MKAnnotation */ = { isa = PBXNativeTarget; - buildConfigurationList = 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage ARC+MKAnnotation" */; + buildConfigurationList = 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage+MKAnnotation" */; buildPhases = ( 531041C3157EAFA400BBABC3 /* Sources */, 531041CC157EAFA400BBABC3 /* Frameworks */, @@ -323,14 +536,14 @@ ); dependencies = ( ); - name = "SDWebImage ARC+MKAnnotation"; + name = "SDWebImage+MKAnnotation"; productName = SDWebImage; - productReference = 531041E0157EAFA400BBABC3 /* libSDWebImageARC+MKAnnotation.a */; + productReference = 531041E0157EAFA400BBABC3 /* libSDWebImage+MKAnnotation.a */; productType = "com.apple.product-type.library.static"; }; - 53761307155AD0D5005750A4 /* SDWebImage ARC */ = { + 53761307155AD0D5005750A4 /* SDWebImage */ = { isa = PBXNativeTarget; - buildConfigurationList = 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage ARC" */; + buildConfigurationList = 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage" */; buildPhases = ( 53761308155AD0D5005750A4 /* Sources */, 53761311155AD0D5005750A4 /* Frameworks */, @@ -341,26 +554,9 @@ ); dependencies = ( ); - name = "SDWebImage ARC"; - productName = SDWebImage; - productReference = 53761325155AD0D5005750A4 /* libSDWebImage.a */; - productType = "com.apple.product-type.library.static"; - }; - 53922D6E148C55810056699D /* SDWebImage */ = { - isa = PBXNativeTarget; - buildConfigurationList = 53922D7C148C55820056699D /* Build configuration list for PBXNativeTarget "SDWebImage" */; - buildPhases = ( - 53922D6B148C55810056699D /* Sources */, - 53922D6C148C55810056699D /* Frameworks */, - 53922D6D148C55810056699D /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); name = SDWebImage; productName = SDWebImage; - productReference = 53922D6F148C55820056699D /* libSDWebImage.a */; + productReference = 53761325155AD0D5005750A4 /* libSDWebImage.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -369,7 +565,7 @@ 53922D66148C55810056699D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 0500; ORGANIZATIONNAME = Dailymotion; }; buildConfigurationList = 53922D69148C55810056699D /* Build configuration list for PBXProject "SDWebImage" */; @@ -384,9 +580,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 53922D6E148C55810056699D /* SDWebImage */, - 53761307155AD0D5005750A4 /* SDWebImage ARC */, - 531041C2157EAFA400BBABC3 /* SDWebImage ARC+MKAnnotation */, + 53761307155AD0D5005750A4 /* SDWebImage */, + 531041C2157EAFA400BBABC3 /* SDWebImage+MKAnnotation */, 539F912B16316D2D00160719 /* SDWebImageFramework */, ); }; @@ -436,6 +631,36 @@ 531041C9157EAFA400BBABC3 /* UIButton+WebCache.m in Sources */, 531041CA157EAFA400BBABC3 /* UIImageView+WebCache.m in Sources */, 531041CB157EAFA400BBABC3 /* MKAnnotationView+WebCache.m in Sources */, + 530E49ED16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */, + A18A6CCA172DC28500419892 /* UIImage+GIF.m in Sources */, + A18A6CD0172DC33A00419892 /* NSData+GIF.m in Sources */, + 53EDFB8D17623F7C00698166 /* UIImage+MultiFormat.m in Sources */, + 53EDFB961762547D00698166 /* UIImage+WebP.m in Sources */, + 53EDFC9517625BE300698166 /* webp.c in Sources */, + 53EDFC9D17625C1100698166 /* vp8l.c in Sources */, + 53EDFCA117625C1100698166 /* vp8.c in Sources */, + 53EDFCA917625C5B00698166 /* huffman.c in Sources */, + 53EDFCAC17625C8700698166 /* frame.c in Sources */, + 53EDFCAF17625CA600698166 /* alpha.c in Sources */, + 53EDFCB517625CD800698166 /* quant_levels_dec.c in Sources */, + 53EDFCBC17625D1900698166 /* dec.c in Sources */, + 53EDFCBF17625D3F00698166 /* dec_sse2.c in Sources */, + 53EDFCC217625D8400698166 /* cpu.c in Sources */, + 53EDFCC817625DB200698166 /* bit_reader.c in Sources */, + 53EDFCCE17625DD700698166 /* color_cache.c in Sources */, + 53E481DE17C300F6003E8957 /* alpha_processing.c in Sources */, + 53EDFCD417625DFA00698166 /* lossless.c in Sources */, + 53EDFCD717625E1A00698166 /* tree.c in Sources */, + 53EDFCDA17625E3000698166 /* quant.c in Sources */, + 53EDFCDD17625E6000698166 /* buffer.c in Sources */, + 53EDFCE017625E8D00698166 /* upsampling.c in Sources */, + 53EDFCE617625EB100698166 /* yuv.c in Sources */, + 53EDFCE917625EC800698166 /* io.c in Sources */, + 53EDFCEC17625EE200698166 /* upsampling_sse2.c in Sources */, + 53EDFCF217625F1D00698166 /* rescaler.c in Sources */, + 53EDFCF817625F4100698166 /* utils.c in Sources */, + 53EDFCFE17625F5F00698166 /* filters.c in Sources */, + 53EDFD0417625F7900698166 /* thread.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -450,20 +675,37 @@ 5376130D155AD0D5005750A4 /* SDWebImagePrefetcher.m in Sources */, 5376130E155AD0D5005750A4 /* UIButton+WebCache.m in Sources */, 5376130F155AD0D5005750A4 /* UIImageView+WebCache.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 53922D6B148C55810056699D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 53922D98148C56230056699D /* SDImageCache.m in Sources */, - 53922D9C148C56230056699D /* SDWebImageDecoder.m in Sources */, - 53922D9E148C56230056699D /* SDWebImageDownloader.m in Sources */, - 53922DA1148C56230056699D /* SDWebImageManager.m in Sources */, - 53922DA4148C56230056699D /* SDWebImagePrefetcher.m in Sources */, - 53922DA6148C56230056699D /* UIButton+WebCache.m in Sources */, - 53922DA8148C56230056699D /* UIImageView+WebCache.m in Sources */, + 530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */, + 53406750167780C40042B59E /* SDWebImageCompat.m in Sources */, + A18A6CC9172DC28500419892 /* UIImage+GIF.m in Sources */, + A18A6CCF172DC33A00419892 /* NSData+GIF.m in Sources */, + 53EDFB8C17623F7C00698166 /* UIImage+MultiFormat.m in Sources */, + 53EDFB951762547D00698166 /* UIImage+WebP.m in Sources */, + 53EDFC9417625BE300698166 /* webp.c in Sources */, + 53EDFC9C17625C1100698166 /* vp8l.c in Sources */, + 53EDFCA017625C1100698166 /* vp8.c in Sources */, + 53EDFCA817625C5B00698166 /* huffman.c in Sources */, + 53EDFCAB17625C8700698166 /* frame.c in Sources */, + 53EDFCAE17625CA600698166 /* alpha.c in Sources */, + 53EDFCB417625CD800698166 /* quant_levels_dec.c in Sources */, + 53EDFCBB17625D1900698166 /* dec.c in Sources */, + 53EDFCBE17625D3F00698166 /* dec_sse2.c in Sources */, + 53EDFCC117625D8400698166 /* cpu.c in Sources */, + 53EDFCC717625DB200698166 /* bit_reader.c in Sources */, + 53EDFCCD17625DD700698166 /* color_cache.c in Sources */, + 53E481DD17C300F6003E8957 /* alpha_processing.c in Sources */, + 53EDFCD317625DFA00698166 /* lossless.c in Sources */, + 53EDFCD617625E1A00698166 /* tree.c in Sources */, + 53EDFCD917625E3000698166 /* quant.c in Sources */, + 53EDFCDC17625E6000698166 /* buffer.c in Sources */, + 53EDFCDF17625E8D00698166 /* upsampling.c in Sources */, + 53EDFCE517625EB100698166 /* yuv.c in Sources */, + 53EDFCE817625EC800698166 /* io.c in Sources */, + 53EDFCEB17625EE200698166 /* upsampling_sse2.c in Sources */, + 53EDFCF117625F1D00698166 /* rescaler.c in Sources */, + 53EDFCF717625F4100698166 /* utils.c in Sources */, + 53EDFCFD17625F5F00698166 /* filters.c in Sources */, + 53EDFD0317625F7900698166 /* thread.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -472,7 +714,7 @@ /* Begin PBXTargetDependency section */ 539F913016316D3700160719 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 53761307155AD0D5005750A4 /* SDWebImage ARC */; + target = 53761307155AD0D5005750A4 /* SDWebImage */; targetProxy = 539F912F16316D3700160719 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -482,16 +724,19 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_OBJCPP_ARC_ABI = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; DSTROOT = /tmp/SDWebImage.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)\"", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "SDWebImageARC+MKAnnotation"; + PRODUCT_NAME = "SDWebImage+MKAnnotation"; SKIP_INSTALL = YES; STRIP_STYLE = "non-global"; }; @@ -501,16 +746,19 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_OBJCPP_ARC_ABI = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; DSTROOT = /tmp/SDWebImage.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)\"", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "SDWebImageARC+MKAnnotation"; + PRODUCT_NAME = "SDWebImage+MKAnnotation"; SKIP_INSTALL = YES; STRIP_STYLE = "non-global"; }; @@ -520,14 +768,17 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_OBJCPP_ARC_ABI = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; DSTROOT = /tmp/SDWebImage.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)\"", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = SDWebImage; SKIP_INSTALL = YES; @@ -539,14 +790,17 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_OBJCPP_ARC_ABI = YES; COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = NO; DSTROOT = /tmp/SDWebImage.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)\"", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = SDWebImage; SKIP_INSTALL = YES; @@ -558,12 +812,12 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", + "SD_WEBP=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; @@ -581,7 +835,8 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.3; + HEADER_SEARCH_PATHS = Vendors/libwebp/src; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PUBLIC_HEADERS_FOLDER_PATH = include/SDWebImage; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; @@ -592,8 +847,8 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; COPY_PHASE_STRIP = YES; + GCC_PREPROCESSOR_DEFINITIONS = "SD_WEBP=1"; GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; @@ -608,7 +863,8 @@ GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 4.3; + HEADER_SEARCH_PATHS = Vendors/libwebp/src; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; PUBLIC_HEADERS_FOLDER_PATH = include/SDWebImage; RUN_CLANG_STATIC_ANALYZER = YES; SDKROOT = iphoneos; @@ -616,42 +872,6 @@ }; name = Release; }; - 53922D7D148C55820056699D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = NO; - CLANG_WARN_OBJCPP_ARC_ABI = NO; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = NO; - DSTROOT = /tmp/SDWebImage.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = ""; - INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRIP_STYLE = "non-global"; - }; - name = Debug; - }; - 53922D7E148C55820056699D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_OBJC_ARC = NO; - CLANG_WARN_OBJCPP_ARC_ABI = NO; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = NO; - DSTROOT = /tmp/SDWebImage.dst; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = ""; - INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)"; - OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRIP_STYLE = "non-global"; - }; - name = Release; - }; 539F912D16316D2D00160719 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -669,7 +889,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage ARC+MKAnnotation" */ = { + 531041DD157EAFA400BBABC3 /* Build configuration list for PBXNativeTarget "SDWebImage+MKAnnotation" */ = { isa = XCConfigurationList; buildConfigurations = ( 531041DE157EAFA400BBABC3 /* Debug */, @@ -678,7 +898,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage ARC" */ = { + 53761322155AD0D5005750A4 /* Build configuration list for PBXNativeTarget "SDWebImage" */ = { isa = XCConfigurationList; buildConfigurations = ( 53761323155AD0D5005750A4 /* Debug */, @@ -696,15 +916,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 53922D7C148C55820056699D /* Build configuration list for PBXNativeTarget "SDWebImage" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 53922D7D148C55820056699D /* Debug */, - 53922D7E148C55820056699D /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 539F912C16316D2D00160719 /* Build configuration list for PBXAggregateTarget "SDWebImageFramework" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/SDWebImage.xcodeproj/xcshareddata/xcschemes/SDWebImageFramework.xcscheme b/SDWebImage.xcodeproj/xcshareddata/xcschemes/SDWebImageFramework.xcscheme new file mode 100644 index 000000000..17a3c2a27 --- /dev/null +++ b/SDWebImage.xcodeproj/xcshareddata/xcschemes/SDWebImageFramework.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SDWebImage/MKAnnotationView+WebCache.h b/SDWebImage/MKAnnotationView+WebCache.h index 2de424d56..fca8df098 100644 --- a/SDWebImage/MKAnnotationView+WebCache.h +++ b/SDWebImage/MKAnnotationView+WebCache.h @@ -7,14 +7,12 @@ // #import "MapKit/MapKit.h" -#import "SDWebImageCompat.h" -#import "SDWebImageManagerDelegate.h" #import "SDWebImageManager.h" /** * Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView. */ -@interface MKAnnotationView (WebCache) +@interface MKAnnotationView (WebCache) /** * Set the imageView `image` with an `url`. @@ -47,17 +45,18 @@ */ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; -#if NS_BLOCKS_AVAILABLE /** * Set the imageView `image` with an `url`. * * The downloand is asynchronous and cached. * * @param url The url for the image. - * @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder. @@ -66,10 +65,12 @@ * * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. - * @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder and custom options. @@ -79,11 +80,12 @@ * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param success A block to be executed when the image request succeed This block has no return value and takes a Boolean as parameter indicating if the image was cached or not. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; -#endif +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock; /** * Cancel the current download diff --git a/SDWebImage/MKAnnotationView+WebCache.m b/SDWebImage/MKAnnotationView+WebCache.m index 0fe945702..82dc5b9e2 100644 --- a/SDWebImage/MKAnnotationView+WebCache.m +++ b/SDWebImage/MKAnnotationView+WebCache.m @@ -7,74 +7,76 @@ // #import "MKAnnotationView+WebCache.h" +#import "objc/runtime.h" + +static char operationKey; @implementation MKAnnotationView (WebCache) - (void)setImageWithURL:(NSURL *)url { - [self setImageWithURL:url placeholderImage:nil]; + [self setImageWithURL:url placeholderImage:nil options:0 completed:nil]; } - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { - [self setImageWithURL:url placeholderImage:placeholder options:0]; + [self setImageWithURL:url placeholderImage:placeholder options:0 completed:nil]; } - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; - - self.image = placeholder; - - if (url) - { - [manager downloadWithURL:url delegate:self options:options]; - } + [self setImageWithURL:url placeholderImage:placeholder options:options completed:nil]; } -#if NS_BLOCKS_AVAILABLE -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:nil success:success failure:failure]; + [self setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure]; + [self setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; - + [self cancelCurrentImageLoad]; + self.image = placeholder; if (url) { - [manager downloadWithURL:url delegate:self options:options success:success failure:failure]; + __weak MKAnnotationView *wself = self; + id operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (!wself) return; + dispatch_main_sync_safe(^ + { + __strong MKAnnotationView *sself = wself; + if (!sself) return; + if (image) + { + sself.image = image; + } + if (completedBlock && finished) + { + completedBlock(image, error, cacheType); + } + }); + }]; + objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } -#endif - (void)cancelCurrentImageLoad { - [[SDWebImageManager sharedManager] cancelForDelegate:self]; -} - -- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url -{ - self.image = image; -} - -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image -{ - self.image = image; + // Cancel in progress downloader from queue + id operation = objc_getAssociatedObject(self, &operationKey); + if (operation) + { + [operation cancel]; + objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } } @end diff --git a/SDWebImage/NSData+GIF.h b/SDWebImage/NSData+GIF.h new file mode 100644 index 000000000..9065bc672 --- /dev/null +++ b/SDWebImage/NSData+GIF.h @@ -0,0 +1,15 @@ +// +// NSData+GIF.h +// SDWebImage +// +// Created by Andy LaVoy on 4/28/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import + +@interface NSData (GIF) + +- (BOOL)sd_isGIF; + +@end diff --git a/SDWebImage/NSData+GIF.m b/SDWebImage/NSData+GIF.m new file mode 100644 index 000000000..13f2e9803 --- /dev/null +++ b/SDWebImage/NSData+GIF.m @@ -0,0 +1,32 @@ +// +// NSData+GIF.m +// SDWebImage +// +// Created by Andy LaVoy on 4/28/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import "NSData+GIF.h" + +@implementation NSData (GIF) + +- (BOOL)sd_isGIF +{ + BOOL isGIF = NO; + + uint8_t c; + [self getBytes:&c length:1]; + + switch (c) + { + case 0x47: // probably a GIF + isGIF = YES; + break; + default: + break; + } + + return isGIF; +} + +@end diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index cf0d434ed..133af9dd6 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -7,18 +7,40 @@ */ #import -#import "SDImageCacheDelegate.h" +#import "SDWebImageCompat.h" + +enum SDImageCacheType +{ + /** + * The image wasn't available the SDWebImage caches, but was downloaded from the web. + */ + SDImageCacheTypeNone = 0, + /** + * The image was obtained from the disk cache. + */ + SDImageCacheTypeDisk, + /** + * The image was obtained from the memory cache. + */ + SDImageCacheTypeMemory +}; +typedef enum SDImageCacheType SDImageCacheType; /** * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed * asynchronous so it doesn’t add unnecessary latency to the UI. */ @interface SDImageCache : NSObject -{ - NSCache *memCache; - NSString *diskCachePath; - NSOperationQueue *cacheInQueue, *cacheOutQueue; -} + +/** + * The maximum length of time to keep an image in the cache, in seconds + */ +@property (assign, nonatomic) NSInteger maxCacheAge; + +/** + * The maximum size of the cache, in bytes. + */ +@property (assign, nonatomic) unsigned long long maxCacheSize; /** * Returns global shared cache instance @@ -28,11 +50,19 @@ + (SDImageCache *)sharedImageCache; /** - * Sets the global maximum cache age + * Init a new cache store with a specific namespace * - * @param maxCacheAge The maximum length of time to keep an image in the cache, in seconds + * @param ns The namespace to use for this cache store */ -+ (void) setMaxCacheAge:(NSInteger) maxCacheAge; +- (id)initWithNamespace:(NSString *)ns; + +/** + * Add a read-only cache path to search for images pre-cached by SDImageCache + * Useful if you want to bundle pre-loaded images with your app + * + * @param path The path to use for this read-only cache path + */ +- (void)addReadOnlyCachePath:(NSString *)path; /** * Store an image into memory and disk cache at the given key. @@ -64,45 +94,35 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk; /** - * Query the memory cache for an image at a given key and fallback to disk cache - * synchronousely if not found in memory. - * - * @warning This method may perform some synchronous IO operations + * Query the disk cache asynchronously. * * @param key The unique key used to store the wanted image */ -- (UIImage *)imageFromKey:(NSString *)key; +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock; /** - * Query the memory cache for an image at a given key and optionnaly fallback to disk cache - * synchronousely if not found in memory. - * - * @warning This method may perform some synchronous IO operations if fromDisk is YES + * Query the memory cache synchronously. * * @param key The unique key used to store the wanted image - * @param fromDisk Try to retrive the image from disk if not found in memory if YES */ -- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk; - +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key; /** - * Query the disk cache asynchronousely. + * Query the disk cache synchronously after checking the memory cache. * * @param key The unique key used to store the wanted image - * @param delegate The delegate object to send response to - * @param info An NSDictionary with some user info sent back to the delegate */ -- (void)queryDiskCacheForKey:(NSString *)key delegate:(id )delegate userInfo:(NSDictionary *)info; +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key; /** - * Remove the image from memory and disk cache synchronousely + * Remove the image from memory and disk cache synchronously * * @param key The unique image cache key */ - (void)removeImageForKey:(NSString *)key; /** - * Remove the image from memory and optionaly disk cache synchronousely + * Remove the image from memory and optionaly disk cache synchronously * * @param key The unique image cache key * @param fromDisk Also remove cache entry from disk if YES @@ -127,11 +147,16 @@ /** * Get the size used by the disk cache */ -- (int)getSize; +- (unsigned long long)getSize; /** * Get the number of images in the disk cache */ - (int)getDiskCount; +/** + * Asynchronously calculate the disk cache's size. + */ +- (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, unsigned long long totalSize))completionBlock; + @end diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 1a4e0c342..0b86507e1 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -8,44 +8,57 @@ #import "SDImageCache.h" #import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" #import -#import "SDWebImageDecoder.h" #import #import -static SDImageCache *instance; +static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week + +@interface SDImageCache () + +@property (strong, nonatomic) NSCache *memCache; +@property (strong, nonatomic) NSString *diskCachePath; +@property (strong, nonatomic) NSMutableArray *customPaths; +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t ioQueue; + +@end -static NSInteger cacheMaxCacheAge = 60*60*24*7; // 1 week @implementation SDImageCache -#pragma mark NSObject ++ (SDImageCache *)sharedImageCache +{ + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{instance = self.new;}); + return instance; +} - (id)init +{ + return [self initWithNamespace:@"default"]; +} + +- (id)initWithNamespace:(NSString *)ns { if ((self = [super init])) { + NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns]; + + // Create IO serial queue + _ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL); + + // Init default values + _maxCacheAge = kDefaultCacheMaxCacheAge; + // Init the memory cache - memCache = [[NSCache alloc] init]; - memCache.name = @"ImageCache"; + _memCache = [[NSCache alloc] init]; + _memCache.name = fullNamespace; // Init the disk cache NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - diskCachePath = SDWIReturnRetained([[paths objectAtIndex:0] stringByAppendingPathComponent:@"ImageCache"]); - - if (![[NSFileManager defaultManager] fileExistsAtPath:diskCachePath]) - { - [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath - withIntermediateDirectories:YES - attributes:nil - error:NULL]; - } - - // Init the operation queue - cacheInQueue = [[NSOperationQueue alloc] init]; - cacheInQueue.maxConcurrentOperationCount = 1; - cacheOutQueue = [[NSOperationQueue alloc] init]; - cacheOutQueue.maxConcurrentOperationCount = 1; + _diskCachePath = [paths[0] stringByAppendingPathComponent:fullNamespace]; #if TARGET_OS_IPHONE // Subscribe to app events @@ -58,18 +71,11 @@ - (id)init selector:@selector(cleanDisk) name:UIApplicationWillTerminateNotification object:nil]; - -#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 - UIDevice *device = [UIDevice currentDevice]; - if ([device respondsToSelector:@selector(isMultitaskingSupported)] && device.multitaskingSupported) - { - // When in background, clean memory in order to have less chance to be killed - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(clearMemory) - name:UIApplicationDidEnterBackgroundNotification - object:nil]; - } -#endif + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(backgroundCleanDisk) + name:UIApplicationDidEnterBackgroundNotification + object:nil]; #endif } @@ -78,151 +84,93 @@ - (id)init - (void)dealloc { - SDWISafeRelease(memCache); - SDWISafeRelease(diskCachePath); - SDWISafeRelease(cacheInQueue); - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - SDWISuperDealoc; + SDDispatchQueueRelease(_ioQueue); } -#pragma mark SDImageCache (class methods) - -+ (SDImageCache *)sharedImageCache +- (void)addReadOnlyCachePath:(NSString *)path { - if (instance == nil) + if (!self.customPaths) { - instance = [[SDImageCache alloc] init]; + self.customPaths = NSMutableArray.new; } - return instance; -} - -+ (void) setMaxCacheAge:(NSInteger)maxCacheAge -{ - cacheMaxCacheAge = maxCacheAge; + if (![self.customPaths containsObject:path]) + { + [self.customPaths addObject:path]; + } } #pragma mark SDImageCache (private) -- (NSString *)cachePathForKey:(NSString *)key -{ - const char *str = [key UTF8String]; - unsigned char r[CC_MD5_DIGEST_LENGTH]; - CC_MD5(str, (CC_LONG)strlen(str), r); - NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; - - return [diskCachePath stringByAppendingPathComponent:filename]; -} - -- (void)storeKeyWithDataToDisk:(NSArray *)keyAndData +- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path { - // Can't use defaultManager another thread - NSFileManager *fileManager = [[NSFileManager alloc] init]; - - NSString *key = [keyAndData objectAtIndex:0]; - NSData *data = [keyAndData count] > 1 ? [keyAndData objectAtIndex:1] : nil; - - if (data) - { - [fileManager createFileAtPath:[self cachePathForKey:key] contents:data attributes:nil]; - } - else - { - // If no data representation given, convert the UIImage in JPEG and store it - // This trick is more CPU/memory intensive and doesn't preserve alpha channel - UIImage *image = SDWIReturnRetained([self imageFromKey:key fromDisk:YES]); // be thread safe with no lock - if (image) - { -#if TARGET_OS_IPHONE - [fileManager createFileAtPath:[self cachePathForKey:key] contents:UIImageJPEGRepresentation(image, (CGFloat)1.0) attributes:nil]; -#else - NSArray* representations = [image representations]; - NSData* jpegData = [NSBitmapImageRep representationOfImageRepsInArray: representations usingType: NSJPEGFileType properties:nil]; - [fileManager createFileAtPath:[self cachePathForKey:key] contents:jpegData attributes:nil]; -#endif - SDWIRelease(image); - } - } - - SDWIRelease(fileManager); + NSString *filename = [self cachedFileNameForKey:key]; + return [path stringByAppendingPathComponent:filename]; } -- (void)notifyDelegate:(NSDictionary *)arguments +- (NSString *)defaultCachePathForKey:(NSString *)key { - NSString *key = [arguments objectForKey:@"key"]; - id delegate = [arguments objectForKey:@"delegate"]; - NSDictionary *info = [arguments objectForKey:@"userInfo"]; - UIImage *image = [arguments objectForKey:@"image"]; - - if (image) - { - [memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale]; - - if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) - { - [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; - } - } - else - { - if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) - { - [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; - } - } + return [self cachePathForKey:key inPath:self.diskCachePath]; } -- (void)queryDiskCacheOperation:(NSDictionary *)arguments +- (NSString *)cachedFileNameForKey:(NSString *)key { - NSString *key = [arguments objectForKey:@"key"]; - NSMutableDictionary *mutableArguments = SDWIReturnAutoreleased([arguments mutableCopy]); - - UIImage *image = SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]]); - - if (image) + const char *str = [key UTF8String]; + if (str == NULL) { - UIImage *decodedImage = [UIImage decodedImageWithImage:image]; - if (decodedImage) - { - image = decodedImage; - } - - [mutableArguments setObject:image forKey:@"image"]; + str = ""; } + unsigned char r[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, (CC_LONG)strlen(str), r); + NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], r[11], r[12], r[13], r[14], r[15]]; - [self performSelectorOnMainThread:@selector(notifyDelegate:) withObject:mutableArguments waitUntilDone:NO]; + return filename; } #pragma mark ImageCache -- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk +- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk { if (!image || !key) { return; } - - [memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale]; + + [self.memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale]; if (toDisk) { - NSArray *keyWithData; - if (data) - { - keyWithData = [NSArray arrayWithObjects:key, data, nil]; - } - else + dispatch_async(self.ioQueue, ^ { - keyWithData = [NSArray arrayWithObjects:key, nil]; - } + NSData *data = imageData; - NSInvocationOperation *operation = SDWIReturnAutoreleased([[NSInvocationOperation alloc] initWithTarget:self - selector:@selector(storeKeyWithDataToDisk:) - object:keyWithData]); - [cacheInQueue addOperation:operation]; + if (!data) + { + if (image) + { +#if TARGET_OS_IPHONE + data = UIImageJPEGRepresentation(image, (CGFloat)1.0); +#else + data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil]; +#endif + } + } + + if (data) + { + // Can't use defaultManager another thread + NSFileManager *fileManager = NSFileManager.new; + + if (![fileManager fileExistsAtPath:_diskCachePath]) + { + [fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; + } + + [fileManager createFileAtPath:[self defaultCachePathForKey:key] contents:data attributes:nil]; + } + }); } } @@ -236,72 +184,117 @@ - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk [self storeImage:image imageData:nil forKey:key toDisk:toDisk]; } - -- (UIImage *)imageFromKey:(NSString *)key +- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { - return [self imageFromKey:key fromDisk:YES]; + return [self.memCache objectForKey:key]; } -- (UIImage *)imageFromKey:(NSString *)key fromDisk:(BOOL)fromDisk +- (UIImage *)imageFromDiskCacheForKey:(NSString *)key { - if (key == nil) + // First check the in-memory cache... + UIImage *image = [self imageFromMemoryCacheForKey:key]; + if (image) { - return nil; + return image; } + + // Second check the disk cache... + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage) + { + CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale; + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + return diskImage; +} - UIImage *image = [memCache objectForKey:key]; +- (NSData *)diskImageDataBySearchingAllPathsForKey:(NSString *)key +{ + NSString *defaultPath = [self defaultCachePathForKey:key]; + NSData *data = [NSData dataWithContentsOfFile:defaultPath]; + if (data) + { + return data; + } - if (!image && fromDisk) + for (NSString *path in self.customPaths) { - image = SDScaledImageForPath(key, [NSData dataWithContentsOfFile:[self cachePathForKey:key]]); - if (image) - { - [memCache setObject:image forKey:key cost:image.size.height * image.size.width * image.scale]; + NSString *filePath = [self cachePathForKey:key inPath:path]; + NSData *imageData = [NSData dataWithContentsOfFile:filePath]; + if (imageData) { + return imageData; } } - return image; + return nil; } -- (void)queryDiskCacheForKey:(NSString *)key delegate:(id )delegate userInfo:(NSDictionary *)info +- (UIImage *)diskImageForKey:(NSString *)key { - if (!delegate) + NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; + if (data) { - return; + UIImage *image = [UIImage sd_imageWithData:data]; + image = [self scaledImageForKey:key image:image]; + image = [UIImage decodedImageWithImage:image]; + return image; } + else + { + return nil; + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image +{ + return SDScaledImageForKey(key, image); +} + +- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock +{ + NSOperation *operation = NSOperation.new; + + if (!doneBlock) return nil; if (!key) { - if ([delegate respondsToSelector:@selector(imageCache:didNotFindImageForKey:userInfo:)]) - { - [delegate imageCache:self didNotFindImageForKey:key userInfo:info]; - } - return; + doneBlock(nil, SDImageCacheTypeNone); + return nil; } // First check the in-memory cache... - UIImage *image = [memCache objectForKey:key]; + UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { - // ...notify delegate immediately, no need to go async - if ([delegate respondsToSelector:@selector(imageCache:didFindImage:forKey:userInfo:)]) - { - [delegate imageCache:self didFindImage:image forKey:key userInfo:info]; - } - return; + doneBlock(image, SDImageCacheTypeMemory); + return nil; } - NSMutableDictionary *arguments = [NSMutableDictionary dictionaryWithCapacity:3]; - [arguments setObject:key forKey:@"key"]; - [arguments setObject:delegate forKey:@"delegate"]; - if (info) + dispatch_async(self.ioQueue, ^ { - [arguments setObject:info forKey:@"userInfo"]; - } - NSInvocationOperation *operation = SDWIReturnAutoreleased([[NSInvocationOperation alloc] initWithTarget:self - selector:@selector(queryDiskCacheOperation:) - object:arguments]); - [cacheOutQueue addOperation:operation]; + if (operation.isCancelled) + { + return; + } + + @autoreleasepool + { + UIImage *diskImage = [self diskImageForKey:key]; + if (diskImage) + { + CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale; + [self.memCache setObject:diskImage forKey:key cost:cost]; + } + + dispatch_main_sync_safe(^ + { + doneBlock(diskImage, SDImageCacheTypeDisk); + }); + } + }); + + return operation; } - (void)removeImageForKey:(NSString *)key @@ -316,52 +309,142 @@ - (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk return; } - [memCache removeObjectForKey:key]; + [self.memCache removeObjectForKey:key]; if (fromDisk) { - [[NSFileManager defaultManager] removeItemAtPath:[self cachePathForKey:key] error:nil]; + dispatch_async(self.ioQueue, ^ + { + [[NSFileManager defaultManager] removeItemAtPath:[self defaultCachePathForKey:key] error:nil]; + }); } } - (void)clearMemory { - [cacheInQueue cancelAllOperations]; // won't be able to complete - [memCache removeAllObjects]; + [self.memCache removeAllObjects]; } - (void)clearDisk { - [cacheInQueue cancelAllOperations]; - [[NSFileManager defaultManager] removeItemAtPath:diskCachePath error:nil]; - [[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath - withIntermediateDirectories:YES - attributes:nil - error:NULL]; + dispatch_async(self.ioQueue, ^ + { + [[NSFileManager defaultManager] removeItemAtPath:self.diskCachePath error:nil]; + [[NSFileManager defaultManager] createDirectoryAtPath:self.diskCachePath + withIntermediateDirectories:YES + attributes:nil + error:NULL]; + }); } - (void)cleanDisk { - NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-cacheMaxCacheAge]; - NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath]; - for (NSString *fileName in fileEnumerator) + dispatch_async(self.ioQueue, ^ { - NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName]; - NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; - if ([[[attrs fileModificationDate] laterDate:expirationDate] isEqualToDate:expirationDate]) + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + NSArray *resourceKeys = @[ NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ]; + + // This enumerator prefetches useful properties for our cache files. + NSDirectoryEnumerator *fileEnumerator = [fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:resourceKeys + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge]; + NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary]; + unsigned long long currentCacheSize = 0; + + // Enumerate all of the files in the cache directory. This loop has two purposes: + // + // 1. Removing files that are older than the expiration date. + // 2. Storing file attributes for the size-based cleanup pass. + for (NSURL *fileURL in fileEnumerator) { - [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; + NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL]; + + // Skip directories. + if ([resourceValues[NSURLIsDirectoryKey] boolValue]) + { + continue; + } + + // Remove files that are older than the expiration date; + NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey]; + if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) + { + [fileManager removeItemAtURL:fileURL error:nil]; + continue; + } + + // Store a reference to this file and account for its total size. + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize += [totalAllocatedSize unsignedLongLongValue]; + [cacheFiles setObject:resourceValues forKey:fileURL]; } - } + + // If our remaining disk cache exceeds a configured maximum size, perform a second + // size-based cleanup pass. We delete the oldest files first. + if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) + { + // Target half of our maximum cache size for this cleanup pass. + const unsigned long long desiredCacheSize = self.maxCacheSize / 2; + + // Sort the remaining cache files by their last modification time (oldest first). + NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent + usingComparator:^NSComparisonResult(id obj1, id obj2) + { + return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]]; + }]; + + // Delete files until we fall below our desired cache size. + for (NSURL *fileURL in sortedFiles) + { + if ([fileManager removeItemAtURL:fileURL error:nil]) + { + NSDictionary *resourceValues = cacheFiles[fileURL]; + NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey]; + currentCacheSize -= [totalAllocatedSize unsignedLongLongValue]; + + if (currentCacheSize < desiredCacheSize) + { + break; + } + } + } + } + }); +} + +- (void)backgroundCleanDisk +{ + UIApplication *application = [UIApplication sharedApplication]; + __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^ + { + // Clean up any unfinished task business by marking where you + // stopped or ending the task outright. + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }]; + + // Start the long-running task and return immediately. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ + { + // Do the work associated with the task, preferably in chunks. + [self cleanDisk]; + + [application endBackgroundTask:bgTask]; + bgTask = UIBackgroundTaskInvalid; + }); } --(int)getSize +- (unsigned long long)getSize { - int size = 0; - NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath]; + unsigned long long size = 0; + NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath]; for (NSString *fileName in fileEnumerator) { - NSString *filePath = [diskCachePath stringByAppendingPathComponent:fileName]; + NSString *filePath = [self.diskCachePath stringByAppendingPathComponent:fileName]; NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil]; size += [attrs fileSize]; } @@ -371,7 +454,7 @@ -(int)getSize - (int)getDiskCount { int count = 0; - NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:diskCachePath]; + NSDirectoryEnumerator *fileEnumerator = [[NSFileManager defaultManager] enumeratorAtPath:self.diskCachePath]; for (NSString *fileName in fileEnumerator) { count += 1; @@ -380,4 +463,37 @@ - (int)getDiskCount return count; } +- (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, unsigned long long totalSize))completionBlock +{ + NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES]; + + dispatch_async(self.ioQueue, ^ + { + NSUInteger fileCount = 0; + unsigned long long totalSize = 0; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSDirectoryEnumerator *fileEnumerator = [fileManager enumeratorAtURL:diskCacheURL + includingPropertiesForKeys:@[ NSFileSize ] + options:NSDirectoryEnumerationSkipsHiddenFiles + errorHandler:NULL]; + + for (NSURL *fileURL in fileEnumerator) + { + NSNumber *fileSize; + [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL]; + totalSize += [fileSize unsignedLongLongValue]; + fileCount += 1; + } + + if (completionBlock) + { + dispatch_main_sync_safe(^ + { + completionBlock(fileCount, totalSize); + }); + } + }); +} + @end diff --git a/SDWebImage/SDImageCacheDelegate.h b/SDWebImage/SDImageCacheDelegate.h deleted file mode 100644 index 88b6eb2ca..000000000 --- a/SDWebImage/SDImageCacheDelegate.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// SDImageCacheDelegate.h -// Dailymotion -// -// Created by Olivier Poitrey on 16/09/10. -// Copyright 2010 Dailymotion. All rights reserved. -// - -#import "SDWebImageCompat.h" - -@class SDImageCache; - -/** - * Delegate protocol for SDImageCache - */ -@protocol SDImageCacheDelegate - -@optional - -/** - * Called when [SDImageCache queryDiskCacheForKey:delegate:userInfo:] retrived the image from cache - * - * @param imageCache The cache store instance - * @param image The requested image instance - * @param key The requested image cache key - * @param info The provided user info dictionary - */ -- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info; - -/** - * Called when [SDImageCache queryDiskCacheForKey:delegate:userInfo:] did not find the image in the cache - * - * @param imageCache The cache store instance - * @param key The requested image cache key - * @param info The provided user info dictionary - */ -- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info; - -@end diff --git a/SDWebImage/SDWebImageCompat.h b/SDWebImage/SDWebImageCompat.h index 82d4c0ade..673af06cb 100644 --- a/SDWebImage/SDWebImageCompat.h +++ b/SDWebImage/SDWebImageCompat.h @@ -9,6 +9,14 @@ #import +#ifdef __OBJC_GC__ +#error SDWebImage does not support Objective-C Garbage Collection +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0 +#error SDWebImage doesn't support Deployement Target version < 5.0 +#endif + #if !TARGET_OS_IPHONE #import #ifndef UIImage @@ -21,72 +29,26 @@ #import #endif -#if ! __has_feature(objc_arc) -#define SDWIAutorelease(__v) ([__v autorelease]); -#define SDWIReturnAutoreleased SDWIAutorelease - -#define SDWIRetain(__v) ([__v retain]); -#define SDWIReturnRetained SDWIRetain - -#define SDWIRelease(__v) ([__v release]); -#define SDWISafeRelease(__v) ([__v release], __v = nil); -#define SDWISuperDealoc [super dealloc]; - -#define SDWIWeak +#if OS_OBJECT_USE_OBJC + #undef SDDispatchQueueRelease + #undef SDDispatchQueueSetterSementics + #define SDDispatchQueueRelease(q) + #define SDDispatchQueueSetterSementics strong #else -// -fobjc-arc -#define SDWIAutorelease(__v) -#define SDWIReturnAutoreleased(__v) (__v) - -#define SDWIRetain(__v) -#define SDWIReturnRetained(__v) (__v) - -#define SDWIRelease(__v) -#define SDWISafeRelease(__v) (__v = nil); -#define SDWISuperDealoc - -#define SDWIWeak __unsafe_unretained + #undef SDDispatchQueueRelease + #undef SDDispatchQueueSetterSementics + #define SDDispatchQueueRelease(q) (dispatch_release(q)) + #define SDDispatchQueueSetterSementics assign #endif +extern inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image); -NS_INLINE UIImage *SDScaledImageForPath(NSString *path, NSObject *imageOrData) -{ - if (!imageOrData) - { - return nil; - } - - UIImage *image = nil; - if ([imageOrData isKindOfClass:[NSData class]]) - { - image = [[UIImage alloc] initWithData:(NSData *)imageOrData]; - } - else if ([imageOrData isKindOfClass:[UIImage class]]) - { - image = SDWIReturnRetained((UIImage *)imageOrData); - } - else - { - return nil; +#define dispatch_main_sync_safe(block)\ + if ([NSThread isMainThread])\ + {\ + block();\ + }\ + else\ + {\ + dispatch_sync(dispatch_get_main_queue(), block);\ } - - if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) - { - CGFloat scale = 1.0; - if (path.length >= 8) - { - // Search @2x. at the end of the string, before a 3 to 4 extension length (only if key len is 8 or more @2x. + 4 len ext) - NSRange range = [path rangeOfString:@"@2x." options:0 range:NSMakeRange(path.length - 8, 5)]; - if (range.location != NSNotFound) - { - scale = 2.0; - } - } - - UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:UIImageOrientationUp]; - SDWISafeRelease(image) - image = scaledImage; - } - - return SDWIReturnAutoreleased(image); -} diff --git a/SDWebImage/SDWebImageCompat.m b/SDWebImage/SDWebImageCompat.m new file mode 100644 index 000000000..e88f9fa0d --- /dev/null +++ b/SDWebImage/SDWebImageCompat.m @@ -0,0 +1,48 @@ +// +// SDWebImageCompat.m +// SDWebImage +// +// Created by Olivier Poitrey on 11/12/12. +// Copyright (c) 2012 Dailymotion. All rights reserved. +// + +#import "SDWebImageCompat.h" + +#if !__has_feature(objc_arc) +#error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag +#endif + +inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image) +{ + if ([image.images count] > 0) + { + NSMutableArray *scaledImages = [NSMutableArray array]; + + for (UIImage *tempImage in image.images) + { + [scaledImages addObject:SDScaledImageForKey(key, tempImage)]; + } + + return [UIImage animatedImageWithImages:scaledImages duration:image.duration]; + } + else + { + if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) + { + CGFloat scale = 1.0; + if (key.length >= 8) + { + // Search @2x. at the end of the string, before a 3 to 4 extension length (only if key len is 8 or more @2x. + 4 len ext) + NSRange range = [key rangeOfString:@"@2x." options:0 range:NSMakeRange(key.length - 8, 5)]; + if (range.location != NSNotFound) + { + scale = 2.0; + } + } + + UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + image = scaledImage; + } + return image; + } +} diff --git a/SDWebImage/SDWebImageDecoder.h b/SDWebImage/SDWebImageDecoder.h index 23a0c069b..0176a7bae 100644 --- a/SDWebImage/SDWebImageDecoder.h +++ b/SDWebImage/SDWebImageDecoder.h @@ -11,55 +11,6 @@ #import #import "SDWebImageCompat.h" -@protocol SDWebImageDecoderDelegate; - -/** - * Decoding image data is the most expensive step, and it is performed on the main thread. SDWebImageDecoder force the - * image decoding in a separate thread so UIImage will have high chance to reuse the cached result when used by UI in - * the main thread. - * - * @see https://github.com/rs/SDWebImage/pull/18 - */ - -@interface SDWebImageDecoder : NSObject -{ - NSOperationQueue *imageDecodingQueue; -} - -/** - * Returns a shared global instance of image decoder - * - * @return An SDWebImageDecoder shared instance - */ -+ (SDWebImageDecoder *)sharedImageDecoder; - -/** - * Pre-decode a given image in a separate thread. - * - * @param image The image to pre-decode - * @param delegate The object to notify once pre-decoding is completed - * @param info A user info object - */ -- (void)decodeImage:(UIImage *)image withDelegate:(id )delegate userInfo:(NSDictionary *)info; - -@end - -/** - * Delegate protocol for SDWebImageDecoder - */ -@protocol SDWebImageDecoderDelegate - -/** - * Called when pre-decoding is completed - * - * @param decoder The image decoder instance - * @param image The pre-decoded image - * @param userInfo the provided user info dictionary - */ -- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)userInfo; - -@end - @interface UIImage (ForceDecode) + (UIImage *)decodedImageWithImage:(UIImage *)image; diff --git a/SDWebImage/SDWebImageDecoder.m b/SDWebImage/SDWebImageDecoder.m index fe10d0be0..6e904d3ae 100644 --- a/SDWebImage/SDWebImageDecoder.m +++ b/SDWebImage/SDWebImageDecoder.m @@ -10,123 +10,67 @@ #import "SDWebImageDecoder.h" -#define DECOMPRESSED_IMAGE_KEY @"decompressedImage" -#define DECODE_INFO_KEY @"decodeInfo" - -#define IMAGE_KEY @"image" -#define DELEGATE_KEY @"delegate" -#define USER_INFO_KEY @"userInfo" - -@implementation SDWebImageDecoder -static SDWebImageDecoder *sharedInstance; - -- (void)notifyDelegateOnMainThreadWithInfo:(NSDictionary *)dict -{ - SDWIRetain(dict); - NSDictionary *decodeInfo = [dict objectForKey:DECODE_INFO_KEY]; - UIImage *decodedImage = [dict objectForKey:DECOMPRESSED_IMAGE_KEY]; - - id delegate = [decodeInfo objectForKey:DELEGATE_KEY]; - NSDictionary *userInfo = [decodeInfo objectForKey:USER_INFO_KEY]; - - [delegate imageDecoder:self didFinishDecodingImage:decodedImage userInfo:userInfo]; - SDWIRelease(dict); -} +@implementation UIImage (ForceDecode) -- (void)decodeImageWithInfo:(NSDictionary *)decodeInfo ++ (UIImage *)decodedImageWithImage:(UIImage *)image { - UIImage *image = [decodeInfo objectForKey:IMAGE_KEY]; - - UIImage *decompressedImage = [UIImage decodedImageWithImage:image]; - if (!decompressedImage) + if (image.images) { - // If really have any error occurs, we use the original image at this moment - decompressedImage = image; + // Do not decode animated images + return image; } - NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: - decompressedImage, DECOMPRESSED_IMAGE_KEY, - decodeInfo, DECODE_INFO_KEY, nil]; + CGImageRef imageRef = image.CGImage; + CGSize imageSize = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)); + CGRect imageRect = (CGRect){.origin = CGPointZero, .size = imageSize}; - [self performSelectorOnMainThread:@selector(notifyDelegateOnMainThreadWithInfo:) withObject:dict waitUntilDone:NO]; -} + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); -- (id)init -{ - if ((self = [super init])) + int infoMask = (bitmapInfo & kCGBitmapAlphaInfoMask); + BOOL anyNonAlpha = (infoMask == kCGImageAlphaNone || + infoMask == kCGImageAlphaNoneSkipFirst || + infoMask == kCGImageAlphaNoneSkipLast); + + // CGBitmapContextCreate doesn't support kCGImageAlphaNone with RGB. + // https://developer.apple.com/library/mac/#qa/qa1037/_index.html + if (infoMask == kCGImageAlphaNone && CGColorSpaceGetNumberOfComponents(colorSpace) > 1) { - // Initialization code here. - imageDecodingQueue = [[NSOperationQueue alloc] init]; + // Unset the old alpha info. + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + + // Set noneSkipFirst. + bitmapInfo |= kCGImageAlphaNoneSkipFirst; } - - return self; -} - -- (void)decodeImage:(UIImage *)image withDelegate:(id)delegate userInfo:(NSDictionary *)info -{ - NSDictionary *decodeInfo = [NSDictionary dictionaryWithObjectsAndKeys: - image, IMAGE_KEY, - delegate, DELEGATE_KEY, - info, USER_INFO_KEY, nil]; - - NSOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(decodeImageWithInfo:) object:decodeInfo]; - [imageDecodingQueue addOperation:operation]; - SDWIRelease(operation); -} - -- (void)dealloc -{ - SDWISafeRelease(imageDecodingQueue); - SDWISuperDealoc; -} - -+ (SDWebImageDecoder *)sharedImageDecoder -{ - if (!sharedInstance) + // Some PNGs tell us they have alpha but only 3 components. Odd. + else if (!anyNonAlpha && CGColorSpaceGetNumberOfComponents(colorSpace) == 3) { - sharedInstance = [[SDWebImageDecoder alloc] init]; + // Unset the old alpha info. + bitmapInfo &= ~kCGBitmapAlphaInfoMask; + bitmapInfo |= kCGImageAlphaPremultipliedFirst; } - return sharedInstance; -} - -@end - - -@implementation UIImage (ForceDecode) - -+ (UIImage *)decodedImageWithImage:(UIImage *)image -{ - CGImageRef imageRef = image.CGImage; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef); - - BOOL imageHasAlphaInfo = (alphaInfo != kCGImageAlphaNone && - alphaInfo != kCGImageAlphaNoneSkipFirst && - alphaInfo != kCGImageAlphaNoneSkipLast); - - int bytesPerPixel = alphaInfo != kCGImageAlphaNone ? 4 : 3; - CGBitmapInfo bitmapInfo = imageHasAlphaInfo ? kCGImageAlphaPremultipliedLast : alphaInfo; + // It calculates the bytes-per-row based on the bitsPerComponent and width arguments. CGContextRef context = CGBitmapContextCreate(NULL, - CGImageGetWidth(imageRef), - CGImageGetHeight(imageRef), - 8, - // Just always return width * bytesPerPixel will be enough - CGImageGetWidth(imageRef) * bytesPerPixel, - // System only supports RGB, set explicitly + imageSize.width, + imageSize.height, + CGImageGetBitsPerComponent(imageRef), + 0, colorSpace, bitmapInfo); CGColorSpaceRelease(colorSpace); - if (!context) return nil; - CGRect rect = (CGRect){CGPointZero,{CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}}; - CGContextDrawImage(context, rect, imageRef); + // If failed, return undecompressed image + if (!context) return image; + + CGContextDrawImage(context, imageRect, imageRef); CGImageRef decompressedImageRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); - - UIImage *decompressedImage = [[UIImage alloc] initWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation]; + + UIImage *decompressedImage = [UIImage imageWithCGImage:decompressedImageRef scale:image.scale orientation:image.imageOrientation]; CGImageRelease(decompressedImageRef); - return SDWIReturnAutoreleased(decompressedImage); + return decompressedImage; } @end diff --git a/SDWebImage/SDWebImageDownloader.h b/SDWebImage/SDWebImageDownloader.h index 7bef74b83..399db45aa 100644 --- a/SDWebImage/SDWebImageDownloader.h +++ b/SDWebImage/SDWebImageDownloader.h @@ -7,45 +7,71 @@ */ #import -#import "SDWebImageDownloaderDelegate.h" #import "SDWebImageCompat.h" +#import "SDWebImageOperation.h" + +typedef enum +{ + SDWebImageDownloaderLowPriority = 1 << 0, + SDWebImageDownloaderProgressiveDownload = 1 << 1, + /** + * By default, request prevent the of NSURLCache. With this flag, NSURLCache + * is used with default policies. + */ + SDWebImageDownloaderUseNSURLCache = 1 << 2, + /** + * Call completion block with nil image/imageData if the image was read from NSURLCache + * (to be combined with `SDWebImageDownloaderUseNSURLCache`). + */ + SDWebImageDownloaderIgnoreCachedResponse = 1 << 3 +} SDWebImageDownloaderOptions; + +typedef enum +{ + SDWebImageDownloaderFIFOExecutionOrder, + /** + * Default value. All download operations will execute in queue style (first-in-first-out). + */ + SDWebImageDownloaderLIFOExecutionOrder + /** + * All download operations will execute in stack style (last-in-first-out). + */ +} SDWebImageDownloaderExecutionOrder; extern NSString *const SDWebImageDownloadStartNotification; extern NSString *const SDWebImageDownloadStopNotification; +typedef void(^SDWebImageDownloaderProgressBlock)(NSUInteger receivedSize, long long expectedSize); +typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage *image, NSData *data, NSError *error, BOOL finished); + /** * Asynchronous downloader dedicated and optimized for image loading. */ @interface SDWebImageDownloader : NSObject -{ - @private - NSURL *url; - SDWIWeak id delegate; - NSURLConnection *connection; - NSMutableData *imageData; - id userInfo; - BOOL lowPriority; - NSUInteger expectedSize; - BOOL progressive; - size_t width, height; -} -@property (nonatomic, retain) NSURL *url; -@property (nonatomic, assign) id delegate; -@property (nonatomic, retain) NSMutableData *imageData; -@property (nonatomic, retain) id userInfo; -@property (nonatomic, readwrite) BOOL lowPriority; +@property (assign, nonatomic) NSInteger maxConcurrentDownloads; + +/** + * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. + */ +@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; + ++ (SDWebImageDownloader *)sharedDownloader; /** - * If set to YES, enables progressive download support. + * Set a value for a HTTP header to be appended to each download HTTP request. * - * The [SDWebImageDownloaderDelegate imageDownloader:didUpdatePartialImage:] delegate method is then called - * while the image is downloaded with an image object containing the portion of the currently downloaded - * image. + * @param value The value for the header field. Use `nil` value to remove the header. + * @param field The name of the header field to set. + */ +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field; + +/** + * Returns the value of the specified HTTP header field. * - * @see http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ + * @return The value associated with the header field field, or `nil` if there is no corresponding header field. */ -@property (nonatomic, readwrite) BOOL progressive; +- (NSString *)valueForHTTPHeaderField:(NSString *)field; /** * Creates a SDWebImageDownloader async downloader instance with a given URL @@ -55,24 +81,22 @@ extern NSString *const SDWebImageDownloadStopNotification; * @see SDWebImageDownloaderDelegate * * @param url The URL to the image to download - * @param delegate The delegate object - * @param userInfo A NSDictionary containing custom info - * @param lowPriority Ensure the download won't run during UI interactions + * @param options The options to be used for this download + * @param progressBlock A block called repeatedly while the image is downloading + * @param completedBlock A block called once the download is completed. + * If the download succeeded, the image parameter is set, in case of error, + * error parameter is set with the error. The last parameter is always YES + * if SDWebImageDownloaderProgressiveDownload isn't use. With the + * SDWebImageDownloaderProgressiveDownload option, this block is called + * repeatedly with the partial image object and the finished argument set to NO + * before to be called a last time with the full image and finished argument + * set to YES. In case of error, the finished argument is always YES. * - * @return A new SDWebImageDownloader instance + * @return A cancellable SDWebImageOperation */ -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority; -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate userInfo:(id)userInfo; -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate; - -- (void)start; - -/** - * Cancel the download immediatelly - */ -- (void)cancel; - -// This method is now no-op and is deprecated -+ (void)setMaxConcurrentDownloads:(NSUInteger)max __attribute__((deprecated)); +- (id)downloadImageWithURL:(NSURL *)url + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock; @end diff --git a/SDWebImage/SDWebImageDownloader.m b/SDWebImage/SDWebImageDownloader.m index 6285880e7..063056fe2 100644 --- a/SDWebImage/SDWebImageDownloader.m +++ b/SDWebImage/SDWebImageDownloader.m @@ -7,41 +7,35 @@ */ #import "SDWebImageDownloader.h" -#import "SDWebImageDecoder.h" +#import "SDWebImageDownloaderOperation.h" #import -@interface SDWebImageDownloader (ImageDecoder) -@end - NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification"; NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification"; -@interface SDWebImageDownloader () -@property (nonatomic, retain) NSURLConnection *connection; -@end +static NSString *const kProgressCallbackKey = @"progress"; +static NSString *const kCompletedCallbackKey = @"completed"; -@implementation SDWebImageDownloader -@synthesize url, delegate, connection, imageData, userInfo, lowPriority, progressive; +@interface SDWebImageDownloader () -#pragma mark Public Methods +@property (strong, nonatomic) NSOperationQueue *downloadQueue; +@property (weak, nonatomic) NSOperation *lastAddedOperation; +@property (strong, nonatomic) NSMutableDictionary *URLCallbacks; +@property (strong, nonatomic) NSMutableDictionary *HTTPHeaders; +// This queue is used to serialize the handling of the network responses of all the download operation in a single queue +@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue; -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate -{ - return [self downloaderWithURL:url delegate:delegate userInfo:nil]; -} +@end -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate userInfo:(id)userInfo -{ - return [self downloaderWithURL:url delegate:delegate userInfo:userInfo lowPriority:NO]; -} +@implementation SDWebImageDownloader -+ (id)downloaderWithURL:(NSURL *)url delegate:(id)delegate userInfo:(id)userInfo lowPriority:(BOOL)lowPriority ++ (void)initialize { // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import if (NSClassFromString(@"SDNetworkActivityIndicator")) { - + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; @@ -58,226 +52,171 @@ + (id)downloaderWithURL:(NSURL *)url delegate:(id) selector:NSSelectorFromString(@"stopActivity") name:SDWebImageDownloadStopNotification object:nil]; } - - SDWebImageDownloader *downloader = SDWIReturnAutoreleased([[SDWebImageDownloader alloc] init]); - downloader.url = url; - downloader.delegate = delegate; - downloader.userInfo = userInfo; - downloader.lowPriority = lowPriority; - [downloader performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES]; - return downloader; } -+ (void)setMaxConcurrentDownloads:(NSUInteger)max ++ (SDWebImageDownloader *)sharedDownloader { - // NOOP + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{instance = self.new;}); + return instance; } -- (void)start +- (id)init { - // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests - NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15]; - self.connection = SDWIReturnAutoreleased([[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]); - - // If not in low priority mode, ensure we aren't blocked by UI manipulations (default runloop mode for NSURLConnection is NSEventTrackingRunLoopMode) - if (!lowPriority) - { - [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - } - [connection start]; - SDWIRelease(request); - - if (connection) - { - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; - } - else + if ((self = [super init])) { - if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]) - { - [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:nil]; - } + _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; + _downloadQueue = NSOperationQueue.new; + _downloadQueue.maxConcurrentOperationCount = 2; + _URLCallbacks = NSMutableDictionary.new; + _HTTPHeaders = [NSMutableDictionary dictionaryWithObject:@"image/webp,image/*;q=0.8" forKey:@"Accept"]; + _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); } + return self; } -- (void)cancel +- (void)dealloc { - if (connection) - { - [connection cancel]; - self.connection = nil; - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; - } + [self.downloadQueue cancelAllOperations]; + SDDispatchQueueRelease(_barrierQueue); } -#pragma mark NSURLConnection (delegate) - -- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response +- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field { - if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) + if (value) { - expectedSize = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0; - self.imageData = SDWIReturnAutoreleased([[NSMutableData alloc] initWithCapacity:expectedSize]); + self.HTTPHeaders[field] = value; } else { - [aConnection cancel]; - - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; - - if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]) - { - NSError *error = [[NSError alloc] initWithDomain:NSURLErrorDomain - code:[((NSHTTPURLResponse *)response) statusCode] - userInfo:nil]; - [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error]; - SDWIRelease(error); - } - - self.connection = nil; - self.imageData = nil; + [self.HTTPHeaders removeObjectForKey:field]; } } -- (void)connection:(NSURLConnection *)aConnection didReceiveData:(NSData *)data +- (NSString *)valueForHTTPHeaderField:(NSString *)field { - [imageData appendData:data]; - - if (CGImageSourceCreateImageAtIndex == NULL) - { - // ImageIO isn't present in iOS < 4 - self.progressive = NO; - } + return self.HTTPHeaders[field]; +} - if (self.progressive && expectedSize > 0 && [delegate respondsToSelector:@selector(imageDownloader:didUpdatePartialImage:)]) - { - // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ - // Thanks to the author @Nyx0uf +- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads +{ + _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; +} - // Get the total bytes downloaded - const NSUInteger totalSize = [imageData length]; +- (NSInteger)maxConcurrentDownloads +{ + return _downloadQueue.maxConcurrentOperationCount; +} - // Update the data source, we must pass ALL the data, not just the new bytes - CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL); -#if __has_feature(objc_arc) - CGImageSourceUpdateData(imageSource, (__bridge CFDataRef)imageData, totalSize == expectedSize); -#else - CGImageSourceUpdateData(imageSource, (CFDataRef)imageData, totalSize == expectedSize); -#endif +- (id)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSData *, NSError *, BOOL))completedBlock +{ + __block SDWebImageDownloaderOperation *operation; + __weak SDWebImageDownloader *wself = self; - if (width + height == 0) + [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^ + { + // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise + NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:15]; + request.HTTPShouldHandleCookies = NO; + request.HTTPShouldUsePipelining = YES; + request.allHTTPHeaderFields = wself.HTTPHeaders; + operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request options:options progress:^(NSUInteger receivedSize, long long expectedSize) { - CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); - if (properties) + if (!wself) return; + SDWebImageDownloader *sself = wself; + NSArray *callbacksForURL = [sself callbacksForURL:url]; + for (NSDictionary *callbacks in callbacksForURL) { - CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); - if (val) CFNumberGetValue(val, kCFNumberLongType, &height); - val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); - if (val) CFNumberGetValue(val, kCFNumberLongType, &width); - CFRelease(properties); + SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; + if (callback) callback(receivedSize, expectedSize); } } - - if (width + height > 0 && totalSize < expectedSize) + completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) { - // Create the image - CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); - -#ifdef TARGET_OS_IPHONE - // Workaround for iOS anamorphic image - if (partialImageRef) + if (!wself) return; + SDWebImageDownloader *sself = wself; + NSArray *callbacksForURL = [sself callbacksForURL:url]; + if (finished) { - const size_t partialHeight = CGImageGetHeight(partialImageRef); - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); - CGColorSpaceRelease(colorSpace); - if (bmContext) - { - CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); - CGImageRelease(partialImageRef); - partialImageRef = CGBitmapContextCreateImage(bmContext); - CGContextRelease(bmContext); - } - else - { - CGImageRelease(partialImageRef); - partialImageRef = nil; - } + [sself removeCallbacksForURL:url]; } -#endif - - if (partialImageRef) + for (NSDictionary *callbacks in callbacksForURL) { - UIImage *image = SDScaledImageForPath(url.absoluteString, [UIImage imageWithCGImage:partialImageRef]); - [[SDWebImageDecoder sharedImageDecoder] decodeImage:image - withDelegate:self - userInfo:[NSDictionary dictionaryWithObject:@"partial" forKey:@"type"]]; - - CGImageRelease(partialImageRef); + SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; + if (callback) callback(image, data, error, finished); } } + cancelled:^ + { + if (!wself) return; + SDWebImageDownloader *sself = wself; + [sself removeCallbacksForURL:url]; + }]; + [wself.downloadQueue addOperation:operation]; + if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) + { + // Emulate LIFO execution order by systematically adding new operations as last operation's dependency + [wself.lastAddedOperation addDependency:operation]; + wself.lastAddedOperation = operation; + } + }]; - CFRelease(imageSource); - } + return operation; } -#pragma GCC diagnostic ignored "-Wundeclared-selector" -- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection +- (void)addProgressCallback:(void (^)(NSUInteger, long long))progressBlock andCompletedBlock:(void (^)(UIImage *, NSData *data, NSError *, BOOL))completedBlock forURL:(NSURL *)url createCallback:(void (^)())createCallback { - self.connection = nil; - - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; - - if ([delegate respondsToSelector:@selector(imageDownloaderDidFinish:)]) + // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data. + if(url == nil) { - [delegate performSelector:@selector(imageDownloaderDidFinish:) withObject:self]; + if (completedBlock != nil) + { + completedBlock(nil, nil, nil, NO); + } + return; } - - if ([delegate respondsToSelector:@selector(imageDownloader:didFinishWithImage:)]) + + dispatch_barrier_sync(self.barrierQueue, ^ { - UIImage *image = SDScaledImageForPath(url.absoluteString, imageData); - [[SDWebImageDecoder sharedImageDecoder] decodeImage:image withDelegate:self userInfo:nil]; - } -} + BOOL first = NO; + if (!self.URLCallbacks[url]) + { + self.URLCallbacks[url] = NSMutableArray.new; + first = YES; + } -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + // Handle single download of simultaneous download request for the same URL + NSMutableArray *callbacksForURL = self.URLCallbacks[url]; + NSMutableDictionary *callbacks = NSMutableDictionary.new; + if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy]; + if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy]; + [callbacksForURL addObject:callbacks]; + self.URLCallbacks[url] = callbacksForURL; - if ([delegate respondsToSelector:@selector(imageDownloader:didFailWithError:)]) - { - [delegate performSelector:@selector(imageDownloader:didFailWithError:) withObject:self withObject:error]; - } - - self.connection = nil; - self.imageData = nil; + if (first) + { + createCallback(); + } + }); } -#pragma mark SDWebImageDecoderDelegate - -- (void)imageDecoder:(SDWebImageDecoder *)decoder didFinishDecodingImage:(UIImage *)image userInfo:(NSDictionary *)aUserInfo +- (NSArray *)callbacksForURL:(NSURL *)url { - if ([[aUserInfo valueForKey:@"type"] isEqualToString:@"partial"]) + __block NSArray *callbacksForURL; + dispatch_sync(self.barrierQueue, ^ { - [delegate imageDownloader:self didUpdatePartialImage:image]; - } - else - { - [delegate performSelector:@selector(imageDownloader:didFinishWithImage:) withObject:self withObject:image]; - } + callbacksForURL = self.URLCallbacks[url]; + }); + return [callbacksForURL copy]; } -#pragma mark NSObject - -- (void)dealloc +- (void)removeCallbacksForURL:(NSURL *)url { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - SDWISafeRelease(url); - SDWISafeRelease(connection); - SDWISafeRelease(imageData); - SDWISafeRelease(userInfo); - SDWISuperDealoc; + dispatch_barrier_async(self.barrierQueue, ^ + { + [self.URLCallbacks removeObjectForKey:url]; + }); } - @end diff --git a/SDWebImage/SDWebImageDownloaderDelegate.h b/SDWebImage/SDWebImageDownloaderDelegate.h deleted file mode 100644 index c4a6f1121..000000000 --- a/SDWebImage/SDWebImageDownloaderDelegate.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -#import "SDWebImageCompat.h" - -@class SDWebImageDownloader; - -/** - * Delegate protocol for SDWebImageDownloader - */ -@protocol SDWebImageDownloaderDelegate - -@optional - -- (void)imageDownloaderDidFinish:(SDWebImageDownloader *)downloader; - -/** - * Called repeatedly while the image is downloading when [SDWebImageDownloader progressive] is enabled. - * - * @param downloader The SDWebImageDownloader instance - * @param image The partial image representing the currently download portion of the image - */ -- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image; - -/** - * Called when download completed successfuly. - * - * @param downloader The SDWebImageDownloader instance - * @param image The downloaded image object - */ -- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image; - -/** - * Called when an error occurred - * - * @param downloader The SDWebImageDownloader instance - * @param error The error details - */ -- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error; - -@end diff --git a/SDWebImage/SDWebImageDownloaderOperation.h b/SDWebImage/SDWebImageDownloaderOperation.h new file mode 100644 index 000000000..154a4e5ac --- /dev/null +++ b/SDWebImage/SDWebImageDownloaderOperation.h @@ -0,0 +1,24 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import +#import "SDWebImageDownloader.h" +#import "SDWebImageOperation.h" + +@interface SDWebImageDownloaderOperation : NSOperation + +@property (strong, nonatomic, readonly) NSURLRequest *request; +@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options; + +- (id)initWithRequest:(NSURLRequest *)request + options:(SDWebImageDownloaderOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageDownloaderCompletedBlock)completedBlock + cancelled:(void (^)())cancelBlock; + +@end diff --git a/SDWebImage/SDWebImageDownloaderOperation.m b/SDWebImage/SDWebImageDownloaderOperation.m new file mode 100644 index 000000000..de4b14198 --- /dev/null +++ b/SDWebImage/SDWebImageDownloaderOperation.m @@ -0,0 +1,343 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageDownloaderOperation.h" +#import "SDWebImageDecoder.h" +#import "UIImage+MultiFormat.h" +#import + +@interface SDWebImageDownloaderOperation () + +@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock; +@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock; +@property (copy, nonatomic) void (^cancelBlock)(); + +@property (assign, nonatomic, getter = isExecuting) BOOL executing; +@property (assign, nonatomic, getter = isFinished) BOOL finished; +@property (assign, nonatomic) long long expectedSize; +@property (strong, nonatomic) NSMutableData *imageData; +@property (strong, nonatomic) NSURLConnection *connection; + +@end + +@implementation SDWebImageDownloaderOperation +{ + size_t width, height; + BOOL responseFromCached; +} + +// https://github.com/rs/SDWebImage/pull/741 issue solved: +@synthesize executing = _executing; +@synthesize finished = _finished; + +- (id)initWithRequest:(NSURLRequest *)request options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSData *, NSError *, BOOL))completedBlock cancelled:(void (^)())cancelBlock +{ + if ((self = [super init])) + { + _request = request; + _options = options; + _progressBlock = [progressBlock copy]; + _completedBlock = [completedBlock copy]; + _cancelBlock = [cancelBlock copy]; + _executing = NO; + _finished = NO; + _expectedSize = 0; + responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called + } + return self; +} + +- (void)start +{ + if (self.isCancelled) + { + self.finished = YES; + [self reset]; + return; + } + + self.executing = YES; + self.connection = [NSURLConnection.alloc initWithRequest:self.request delegate:self startImmediately:NO]; + + [self.connection start]; + + if (self.connection) + { + if (self.progressBlock) + { + self.progressBlock(0, -1); + } + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; + + // Make sure to run the runloop in our background thread so it can process downloaded data + // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 + // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); + if (!self.isFinished) + { + [self.connection cancel]; + [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey: self.request.URL}]]; + } + } + else + { + if (self.completedBlock) + { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey: @"Connection can't be initialized"}], YES); + } + } +} + +- (void)cancel +{ + if (self.isFinished) return; + [super cancel]; + if (self.cancelBlock) self.cancelBlock(); + + if (self.connection) + { + [self.connection cancel]; + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; + + // As we cancelled the connection, its callback won't be called and thus won't + // maintain the isFinished and isExecuting flags. + if (self.isExecuting) self.executing = NO; + if (!self.isFinished) self.finished = YES; + } + + [self reset]; +} + +- (void)done +{ + self.finished = YES; + self.executing = NO; + [self reset]; +} + +- (void)reset +{ + self.cancelBlock = nil; + self.completedBlock = nil; + self.progressBlock = nil; + self.connection = nil; + self.imageData = nil; +} + +- (void)setFinished:(BOOL)finished +{ + [self willChangeValueForKey:@"isFinished"]; + _finished = finished; + [self didChangeValueForKey:@"isFinished"]; +} + +- (void)setExecuting:(BOOL)executing +{ + [self willChangeValueForKey:@"isExecuting"]; + _executing = executing; + [self didChangeValueForKey:@"isExecuting"]; +} + +- (BOOL)isConcurrent +{ + return YES; +} + +#pragma mark NSURLConnection (delegate) + +- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response +{ + if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) + { + NSUInteger expected = response.expectedContentLength > 0 ? (NSUInteger)response.expectedContentLength : 0; + self.expectedSize = expected; + if (self.progressBlock) + { + self.progressBlock(0, expected); + } + + self.imageData = [NSMutableData.alloc initWithCapacity:expected]; + } + else + { + [self.connection cancel]; + + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + + if (self.completedBlock) + { + self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES); + } + + [self done]; + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data +{ + [self.imageData appendData:data]; + + if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) + { + // The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/ + // Thanks to the author @Nyx0uf + + // Get the total bytes downloaded + const NSUInteger totalSize = self.imageData.length; + + // Update the data source, we must pass ALL the data, not just the new bytes + CGImageSourceRef imageSource = CGImageSourceCreateIncremental(NULL); + CGImageSourceUpdateData(imageSource, (__bridge CFDataRef)self.imageData, totalSize == self.expectedSize); + + if (width + height == 0) + { + CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); + if (properties) + { + CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight); + if (val) CFNumberGetValue(val, kCFNumberLongType, &height); + val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth); + if (val) CFNumberGetValue(val, kCFNumberLongType, &width); + CFRelease(properties); + } + } + + if (width + height > 0 && totalSize < self.expectedSize) + { + // Create the image + CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); + +#ifdef TARGET_OS_IPHONE + // Workaround for iOS anamorphic image + if (partialImageRef) + { + const size_t partialHeight = CGImageGetHeight(partialImageRef); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + CGColorSpaceRelease(colorSpace); + if (bmContext) + { + CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef); + CGImageRelease(partialImageRef); + partialImageRef = CGBitmapContextCreateImage(bmContext); + CGContextRelease(bmContext); + } + else + { + CGImageRelease(partialImageRef); + partialImageRef = nil; + } + } +#endif + + if (partialImageRef) + { + UIImage *image = [UIImage imageWithCGImage:partialImageRef]; + UIImage *scaledImage = [self scaledImageForKey:self.request.URL.absoluteString image:image]; + image = [UIImage decodedImageWithImage:scaledImage]; + CGImageRelease(partialImageRef); + dispatch_main_sync_safe(^ + { + if (self.completedBlock) + { + self.completedBlock(image, nil, nil, NO); + } + }); + } + } + + CFRelease(imageSource); + } + + if (self.progressBlock) + { + self.progressBlock(self.imageData.length, self.expectedSize); + } +} + +- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image +{ + return SDScaledImageForKey(key, image); +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)aConnection +{ + CFRunLoopStop(CFRunLoopGetCurrent()); + self.connection = nil; + + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + + SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock; + + if (completionBlock) + { + if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) + { + completionBlock(nil, nil, nil, YES); + self.completionBlock = nil; + [self done]; + } + else + { + + UIImage *image = [UIImage sd_imageWithData:self.imageData]; + + image = [self scaledImageForKey:self.request.URL.absoluteString image:image]; + + if (!image.images) // Do not force decod animated GIFs + { + image = [UIImage decodedImageWithImage:image]; + } + + if (CGSizeEqualToSize(image.size, CGSizeZero)) + { + completionBlock(nil, nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Downloaded image has 0 pixels"}], YES); + } + else + { + completionBlock(image, self.imageData, nil, YES); + } + self.completionBlock = nil; + [self done]; + } + } + else + { + [self done]; + } +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + CFRunLoopStop(CFRunLoopGetCurrent()); + [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; + + if (self.completedBlock) + { + self.completedBlock(nil, nil, error, YES); + } + + [self done]; +} + +- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse +{ + responseFromCached = NO; // If this method is called, it means the response wasn't read from cache + if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) + { + // Prevents caching of responses + return nil; + } + else + { + return cachedResponse; + } +} + + +@end diff --git a/SDWebImage/SDWebImageManager.h b/SDWebImage/SDWebImageManager.h index f9fa73b50..736fbba87 100644 --- a/SDWebImage/SDWebImageManager.h +++ b/SDWebImage/SDWebImageManager.h @@ -7,22 +7,75 @@ */ #import "SDWebImageCompat.h" -#import "SDWebImageDownloaderDelegate.h" -#import "SDWebImageManagerDelegate.h" -#import "SDImageCacheDelegate.h" +#import "SDWebImageOperation.h" +#import "SDWebImageDownloader.h" +#import "SDImageCache.h" typedef enum { + /** + * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying. + * This flag disable this blacklisting. + */ SDWebImageRetryFailed = 1 << 0, + /** + * By default, image downloads are started during UI interactions, this flags disable this feature, + * leading to delayed download on UIScrollView deceleration for instance. + */ SDWebImageLowPriority = 1 << 1, + /** + * This flag disables on-disk caching + */ SDWebImageCacheMemoryOnly = 1 << 2, - SDWebImageProgressiveDownload = 1 << 3 + /** + * This flag enables progressive download, the image is displayed progressively during download as a browser would do. + * By default, the image is only displayed once completely downloaded. + */ + SDWebImageProgressiveDownload = 1 << 3, + /** + * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed. + * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation. + * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics. + * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image. + * + * Use this flag only if you can't make your URLs static with embeded cache busting parameter. + */ + SDWebImageRefreshCached = 1 << 4 } SDWebImageOptions; -#if NS_BLOCKS_AVAILABLE -typedef void(^SDWebImageSuccessBlock)(UIImage *image, BOOL cached); -typedef void(^SDWebImageFailureBlock)(NSError *error); -#endif +typedef void(^SDWebImageCompletedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType); +typedef void(^SDWebImageCompletedWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished); + + +@class SDWebImageManager; + +@protocol SDWebImageManagerDelegate + +@optional + +/** + * Controls which image should be downloaded when the image is not found in the cache. + * + * @param imageManager The current `SDWebImageManager` + * @param imageURL The url of the image to be downloaded + * + * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied. + */ +- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL; + +/** + * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory. + * NOTE: This method is called from a global queue in order to not to block the main thread. + * + * @param imageManager The current `SDWebImageManager` + * @param image The image to transform + * @param imageURL The url of the image to transform + * + * @return The transformed image object. + */ +- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL; + +@end /** * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes. @@ -32,130 +85,88 @@ typedef void(^SDWebImageFailureBlock)(NSError *error); * * Here is a simple example of how to use SDWebImageManager: * - * SDWebImageManager *manager = [SDWebImageManager sharedManager]; - * [manager downloadWithURL:imageURL - * delegate:self - * options:0 - * success:^(UIImage *image, BOOL cached) - * { - * // do something with image - * } - * failure:nil]; + * @code + +SDWebImageManager *manager = [SDWebImageManager sharedManager]; +[manager downloadWithURL:imageURL + options:0 + progress:nil + completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (image) + { + // do something with image + } + }]; + + * @endcode */ -@interface SDWebImageManager : NSObject -{ - NSMutableArray *downloadInfo; - NSMutableArray *downloadDelegates; - NSMutableArray *downloaders; - NSMutableArray *cacheDelegates; - NSMutableArray *cacheURLs; - NSMutableDictionary *downloaderForURL; - NSMutableArray *failedURLs; -} - -#if NS_BLOCKS_AVAILABLE -typedef NSString *(^CacheKeyFilter)(NSURL *url); +@interface SDWebImageManager : NSObject + +@property (weak, nonatomic) id delegate; + +@property (strong, nonatomic, readonly) SDImageCache *imageCache; +@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader; /** - * The cache filter is a block used each time SDWebManager need to convert an URL into a cache key. This can + * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can * be used to remove dynamic part of an image URL. * * The following example sets a filter in the application delegate that will remove any query-string from the * URL before to use it as a cache key: * - * [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) - * { - * url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; - * return [url absoluteString]; - * }]; - */ -@property (strong) CacheKeyFilter cacheKeyFilter; -#endif + * @code +[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) +{ + url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path]; + return [url absoluteString]; +}]; -/** - * Returns global SDWebImageManager instance. - * - * @return SDWebImageManager shared instance + * @endcode */ -+ (id)sharedManager; - -- (UIImage *)imageWithURL:(NSURL *)url __attribute__ ((deprecated)); +@property (strong) NSString *(^cacheKeyFilter)(NSURL *url); /** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * Returns global SDWebImageManager instance. * - * @param url The URL to the image - * @param delegate The delegate object used to send result back - * @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:] - * @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:success:failure:] + * @return SDWebImageManager shared instance */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate; ++ (SDWebImageManager *)sharedManager; /** * Downloads the image at the given URL if not present in cache or return the cached version otherwise. * * @param url The URL to the image - * @param delegate The delegate object used to send result back * @param options A mask to specify options to use for this request - * @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:] - * @see [SDWebImageManager downloadWithURL:delegate:options:userInfo:success:failure:] - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options; - -/** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. * - * @param url The URL to the image - * @param delegate The delegate object used to send result back - * @param options A mask to specify options to use for this request - * @param info An NSDictionnary passed back to delegate if provided - * @see [SDWebImageManager downloadWithURL:delegate:options:success:failure:] - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)info; - -// use options:SDWebImageRetryFailed instead -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate retryFailed:(BOOL)retryFailed __attribute__ ((deprecated)); -// use options:SDWebImageRetryFailed|SDWebImageLowPriority instead -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate retryFailed:(BOOL)retryFailed lowPriority:(BOOL)lowPriority __attribute__ ((deprecated)); - -#if NS_BLOCKS_AVAILABLE -/** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * This block as no return value and takes the requested UIImage as first parameter. + * In case of error the image parameter is nil and the second parameter may contain an NSError. * - * @param url The URL to the image - * @param delegate The delegate object used to send result back - * @param options A mask to specify options to use for this request - * @param success A block called when image has been retrived successfuly - * @param failure A block called when couldn't be retrived for some reason - * @see [SDWebImageManager downloadWithURL:delegate:options:] - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; - -/** - * Downloads the image at the given URL if not present in cache or return the cached version otherwise. + * The third parameter is an `SDImageCacheType` enum indicating if the image was retrived from the local cache + * or from the memory cache or from the network. * - * @param url The URL to the image - * @param delegate The delegate object used to send result back - * @param options A mask to specify options to use for this request - * @param info An NSDictionnary passed back to delegate if provided - * @param success A block called when image has been retrived successfuly - * @param failure A block called when couldn't be retrived for some reason - * @see [SDWebImageManager downloadWithURL:delegate:options:] - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)info success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; -#endif - -/** - * Cancel all pending download requests for a given delegate + * The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is + * downloading. This block is thus called repetidly with a partial image. When image is fully downloaded, the + * block is called a last time with the full image and the last parameter set to YES. * - * @param delegate The delegate to cancel requests for + * @return Returns a cancellable NSOperation */ -- (void)cancelForDelegate:(id)delegate; +- (id)downloadWithURL:(NSURL *)url + options:(SDWebImageOptions)options + progress:(SDWebImageDownloaderProgressBlock)progressBlock + completed:(SDWebImageCompletedWithFinishedBlock)completedBlock; /** * Cancel all current opreations */ - (void)cancelAll; +/** + * Check one or more operations running + */ +- (BOOL)isRunning; + @end diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index ab76576f3..118ac6630 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -7,59 +7,55 @@ */ #import "SDWebImageManager.h" -#import "SDImageCache.h" -#import "SDWebImageDownloader.h" +#import "UIImage+GIF.h" #import -static SDWebImageManager *instance; +@interface SDWebImageCombinedOperation : NSObject + +@property (assign, nonatomic, getter = isCancelled) BOOL cancelled; +@property (copy, nonatomic) void (^cancelBlock)(); +@property (strong, nonatomic) NSOperation *cacheOperation; + +@end + +@interface SDWebImageManager () + +@property (strong, nonatomic, readwrite) SDImageCache *imageCache; +@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader; +@property (strong, nonatomic) NSMutableArray *failedURLs; +@property (strong, nonatomic) NSMutableArray *runningOperations; + +@end @implementation SDWebImageManager -#if NS_BLOCKS_AVAILABLE -@synthesize cacheKeyFilter; -#endif ++ (id)sharedManager +{ + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{instance = self.new;}); + return instance; +} - (id)init { if ((self = [super init])) { - downloadInfo = [[NSMutableArray alloc] init]; - downloadDelegates = [[NSMutableArray alloc] init]; - downloaders = [[NSMutableArray alloc] init]; - cacheDelegates = [[NSMutableArray alloc] init]; - cacheURLs = [[NSMutableArray alloc] init]; - downloaderForURL = [[NSMutableDictionary alloc] init]; - failedURLs = [[NSMutableArray alloc] init]; + _imageCache = [self createCache]; + _imageDownloader = [SDWebImageDownloader sharedDownloader]; + _failedURLs = NSMutableArray.new; + _runningOperations = NSMutableArray.new; } return self; } -- (void)dealloc +- (SDImageCache *)createCache { - SDWISafeRelease(downloadInfo); - SDWISafeRelease(downloadDelegates); - SDWISafeRelease(downloaders); - SDWISafeRelease(cacheDelegates); - SDWISafeRelease(cacheURLs); - SDWISafeRelease(downloaderForURL); - SDWISafeRelease(failedURLs); - SDWISuperDealoc; -} - - -+ (id)sharedManager -{ - if (instance == nil) - { - instance = [[SDWebImageManager alloc] init]; - } - - return instance; + return [SDImageCache sharedImageCache]; } - (NSString *)cacheKeyForURL:(NSURL *)url { -#if NS_BLOCKS_AVAILABLE if (self.cacheKeyFilter) { return self.cacheKeyFilter(url); @@ -68,439 +64,233 @@ - (NSString *)cacheKeyForURL:(NSURL *)url { return [url absoluteString]; } -#else - return [url absoluteString]; -#endif -} - -/* - * @deprecated - */ -- (UIImage *)imageWithURL:(NSURL *)url -{ - return [[SDImageCache sharedImageCache] imageFromKey:[self cacheKeyForURL:url]]; } -/* - * @deprecated - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate retryFailed:(BOOL)retryFailed -{ - [self downloadWithURL:url delegate:delegate options:(retryFailed ? SDWebImageRetryFailed : 0)]; -} - -/* - * @deprecated - */ -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate retryFailed:(BOOL)retryFailed lowPriority:(BOOL)lowPriority -{ - SDWebImageOptions options = 0; - if (retryFailed) options |= SDWebImageRetryFailed; - if (lowPriority) options |= SDWebImageLowPriority; - [self downloadWithURL:url delegate:delegate options:options]; -} - -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate -{ - [self downloadWithURL:url delegate:delegate options:0]; -} - -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options -{ - [self downloadWithURL:url delegate:delegate options:options userInfo:nil]; -} - -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo -{ +- (id)downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock +{ // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. if ([url isKindOfClass:NSString.class]) { url = [NSURL URLWithString:(NSString *)url]; } - else if (![url isKindOfClass:NSURL.class]) - { - url = nil; // Prevent some common crashes due to common wrong values passed like NSNull.null for instance - } - if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url])) + // Prevents app crashing on argument type error like sending NSNull instead of NSURL + if (![url isKindOfClass:NSURL.class]) { - return; + url = nil; } - // Check the on-disk cache async so we don't block the main thread - [cacheDelegates addObject:delegate]; - [cacheURLs addObject:url]; - NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys: - delegate, @"delegate", - url, @"url", - [NSNumber numberWithInt:options], @"options", - userInfo ? userInfo : [NSNull null], @"userInfo", - nil]; - [[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info]; -} - -#if NS_BLOCKS_AVAILABLE -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure -{ - [self downloadWithURL:url delegate:delegate options:options userInfo:nil success:success failure:failure]; -} - -- (void)downloadWithURL:(NSURL *)url delegate:(id)delegate options:(SDWebImageOptions)options userInfo:(NSDictionary *)userInfo success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure -{ - // repeated logic from above due to requirement for backwards compatability for iOS versions without blocks + __block SDWebImageCombinedOperation *operation = SDWebImageCombinedOperation.new; + __weak SDWebImageCombinedOperation *weakOperation = operation; - // Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't - // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. - if ([url isKindOfClass:NSString.class]) + BOOL isFailedUrl = NO; + @synchronized(self.failedURLs) { - url = [NSURL URLWithString:(NSString *)url]; + isFailedUrl = [self.failedURLs containsObject:url]; } - - if (!url || !delegate || (!(options & SDWebImageRetryFailed) && [failedURLs containsObject:url])) - { - return; - } - - // Check the on-disk cache async so we don't block the main thread - [cacheDelegates addObject:delegate]; - [cacheURLs addObject:url]; - SDWebImageSuccessBlock successCopy = [success copy]; - SDWebImageFailureBlock failureCopy = [failure copy]; - NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys: - delegate, @"delegate", - url, @"url", - [NSNumber numberWithInt:options], @"options", - userInfo ? userInfo : [NSNull null], @"userInfo", - successCopy, @"success", - failureCopy, @"failure", - nil]; - SDWIRelease(successCopy); - SDWIRelease(failureCopy); - [[SDImageCache sharedImageCache] queryDiskCacheForKey:[self cacheKeyForURL:url] delegate:self userInfo:info]; -} -#endif -- (void)cancelForDelegate:(id)delegate -{ - NSUInteger idx; - while ((idx = [cacheDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound) + if (!url || !completedBlock || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { - [cacheDelegates removeObjectAtIndex:idx]; - [cacheURLs removeObjectAtIndex:idx]; - } - - while ((idx = [downloadDelegates indexOfObjectIdenticalTo:delegate]) != NSNotFound) - { - SDWebImageDownloader *downloader = SDWIReturnRetained([downloaders objectAtIndex:idx]); - - [downloadInfo removeObjectAtIndex:idx]; - [downloadDelegates removeObjectAtIndex:idx]; - [downloaders removeObjectAtIndex:idx]; - - if (![downloaders containsObject:downloader]) + if (completedBlock) { - // No more delegate are waiting for this download, cancel it - [downloader cancel]; - [downloaderForURL removeObjectForKey:downloader.url]; + dispatch_main_sync_safe(^ + { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; + completedBlock(nil, error, SDImageCacheTypeNone, YES); + }); } - - SDWIRelease(downloader); + return operation; } -} -- (void)cancelAll -{ - for (SDWebImageDownloader *downloader in downloaders) + @synchronized(self.runningOperations) { - [downloader cancel]; + [self.runningOperations addObject:operation]; } - [cacheDelegates removeAllObjects]; - [cacheURLs removeAllObjects]; - - [downloadInfo removeAllObjects]; - [downloadDelegates removeAllObjects]; - [downloaders removeAllObjects]; - [downloaderForURL removeAllObjects]; -} - -#pragma mark SDImageCacheDelegate + NSString *key = [self cacheKeyForURL:url]; -- (NSUInteger)indexOfDelegate:(id)delegate waitingForURL:(NSURL *)url -{ - // Do a linear search, simple (even if inefficient) - NSUInteger idx; - for (idx = 0; idx < [cacheDelegates count]; idx++) + operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { - if ([cacheDelegates objectAtIndex:idx] == delegate && [[cacheURLs objectAtIndex:idx] isEqual:url]) + if (operation.isCancelled) { - return idx; - } - } - return NSNotFound; -} - -- (void)imageCache:(SDImageCache *)imageCache didFindImage:(UIImage *)image forKey:(NSString *)key userInfo:(NSDictionary *)info -{ - NSURL *url = [info objectForKey:@"url"]; - id delegate = [info objectForKey:@"delegate"]; - - NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url]; - if (idx == NSNotFound) - { - // Request has since been canceled - return; - } + @synchronized(self.runningOperations) + { + [self.runningOperations removeObject:operation]; + } - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]) - { - [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image]; - } - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)]) - { - objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, url); - } - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)]) - { - NSDictionary *userInfo = [info objectForKey:@"userInfo"]; - if ([userInfo isKindOfClass:NSNull.class]) - { - userInfo = nil; + return; } - objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, url, userInfo); - } -#if NS_BLOCKS_AVAILABLE - if ([info objectForKey:@"success"]) - { - SDWebImageSuccessBlock success = [info objectForKey:@"success"]; - success(image, YES); - } -#endif - - [cacheDelegates removeObjectAtIndex:idx]; - [cacheURLs removeObjectAtIndex:idx]; -} -- (void)imageCache:(SDImageCache *)imageCache didNotFindImageForKey:(NSString *)key userInfo:(NSDictionary *)info -{ - NSURL *url = [info objectForKey:@"url"]; - id delegate = [info objectForKey:@"delegate"]; - SDWebImageOptions options = [[info objectForKey:@"options"] intValue]; - - NSUInteger idx = [self indexOfDelegate:delegate waitingForURL:url]; - if (idx == NSNotFound) - { - // Request has since been canceled - return; - } - - [cacheDelegates removeObjectAtIndex:idx]; - [cacheURLs removeObjectAtIndex:idx]; - - // Share the same downloader for identical URLs so we don't download the same URL several times - SDWebImageDownloader *downloader = [downloaderForURL objectForKey:url]; - - if (!downloader) - { - downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self userInfo:info lowPriority:(options & SDWebImageLowPriority)]; - [downloaderForURL setObject:downloader forKey:url]; - } - else - { - // Reuse shared downloader - downloader.lowPriority = (options & SDWebImageLowPriority); - } - - if ((options & SDWebImageProgressiveDownload) && !downloader.progressive) - { - // Turn progressive download support on demand - downloader.progressive = YES; - } - - [downloadInfo addObject:info]; - [downloadDelegates addObject:delegate]; - [downloaders addObject:downloader]; -} - -#pragma mark SDWebImageDownloaderDelegate - -- (void)imageDownloader:(SDWebImageDownloader *)downloader didUpdatePartialImage:(UIImage *)image -{ - // Notify all the downloadDelegates with this downloader - for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--) - { - NSUInteger uidx = (NSUInteger)idx; - SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx]; - if (aDownloader == downloader) + if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { - id delegate = [downloadDelegates objectAtIndex:uidx]; - SDWIRetain(delegate); - SDWIAutorelease(delegate); - - if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:)]) + if (image && options & SDWebImageRefreshCached) { - objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:), self, image, downloader.url); - } - if ([delegate respondsToSelector:@selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:)]) - { - NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"]; - if ([userInfo isKindOfClass:NSNull.class]) + dispatch_main_sync_safe(^ { - userInfo = nil; - } - objc_msgSend(delegate, @selector(webImageManager:didProgressWithPartialImage:forURL:userInfo:), self, image, downloader.url, userInfo); + // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image + // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. + completedBlock(image, nil, cacheType, YES); + }); } - } - } -} -- (void)imageDownloader:(SDWebImageDownloader *)downloader didFinishWithImage:(UIImage *)image -{ - SDWIRetain(downloader); - SDWebImageOptions options = [[downloader.userInfo objectForKey:@"options"] intValue]; - - // Notify all the downloadDelegates with this downloader - for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--) - { - NSUInteger uidx = (NSUInteger)idx; - SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx]; - if (aDownloader == downloader) - { - id delegate = [downloadDelegates objectAtIndex:uidx]; - SDWIRetain(delegate); - SDWIAutorelease(delegate); - - if (image) + // download if no image or requested to refresh anyway, and download allowed by delegate + SDWebImageDownloaderOptions downloaderOptions = 0; + if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; + if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; + if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; + if (image && options & SDWebImageRefreshCached) { - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:)]) - { - [delegate performSelector:@selector(webImageManager:didFinishWithImage:) withObject:self withObject:image]; - } - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:)]) + // force progressive off if image already cached but forced refreshing + downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; + // ignore image read from NSURLCache if image if cached but force refreshing + downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; + } + id subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) + { + if (weakOperation.isCancelled) { - objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:), self, image, downloader.url); + dispatch_main_sync_safe(^ + { + completedBlock(nil, nil, SDImageCacheTypeNone, finished); + }); } - if ([delegate respondsToSelector:@selector(webImageManager:didFinishWithImage:forURL:userInfo:)]) + else if (error) { - NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"]; - if ([userInfo isKindOfClass:NSNull.class]) + dispatch_main_sync_safe(^ { - userInfo = nil; + completedBlock(nil, error, SDImageCacheTypeNone, finished); + }); + + if (error.code != NSURLErrorNotConnectedToInternet) + { + @synchronized(self.failedURLs) + { + [self.failedURLs addObject:url]; + } } - objc_msgSend(delegate, @selector(webImageManager:didFinishWithImage:forURL:userInfo:), self, image, downloader.url, userInfo); - } -#if NS_BLOCKS_AVAILABLE - if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"success"]) - { - SDWebImageSuccessBlock success = [[downloadInfo objectAtIndex:uidx] objectForKey:@"success"]; - success(image, NO); - } -#endif - } - else - { - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)]) - { - [delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:nil]; - } - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)]) - { - objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, nil, downloader.url); } - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)]) + else { - NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"]; - if ([userInfo isKindOfClass:NSNull.class]) + BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); + + if (options & SDWebImageRefreshCached && image && !downloadedImage) + { + // Image refresh hit the NSURLCache cache, do not call the completion block + } + // NOTE: We don't call transformDownloadedImage delegate method on animated images as most transformation code would mangle it + else if (downloadedImage && !downloadedImage.images && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ + { + UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; + + dispatch_main_sync_safe(^ + { + completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished); + }); + + if (transformedImage && finished) + { + NSData *dataToStore = [transformedImage isEqual:downloadedImage] ? data : nil; + [self.imageCache storeImage:transformedImage imageData:dataToStore forKey:key toDisk:cacheOnDisk]; + } + }); + } + else { - userInfo = nil; + dispatch_main_sync_safe(^ + { + completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished); + }); + + if (downloadedImage && finished) + { + [self.imageCache storeImage:downloadedImage imageData:data forKey:key toDisk:cacheOnDisk]; + } } - objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, nil, downloader.url, userInfo); } -#if NS_BLOCKS_AVAILABLE - if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]) + + if (finished) { - SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]; - failure(nil); + @synchronized(self.runningOperations) + { + [self.runningOperations removeObject:operation]; + } } -#endif + }]; + operation.cancelBlock = ^{[subOperation cancel];}; + } + else if (image) + { + dispatch_main_sync_safe(^ + { + completedBlock(image, nil, cacheType, YES); + }); + @synchronized(self.runningOperations) + { + [self.runningOperations removeObject:operation]; } - - [downloaders removeObjectAtIndex:uidx]; - [downloadInfo removeObjectAtIndex:uidx]; - [downloadDelegates removeObjectAtIndex:uidx]; } - } + else + { + // Image not in cache and download disallowed by delegate + dispatch_main_sync_safe(^ + { + completedBlock(nil, nil, SDImageCacheTypeNone, YES); + }); + @synchronized(self.runningOperations) + { + [self.runningOperations removeObject:operation]; + } + } + }]; - if (image) - { - // Store the image in the cache - [[SDImageCache sharedImageCache] storeImage:image - imageData:downloader.imageData - forKey:[self cacheKeyForURL:downloader.url] - toDisk:!(options & SDWebImageCacheMemoryOnly)]; - } - else if (!(options & SDWebImageRetryFailed)) + return operation; +} + +- (void)cancelAll +{ + @synchronized(self.runningOperations) { - // The image can't be downloaded from this URL, mark the URL as failed so we won't try and fail again and again - // (do this only if SDWebImageRetryFailed isn't activated) - [failedURLs addObject:downloader.url]; + [self.runningOperations makeObjectsPerformSelector:@selector(cancel)]; + [self.runningOperations removeAllObjects]; } - - - // Release the downloader - [downloaderForURL removeObjectForKey:downloader.url]; - SDWIRelease(downloader); } -- (void)imageDownloader:(SDWebImageDownloader *)downloader didFailWithError:(NSError *)error; +- (BOOL)isRunning { - SDWIRetain(downloader); + return self.runningOperations.count > 0; +} - // Notify all the downloadDelegates with this downloader - for (NSInteger idx = (NSInteger)[downloaders count] - 1; idx >= 0; idx--) - { - NSUInteger uidx = (NSUInteger)idx; - SDWebImageDownloader *aDownloader = [downloaders objectAtIndex:uidx]; - if (aDownloader == downloader) - { - id delegate = [downloadDelegates objectAtIndex:uidx]; - SDWIRetain(delegate); - SDWIAutorelease(delegate); +@end - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:)]) - { - [delegate performSelector:@selector(webImageManager:didFailWithError:) withObject:self withObject:error]; - } - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:)]) - { - objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:), self, error, downloader.url); - } - if ([delegate respondsToSelector:@selector(webImageManager:didFailWithError:forURL:userInfo:)]) - { - NSDictionary *userInfo = [[downloadInfo objectAtIndex:uidx] objectForKey:@"userInfo"]; - if ([userInfo isKindOfClass:NSNull.class]) - { - userInfo = nil; - } - objc_msgSend(delegate, @selector(webImageManager:didFailWithError:forURL:userInfo:), self, error, downloader.url, userInfo); - } -#if NS_BLOCKS_AVAILABLE - if ([[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]) - { - SDWebImageFailureBlock failure = [[downloadInfo objectAtIndex:uidx] objectForKey:@"failure"]; - failure(error); - } -#endif +@implementation SDWebImageCombinedOperation - [downloaders removeObjectAtIndex:uidx]; - [downloadInfo removeObjectAtIndex:uidx]; - [downloadDelegates removeObjectAtIndex:uidx]; - } +- (void)setCancelBlock:(void (^)())cancelBlock +{ + if (self.isCancelled) + { + if (cancelBlock) cancelBlock(); + } + else + { + _cancelBlock = [cancelBlock copy]; } +} - // Release the downloader - [downloaderForURL removeObjectForKey:downloader.url]; - SDWIRelease(downloader); +- (void)cancel +{ + self.cancelled = YES; + if (self.cacheOperation) + { + [self.cacheOperation cancel]; + self.cacheOperation = nil; + } + if (self.cancelBlock) + { + self.cancelBlock(); + self.cancelBlock = nil; + } } @end diff --git a/SDWebImage/SDWebImageManagerDelegate.h b/SDWebImage/SDWebImageManagerDelegate.h deleted file mode 100644 index c8d17effb..000000000 --- a/SDWebImage/SDWebImageManagerDelegate.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the SDWebImage package. - * (c) Olivier Poitrey - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -@class SDWebImageManager; -@class UIImage; - -/** - * Delegate protocol for SDWebImageManager - */ -@protocol SDWebImageManagerDelegate - -@optional - -/** - * Called while an image is downloading with an partial image object representing the currently downloaded portion of the image. - * This delegate is called only if ImageIO is available and `SDWebImageProgressiveDownload` option has been used. - * - * @param imageManager The image manager - * @param image The retrived image object - * @param url The image URL used to retrive the image - * @param info The user info dictionnary - */ -- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info; -- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url; - -/** - * Called when image download is completed successfuly. - * - * @param imageManager The image manager - * @param image The retrived image object - * @param url The image URL used to retrive the image - * @param info The user info dictionnary - */ -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info; -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url; -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image; - -/** - * Called when an error occurred. - * - * @param imageManager The image manager - * @param error The error - * @param url The image URL used to retrive the image - * @param info The user info dictionnary - */ -- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error forURL:(NSURL *)url userInfo:(NSDictionary *)info; -- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error forURL:(NSURL *)url; -- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error; - -@end diff --git a/SDWebImage/SDWebImageOperation.h b/SDWebImage/SDWebImageOperation.h new file mode 100644 index 000000000..71094ee36 --- /dev/null +++ b/SDWebImage/SDWebImageOperation.h @@ -0,0 +1,15 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import + +@protocol SDWebImageOperation + +- (void)cancel; + +@end diff --git a/SDWebImage/SDWebImagePrefetcher.h b/SDWebImage/SDWebImagePrefetcher.h index 98dac0c53..7695f5707 100644 --- a/SDWebImage/SDWebImagePrefetcher.h +++ b/SDWebImage/SDWebImagePrefetcher.h @@ -7,20 +7,12 @@ */ #import -#import "SDWebImageManagerDelegate.h" #import "SDWebImageManager.h" /** * Prefetch some URLs in the cache for future use. Images are downloaded in low priority. */ -@interface SDWebImagePrefetcher : NSObject -{ - NSArray *_prefetchURLs; - NSUInteger _skippedCount; - NSUInteger _finishedCount; - NSUInteger _requestedCount; - NSTimeInterval _startedTime; -} +@interface SDWebImagePrefetcher : NSObject /** * Maximum number of URLs to prefetch at the same time. Defaults to 3. @@ -47,6 +39,15 @@ */ - (void)prefetchURLs:(NSArray *)urls; +/** + * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, + * currently one image is downloaded at a time, + * and skips images for failed downloads and proceed to the next image in the list + * + * @param urls list of URLs to prefetch + * @param completionBlock block to be called when prefetching is completed + */ +- (void)prefetchURLs:(NSArray *)urls completed:(void (^)(NSUInteger finishedCount, NSUInteger skippedCount))completionBlock; /** * Remove and cancel queued list diff --git a/SDWebImage/SDWebImagePrefetcher.m b/SDWebImage/SDWebImagePrefetcher.m index 08096cd1d..c029e8d95 100644 --- a/SDWebImage/SDWebImagePrefetcher.m +++ b/SDWebImage/SDWebImagePrefetcher.m @@ -10,105 +10,118 @@ #import "SDWebImageManager.h" @interface SDWebImagePrefetcher () -@property (nonatomic, retain) NSArray *prefetchURLs; -@end -@implementation SDWebImagePrefetcher +@property (strong, nonatomic) SDWebImageManager *manager; +@property (strong, nonatomic) NSArray *prefetchURLs; +@property (assign, nonatomic) NSUInteger requestedCount; +@property (assign, nonatomic) NSUInteger skippedCount; +@property (assign, nonatomic) NSUInteger finishedCount; +@property (assign, nonatomic) NSTimeInterval startedTime; +@property (copy, nonatomic) void (^completionBlock)(NSUInteger, NSUInteger); -static SDWebImagePrefetcher *instance; +@end -@synthesize prefetchURLs; -@synthesize maxConcurrentDownloads; -@synthesize options; +@implementation SDWebImagePrefetcher + (SDWebImagePrefetcher *)sharedImagePrefetcher { - if (instance == nil) - { - instance = [[SDWebImagePrefetcher alloc] init]; - instance.maxConcurrentDownloads = 3; - instance.options = (SDWebImageLowPriority); - } - + static dispatch_once_t once; + static id instance; + dispatch_once(&once, ^{instance = self.new;}); return instance; } -- (void)startPrefetchingAtIndex:(NSUInteger)index withManager:(SDWebImageManager *)imageManager +- (id)init { - if (index >= [self.prefetchURLs count]) return; - _requestedCount++; - [imageManager downloadWithURL:[self.prefetchURLs objectAtIndex:index] delegate:self options:self.options]; + if ((self = [super init])) + { + _manager = SDWebImageManager.new; + _options = SDWebImageLowPriority; + self.maxConcurrentDownloads = 3; + } + return self; } -- (void)reportStatus +- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { - NSUInteger total = [self.prefetchURLs count]; - NSLog(@"Finished prefetching (%d successful, %d skipped, timeElasped %.2f)", total - _skippedCount, _skippedCount, CFAbsoluteTimeGetCurrent() - _startedTime); + self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; } -- (void)prefetchURLs:(NSArray *)urls +- (NSUInteger)maxConcurrentDownloads { - [self cancelPrefetching]; // Prevent duplicate prefetch request - _startedTime = CFAbsoluteTimeGetCurrent(); - self.prefetchURLs = urls; + return self.manager.imageDownloader.maxConcurrentDownloads; +} - // Starts prefetching from the very first image on the list with the max allowed concurrency - NSUInteger listCount = [self.prefetchURLs count]; - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - for (NSUInteger i = 0; i < self.maxConcurrentDownloads && _requestedCount < listCount; i++) +- (void)startPrefetchingAtIndex:(NSUInteger)index +{ + if (index >= self.prefetchURLs.count) return; + self.requestedCount++; + [self.manager downloadWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) { - [self startPrefetchingAtIndex:i withManager:manager]; - } + if (!finished) return; + self.finishedCount++; + + if (image) + { + NSLog(@"Prefetched %d out of %d", self.finishedCount, self.prefetchURLs.count); + } + else + { + NSLog(@"Prefetched %d out of %d (Failed)", self.finishedCount, [self.prefetchURLs count]); + + // Add last failed + self.skippedCount++; + } + + if (self.prefetchURLs.count > self.requestedCount) + { + [self startPrefetchingAtIndex:self.requestedCount]; + } + else if (self.finishedCount == self.requestedCount) + { + [self reportStatus]; + if (self.completionBlock) + { + self.completionBlock(self.finishedCount, self.skippedCount); + self.completionBlock = nil; + } + } + }]; } -- (void)cancelPrefetching +- (void)reportStatus { - self.prefetchURLs = nil; - _skippedCount = 0; - _requestedCount = 0; - _finishedCount = 0; - [[SDWebImageManager sharedManager] cancelForDelegate:self]; + NSUInteger total = [self.prefetchURLs count]; + NSLog(@"Finished prefetching (%d successful, %d skipped, timeElasped %.2f)", total - self.skippedCount, self.skippedCount, CFAbsoluteTimeGetCurrent() - self.startedTime); } -#pragma mark SDWebImagePrefetcher (SDWebImageManagerDelegate) - -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image +- (void)prefetchURLs:(NSArray *)urls { - _finishedCount++; - NSLog(@"Prefetched %d out of %d", _finishedCount, [self.prefetchURLs count]); - - if ([self.prefetchURLs count] > _requestedCount) - { - [self startPrefetchingAtIndex:_requestedCount withManager:imageManager]; - } - else if (_finishedCount == _requestedCount) - { - [self reportStatus]; - } + [self prefetchURLs:urls completed:nil]; } -- (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error +- (void)prefetchURLs:(NSArray *)urls completed:(void (^)(NSUInteger, NSUInteger))completionBlock { - _finishedCount++; - NSLog(@"Prefetched %d out of %d (Failed)", _finishedCount, [self.prefetchURLs count]); - - // Add last failed - _skippedCount++; + [self cancelPrefetching]; // Prevent duplicate prefetch request + self.startedTime = CFAbsoluteTimeGetCurrent(); + self.prefetchURLs = urls; + self.completionBlock = completionBlock; - if ([self.prefetchURLs count] > _requestedCount) - { - [self startPrefetchingAtIndex:_requestedCount withManager:imageManager]; - } - else if (_finishedCount == _requestedCount) + // Starts prefetching from the very first image on the list with the max allowed concurrency + NSUInteger listCount = self.prefetchURLs.count; + for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { - [self reportStatus]; + [self startPrefetchingAtIndex:i]; } } -- (void)dealloc +- (void)cancelPrefetching { self.prefetchURLs = nil; - SDWISuperDealoc; + self.skippedCount = 0; + self.requestedCount = 0; + self.finishedCount = 0; + [self.manager cancelAll]; } @end diff --git a/SDWebImage/UIButton+WebCache.h b/SDWebImage/UIButton+WebCache.h index 26ee9275e..1f5d2c82d 100644 --- a/SDWebImage/UIButton+WebCache.h +++ b/SDWebImage/UIButton+WebCache.h @@ -7,13 +7,12 @@ */ #import "SDWebImageCompat.h" -#import "SDWebImageManagerDelegate.h" #import "SDWebImageManager.h" /** * Integrates SDWebImage async downloading and caching of remote images with UIButtonView. */ -@interface UIButton (WebCache) +@interface UIButton (WebCache) /** * Set the imageView `image` with an `url`. @@ -21,8 +20,9 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. */ -- (void)setImageWithURL:(NSURL *)url; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state; /** * Set the imageView `image` with an `url` and a placeholder. @@ -30,10 +30,11 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. * @see setImageWithURL:placeholderImage:options: */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; /** * Set the imageView `image` with an `url`, placeholder and custom options. @@ -41,22 +42,25 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; -#if NS_BLOCKS_AVAILABLE /** * Set the imageView `image` with an `url`. * * The downloand is asynchronous and cached. * * @param url The url for the image. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder. @@ -64,11 +68,14 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder and custom options. @@ -76,13 +83,15 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; -#endif +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the backgroundImageView `image` with an `url`. @@ -90,8 +99,9 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. */ -- (void)setBackgroundImageWithURL:(NSURL *)url; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state; /** * Set the backgroundImageView `image` with an `url` and a placeholder. @@ -99,10 +109,11 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. * @see setImageWithURL:placeholderImage:options: */ -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder; /** * Set the backgroundImageView `image` with an `url`, placeholder and custom options. @@ -110,22 +121,25 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. */ -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; -#if NS_BLOCKS_AVAILABLE /** * Set the backgroundImageView `image` with an `url`. * * The downloand is asynchronous and cached. * * @param url The url for the image. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param state The state that uses the specified title. The values are described in UIControlState. + * @param completedBlock A block object to be executed after the request operation + * completed. This block has no return value and takes three argument: the requested + * `UIImage` object, the `NSError` object describing error that occurred, and an + * `SDImageCacheType` enum describing the source of the image obtained from. */ -- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the backgroundImageView `image` with an `url`, placeholder. @@ -133,11 +147,14 @@ * The downloand is asynchronous and cached. * * @param url The url for the image. + * @param state The state that uses the specified title. The values are described in UIControlState. * @param placeholder The image to be set initially, until the image request finishes. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block object to be executed after the request operation + * completed. This block has no return value and takes three argument: the requested + * `UIImage` object, the `NSError` object describing error that occurred, and an + * `SDImageCacheType` enum describing the source of the image obtained from. */ -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the backgroundImageView `image` with an `url`, placeholder and custom options. @@ -147,12 +164,12 @@ * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block object to be executed after the request operation + * completed. This block has no return value and takes three argument: the requested + * `UIImage` object, the `NSError` object describing error that occurred, and an + * `SDImageCacheType` enum describing the source of the image obtained from. */ -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; -#endif - +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock; /** * Cancel the current download diff --git a/SDWebImage/UIButton+WebCache.m b/SDWebImage/UIButton+WebCache.m index 2b61c1a8b..3b8420e9b 100644 --- a/SDWebImage/UIButton+WebCache.m +++ b/SDWebImage/UIButton+WebCache.m @@ -7,161 +7,130 @@ */ #import "UIButton+WebCache.h" -#import "SDWebImageManager.h" +#import "objc/runtime.h" + +static char operationKey; @implementation UIButton (WebCache) -- (void)setImageWithURL:(NSURL *)url +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self setImageWithURL:url placeholderImage:nil]; + [self setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self setImageWithURL:url placeholderImage:placeholder options:0]; + [self setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; - - [self setImage:placeholder forState:UIControlStateNormal]; - [self setImage:placeholder forState:UIControlStateSelected]; - [self setImage:placeholder forState:UIControlStateHighlighted]; - - - if (url) - { - [manager downloadWithURL:url delegate:self options:options]; - } + [self setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; } -#if NS_BLOCKS_AVAILABLE -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:nil success:success failure:failure]; + [self setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; } - -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure]; + [self setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; + [self cancelCurrentImageLoad]; - [self setImage:placeholder forState:UIControlStateNormal]; - [self setImage:placeholder forState:UIControlStateSelected]; - [self setImage:placeholder forState:UIControlStateHighlighted]; + [self setImage:placeholder forState:state]; if (url) { - [manager downloadWithURL:url delegate:self options:options success:success failure:failure]; + __weak UIButton *wself = self; + id operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (!wself) return; + dispatch_main_sync_safe(^ + { + __strong UIButton *sself = wself; + if (!sself) return; + if (image) + { + [sself setImage:image forState:state]; + } + if (completedBlock && finished) + { + completedBlock(image, error, cacheType); + } + }); + }]; + objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } -#endif -- (void)setBackgroundImageWithURL:(NSURL *)url +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state { - [self setBackgroundImageWithURL:url placeholderImage:nil]; + [self setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil]; } -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder { - [self setBackgroundImageWithURL:url placeholderImage:placeholder options:0]; + [self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil]; } -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; - - [self setBackgroundImage:placeholder forState:UIControlStateNormal]; - [self setBackgroundImage:placeholder forState:UIControlStateSelected]; - [self setBackgroundImage:placeholder forState:UIControlStateHighlighted]; - - if (url) - { - NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"]; - [manager downloadWithURL:url delegate:self options:options userInfo:info]; - } + [self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil]; } -#if NS_BLOCKS_AVAILABLE -- (void)setBackgroundImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state completed:(SDWebImageCompletedBlock)completedBlock { - [self setBackgroundImageWithURL:url placeholderImage:nil success:success failure:failure]; + [self setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock]; } -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self setBackgroundImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure]; + [self setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock]; } -- (void)setBackgroundImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setBackgroundImageWithURL:(NSURL *)url forState:(UIControlState)state placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; + [self cancelCurrentImageLoad]; - [self setBackgroundImage:placeholder forState:UIControlStateNormal]; - [self setBackgroundImage:placeholder forState:UIControlStateSelected]; - [self setBackgroundImage:placeholder forState:UIControlStateHighlighted]; + [self setBackgroundImage:placeholder forState:state]; if (url) { - NSDictionary *info = [NSDictionary dictionaryWithObject:@"background" forKey:@"type"]; - [manager downloadWithURL:url delegate:self options:options userInfo:info success:success failure:failure]; + __weak UIButton *wself = self; + id operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (!wself) return; + dispatch_main_sync_safe(^ + { + __strong UIButton *sself = wself; + if (!sself) return; + if (image) + { + [sself setBackgroundImage:image forState:state]; + } + if (completedBlock && finished) + { + completedBlock(image, error, cacheType); + } + }); + }]; + objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } -#endif - (void)cancelCurrentImageLoad { - [[SDWebImageManager sharedManager] cancelForDelegate:self]; -} - -- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info -{ - if ([[info valueForKey:@"type"] isEqualToString:@"background"]) - { - [self setBackgroundImage:image forState:UIControlStateNormal]; - [self setBackgroundImage:image forState:UIControlStateSelected]; - [self setBackgroundImage:image forState:UIControlStateHighlighted]; - } - else - { - [self setImage:image forState:UIControlStateNormal]; - [self setImage:image forState:UIControlStateSelected]; - [self setImage:image forState:UIControlStateHighlighted]; - } -} - - -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image forURL:(NSURL *)url userInfo:(NSDictionary *)info -{ - if ([[info valueForKey:@"type"] isEqualToString:@"background"]) - { - [self setBackgroundImage:image forState:UIControlStateNormal]; - [self setBackgroundImage:image forState:UIControlStateSelected]; - [self setBackgroundImage:image forState:UIControlStateHighlighted]; - } - else + // Cancel in progress downloader from queue + id operation = objc_getAssociatedObject(self, &operationKey); + if (operation) { - [self setImage:image forState:UIControlStateNormal]; - [self setImage:image forState:UIControlStateSelected]; - [self setImage:image forState:UIControlStateHighlighted]; + [operation cancel]; + objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } diff --git a/SDWebImage/UIImage+GIF.h b/SDWebImage/UIImage+GIF.h new file mode 100755 index 000000000..1183035b3 --- /dev/null +++ b/SDWebImage/UIImage+GIF.h @@ -0,0 +1,19 @@ +// +// UIImage+GIF.h +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "NSData+GIF.h" +#import + +@interface UIImage (GIF) + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name; ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data; + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size; + +@end diff --git a/SDWebImage/UIImage+GIF.m b/SDWebImage/UIImage+GIF.m new file mode 100755 index 000000000..d19fa4129 --- /dev/null +++ b/SDWebImage/UIImage+GIF.m @@ -0,0 +1,145 @@ +// +// UIImage+GIF.m +// LBGIFImage +// +// Created by Laurin Brandner on 06.01.12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "UIImage+GIF.h" +#import + +@implementation UIImage (GIF) + ++ (UIImage *)sd_animatedGIFWithData:(NSData *)data +{ + if (!data) + { + return nil; + } + + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); + + size_t count = CGImageSourceGetCount(source); + + UIImage *animatedImage; + + if (count <= 1) + { + animatedImage = [[UIImage alloc] initWithData:data]; + } + else + { + NSMutableArray *images = [NSMutableArray array]; + + NSTimeInterval duration = 0.0f; + + for (size_t i = 0; i < count; i++) + { + CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); + + NSDictionary *frameProperties = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source, i, NULL)); + duration += [[[frameProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary] objectForKey:(NSString*)kCGImagePropertyGIFDelayTime] doubleValue]; + + [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; + + CGImageRelease(image); + } + + if (!duration) + { + duration = (1.0f/10.0f)*count; + } + + animatedImage = [UIImage animatedImageWithImages:images duration:duration]; + } + + CFRelease(source); + + return animatedImage; +} + ++ (UIImage *)sd_animatedGIFNamed:(NSString *)name +{ + CGFloat scale = [UIScreen mainScreen].scale; + + if (scale > 1.0f) + { + NSString *retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:retinaPath]; + + if (data) + { + return [UIImage sd_animatedGIFWithData:data]; + } + + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + data = [NSData dataWithContentsOfFile:path]; + + if (data) + { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } + else + { + NSString *path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"]; + + NSData *data = [NSData dataWithContentsOfFile:path]; + + if (data) + { + return [UIImage sd_animatedGIFWithData:data]; + } + + return [UIImage imageNamed:name]; + } +} + +- (UIImage *)sd_animatedImageByScalingAndCroppingToSize:(CGSize)size +{ + if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) + { + return self; + } + + CGSize scaledSize = size; + CGPoint thumbnailPoint = CGPointZero; + + CGFloat widthFactor = size.width / self.size.width; + CGFloat heightFactor = size.height / self.size.height; + CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor :heightFactor; + scaledSize.width = self.size.width * scaleFactor; + scaledSize.height = self.size.height * scaleFactor; + + if (widthFactor > heightFactor) + { + thumbnailPoint.y = (size.height - scaledSize.height) * 0.5; + } + else if (widthFactor < heightFactor) + { + thumbnailPoint.x = (size.width - scaledSize.width) * 0.5; + } + + NSMutableArray *scaledImages = [NSMutableArray array]; + + UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); + + for (UIImage *image in self.images) + { + [image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + + [scaledImages addObject:newImage]; + } + + UIGraphicsEndImageContext(); + + return [UIImage animatedImageWithImages:scaledImages duration:self.duration]; +} + +@end diff --git a/SDWebImage/UIImage+MultiFormat.h b/SDWebImage/UIImage+MultiFormat.h new file mode 100644 index 000000000..186ebc0a5 --- /dev/null +++ b/SDWebImage/UIImage+MultiFormat.h @@ -0,0 +1,15 @@ +// +// UIImage+MultiFormat.h +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import + +@interface UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data; + +@end diff --git a/SDWebImage/UIImage+MultiFormat.m b/SDWebImage/UIImage+MultiFormat.m new file mode 100644 index 000000000..63ba82583 --- /dev/null +++ b/SDWebImage/UIImage+MultiFormat.m @@ -0,0 +1,41 @@ +// +// UIImage+MultiFormat.m +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#import "UIImage+MultiFormat.h" +#import "UIImage+GIF.h" + +#ifdef SD_WEBP +#import "UIImage+WebP.h" +#endif + +@implementation UIImage (MultiFormat) + ++ (UIImage *)sd_imageWithData:(NSData *)data +{ + UIImage *image; + + if ([data sd_isGIF]) + { + image = [UIImage sd_animatedGIFWithData:data]; + } + else + { + image = [[UIImage alloc] initWithData:data]; + } + +#ifdef SD_WEBP + if (!image) // TODO: detect webp signature + { + image = [UIImage sd_imageWithWebPData:data]; + } +#endif + + return image; +} + +@end diff --git a/SDWebImage/UIImage+WebP.h b/SDWebImage/UIImage+WebP.h new file mode 100644 index 000000000..66e42f630 --- /dev/null +++ b/SDWebImage/UIImage+WebP.h @@ -0,0 +1,22 @@ +// +// UIImage+WebP.h +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#ifdef SD_WEBP +#import + +// Fix for issue #416 Undefined symbols for architecture armv7 since WebP introduction when deploying to device +void WebPInitPremultiplyNEON(void); +void WebPInitUpsamplersNEON(void); +void VP8DspInitNEON(void); + +@interface UIImage (WebP) + ++ (UIImage *)sd_imageWithWebPData:(NSData *)data; + +@end +#endif diff --git a/SDWebImage/UIImage+WebP.m b/SDWebImage/UIImage+WebP.m new file mode 100644 index 000000000..59efecb2c --- /dev/null +++ b/SDWebImage/UIImage+WebP.m @@ -0,0 +1,69 @@ +// +// UIImage+WebP.m +// SDWebImage +// +// Created by Olivier Poitrey on 07/06/13. +// Copyright (c) 2013 Dailymotion. All rights reserved. +// + +#ifdef SD_WEBP +#import "UIImage+WebP.h" +#import "webp/decode.h" + +// Callback for CGDataProviderRelease +static void FreeImageData(void *info, const void *data, size_t size) +{ + free((void *)data); +} + +@implementation UIImage (WebP) + ++ (UIImage *)sd_imageWithWebPData:(NSData *)data +{ + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) + { + return nil; + } + + config.output.colorspace = MODE_rgbA; + config.options.use_threads = 1; + + // Decode the WebP image data into a RGBA value array. + if (WebPDecode(data.bytes, data.length, &config) != VP8_STATUS_OK) + { + return nil; + } + + int width = config.input.width; + int height = config.input.height; + if (config.options.use_scaling) + { + width = config.options.scaled_width; + height = config.options.scaled_height; + } + + // Construct a UIImage from the decoded RGBA value array. + CGDataProviderRef provider = + CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); + CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast; + CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; + CGImageRef imageRef = CGImageCreate(width, height, 8, 32, 4 * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); + + CGColorSpaceRelease(colorSpaceRef); + CGDataProviderRelease(provider); + + UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; + CGImageRelease(imageRef); + + return image; +} + +@end + +// Functions to resolve some undefined symbols when using WebP and force_load flag +void WebPInitPremultiplyNEON(void) {} +void WebPInitUpsamplersNEON(void) {} +void VP8DspInitNEON(void) {} +#endif diff --git a/SDWebImage/UIImageView+WebCache.h b/SDWebImage/UIImageView+WebCache.h index 7a83c2008..952a1e9df 100644 --- a/SDWebImage/UIImageView+WebCache.h +++ b/SDWebImage/UIImageView+WebCache.h @@ -7,7 +7,6 @@ */ #import "SDWebImageCompat.h" -#import "SDWebImageManagerDelegate.h" #import "SDWebImageManager.h" /** @@ -15,34 +14,36 @@ * * Usage with a UITableViewCell sub-class: * - * #import - * - * ... - * - * - (UITableViewCell *)tableView:(UITableView *)tableView - * cellForRowAtIndexPath:(NSIndexPath *)indexPath - * { - * static NSString *MyIdentifier = @"MyIdentifier"; - * - * UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; - * - * if (cell == nil) - * { - * cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault - * reuseIdentifier:MyIdentifier] autorelease]; - * } - * - * // Here we use the provided setImageWithURL: method to load the web image - * // Ensure you use a placeholder image otherwise cells will be initialized with no image - * [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] - * placeholderImage:[UIImage imageNamed:@"placeholder"]]; - * - * cell.textLabel.text = @"My Text"; - * return cell; - * } - * + * @code + +#import + +... + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *MyIdentifier = @"MyIdentifier"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; + + if (cell == nil) + { + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] + autorelease]; + } + + // Here we use the provided setImageWithURL: method to load the web image + // Ensure you use a placeholder image otherwise cells will be initialized with no image + [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"] + placeholderImage:[UIImage imageNamed:@"placeholder"]]; + + cell.textLabel.text = @"My Text"; + return cell; +} + + * @endcode */ -@interface UIImageView (WebCache) +@interface UIImageView (WebCache) /** * Set the imageView `image` with an `url`. @@ -75,17 +76,18 @@ */ - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options; -#if NS_BLOCKS_AVAILABLE /** * Set the imageView `image` with an `url`. * * The downloand is asynchronous and cached. * * @param url The url for the image. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder. @@ -94,10 +96,12 @@ * * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock; /** * Set the imageView `image` with an `url`, placeholder and custom options. @@ -107,15 +111,41 @@ * @param url The url for the image. * @param placeholder The image to be set initially, until the image request finishes. * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. - * @param success A block to be executed when the image request succeed This block has no return value and takes the retrieved image as argument. - * @param failure A block object to be executed when the image request failed. This block has no return value and takes the error object describing the network or parsing error that occurred (may be nil). + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. */ -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; -#endif +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock; + +/** + * Set the imageView `image` with an `url`, placeholder and custom options. + * + * The downloand is asynchronous and cached. + * + * @param url The url for the image. + * @param placeholder The image to be set initially, until the image request finishes. + * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values. + * @param progressBlock A block called while image is downloading + * @param completedBlock A block called when operation has been completed. This block as no return value + * and takes the requested UIImage as first parameter. In case of error the image parameter + * is nil and the second parameter may contain an NSError. The third parameter is a Boolean + * indicating if the image was retrived from the local cache of from the network. + */ +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock; + +/** + * Download an array of images and starts them in an animation loop + * + *@param arrayOfURLs An array of NSURL + */ +-(void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs; /** * Cancel the current download */ - (void)cancelCurrentImageLoad; +- (void)cancelCurrentArrayLoad; + @end diff --git a/SDWebImage/UIImageView+WebCache.m b/SDWebImage/UIImageView+WebCache.m index 090c82cda..fa772236e 100644 --- a/SDWebImage/UIImageView+WebCache.m +++ b/SDWebImage/UIImageView+WebCache.m @@ -7,76 +7,134 @@ */ #import "UIImageView+WebCache.h" +#import "objc/runtime.h" + +static char operationKey; +static char operationArrayKey; @implementation UIImageView (WebCache) - (void)setImageWithURL:(NSURL *)url { - [self setImageWithURL:url placeholderImage:nil]; + [self setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil]; } - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder { - [self setImageWithURL:url placeholderImage:placeholder options:0]; + [self setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil]; } - (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; - - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; - - self.image = placeholder; - - if (url) - { - [manager downloadWithURL:url delegate:self options:options]; - } + [self setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil]; } -#if NS_BLOCKS_AVAILABLE -- (void)setImageWithURL:(NSURL *)url success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:nil success:success failure:failure]; + [self setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock { - [self setImageWithURL:url placeholderImage:placeholder options:0 success:success failure:failure]; + [self setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock]; } -- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options success:(SDWebImageSuccessBlock)success failure:(SDWebImageFailureBlock)failure; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock { - SDWebImageManager *manager = [SDWebImageManager sharedManager]; + [self setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock]; +} - // Remove in progress downloader from queue - [manager cancelForDelegate:self]; +- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedBlock)completedBlock +{ + [self cancelCurrentImageLoad]; self.image = placeholder; - + if (url) { - [manager downloadWithURL:url delegate:self options:options success:success failure:failure]; + __weak UIImageView *wself = self; + id operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (!wself) return; + dispatch_main_sync_safe(^ + { + __strong UIImageView *sself = wself; + if (!sself) return; + if (image) + { + sself.image = image; + [sself setNeedsLayout]; + } + if (completedBlock && finished) + { + completedBlock(image, error, cacheType); + } + }); + }]; + objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } -#endif -- (void)cancelCurrentImageLoad +- (void)setAnimationImagesWithURLs:(NSArray *)arrayOfURLs { - [[SDWebImageManager sharedManager] cancelForDelegate:self]; + [self cancelCurrentArrayLoad]; + __weak UIImageView *wself = self; + + NSMutableArray *operationsArray = [[NSMutableArray alloc] init]; + + for (NSURL *logoImageURL in arrayOfURLs) + { + id operation = [SDWebImageManager.sharedManager downloadWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) + { + if (!wself) return; + dispatch_main_sync_safe(^ + { + __strong UIImageView *sself = wself; + [sself stopAnimating]; + if (sself && image) + { + NSMutableArray *currentImages = [[sself animationImages] mutableCopy]; + if (!currentImages) + { + currentImages = [[NSMutableArray alloc] init]; + } + [currentImages addObject:image]; + + sself.animationImages = currentImages; + [sself setNeedsLayout]; + } + [sself startAnimating]; + }); + }]; + [operationsArray addObject:operation]; + } + + objc_setAssociatedObject(self, &operationArrayKey, [NSArray arrayWithArray:operationsArray], OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -- (void)webImageManager:(SDWebImageManager *)imageManager didProgressWithPartialImage:(UIImage *)image forURL:(NSURL *)url +- (void)cancelCurrentImageLoad { - self.image = image; - [self setNeedsLayout]; + // Cancel in progress downloader from queue + id operation = objc_getAssociatedObject(self, &operationKey); + if (operation) + { + [operation cancel]; + objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } } -- (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)image +- (void)cancelCurrentArrayLoad { - self.image = image; - [self setNeedsLayout]; + // Cancel in progress downloader from queue + NSArray *operations = objc_getAssociatedObject(self, &operationArrayKey); + for (id operation in operations) + { + if (operation) + { + [operation cancel]; + } + } + objc_setAssociatedObject(self, &operationArrayKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } @end diff --git a/Vendors/libwebp b/Vendors/libwebp new file mode 160000 index 000000000..b25a6fbfd --- /dev/null +++ b/Vendors/libwebp @@ -0,0 +1 @@ +Subproject commit b25a6fbfdcdddd3b5e5a9596f36dda7981eb9b9b