├── .DS_Store
├── HTSDCycleScrollViewDemo
├── .DS_Store
├── HTSDCycleScrollViewDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ ├── cherongyi.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ │ │ └── ios_zzy.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ ├── cherongyi.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ └── ios_zzy.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── HTSDCycleScrollViewDemo
│ ├── .DS_Store
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── img1.imageset
│ │ ├── Contents.json
│ │ └── img1.png
│ ├── img2.imageset
│ │ ├── Contents.json
│ │ └── img2.png
│ ├── img3.imageset
│ │ ├── Contents.json
│ │ └── img3.png
│ └── img4.imageset
│ │ ├── Contents.json
│ │ └── img4.png
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── ViewController.h
│ ├── ViewController.m
│ └── main.m
├── Other
├── .DS_Store
├── normal.png
└── zoom.png
├── README.md
├── SDCycleScrollView
├── PageControl
│ ├── TAAbstractDotView.h
│ ├── TAAbstractDotView.m
│ ├── TAAnimatedDotView.h
│ ├── TAAnimatedDotView.m
│ ├── TADotView.h
│ ├── TADotView.m
│ ├── TAPageControl.h
│ └── TAPageControl.m
├── SDCollectionViewCell.h
├── SDCollectionViewCell.m
├── SDCycleScrollView.h
├── SDCycleScrollView.m
├── UIView+SDExtension.h
└── UIView+SDExtension.m
└── SDWebImage
├── MKAnnotationView+WebCache.h
├── MKAnnotationView+WebCache.m
├── NSData+ImageContentType.h
├── NSData+ImageContentType.m
├── NSImage+WebCache.h
├── NSImage+WebCache.m
├── SDImageCache.h
├── SDImageCache.m
├── SDImageCacheConfig.h
├── SDImageCacheConfig.m
├── SDWebImageCompat.h
├── SDWebImageCompat.m
├── SDWebImageDecoder.h
├── SDWebImageDecoder.m
├── SDWebImageDownloader.h
├── SDWebImageDownloader.m
├── SDWebImageDownloaderOperation.h
├── SDWebImageDownloaderOperation.m
├── SDWebImageManager.h
├── SDWebImageManager.m
├── SDWebImageOperation.h
├── SDWebImagePrefetcher.h
├── SDWebImagePrefetcher.m
├── UIButton+WebCache.h
├── UIButton+WebCache.m
├── UIImage+GIF.h
├── UIImage+GIF.m
├── UIImage+MultiFormat.h
├── UIImage+MultiFormat.m
├── UIImage+WebP.h
├── UIImage+WebP.m
├── UIImageView+HighlightedWebCache.h
├── UIImageView+HighlightedWebCache.m
├── UIImageView+WebCache.h
├── UIImageView+WebCache.m
├── UIView+WebCache.h
├── UIView+WebCache.m
├── UIView+WebCacheOperation.h
└── UIView+WebCacheOperation.m
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/.DS_Store
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/.DS_Store
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/xcuserdata/cherongyi.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/xcuserdata/cherongyi.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/xcuserdata/ios_zzy.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/project.xcworkspace/xcuserdata/ios_zzy.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/xcuserdata/cherongyi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/xcuserdata/cherongyi.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | HTSDCycleScrollViewDemo.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/xcuserdata/ios_zzy.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo.xcodeproj/xcuserdata/ios_zzy.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | HTSDCycleScrollViewDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/.DS_Store
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // HTSDCycleScrollViewDemo
4 | //
5 | // Created by iOS_zzy on 2018/3/27.
6 | // Copyright © 2018年 runThor. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // HTSDCycleScrollViewDemo
4 | //
5 | // Created by iOS_zzy on 2018/3/27.
6 | // Copyright © 2018年 runThor. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "img1.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img1.imageset/img1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img1.imageset/img1.png
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "img2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img2.imageset/img2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img2.imageset/img2.png
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "img3.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img3.imageset/img3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img3.imageset/img3.png
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "img4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img4.imageset/img4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Assets.xcassets/img4.imageset/img4.png
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // HTSDCycleScrollViewDemo
4 | //
5 | // Created by iOS_zzy on 2018/3/27.
6 | // Copyright © 2018年 runThor. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // HTSDCycleScrollViewDemo
4 | //
5 | // Created by iOS_zzy on 2018/3/27.
6 | // Copyright © 2018年 runThor. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "SDCycleScrollView.h"
11 |
12 | @interface ViewController ()
13 |
14 | @property (strong, nonatomic) SDCycleScrollView *adCycleScrollView;
15 | @property (strong, nonatomic) SDCycleScrollView *noticeCycleScrollView;
16 |
17 | @end
18 |
19 | @implementation ViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 |
24 | // 轮播图
25 | SDCycleScrollView *adCycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 100, Screen_Width, 200) delegate:self placeholderImage:[UIImage new]];
26 | adCycleScrollView.zoomType = YES; // 是否使用缩放效果
27 | adCycleScrollView.pageControlStyle = SDCycleScrollViewPageContolStyleAnimated;
28 | adCycleScrollView.scrollDirection = UICollectionViewScrollDirectionHorizontal;
29 | adCycleScrollView.currentPageDotColor = [UIColor whiteColor];
30 | adCycleScrollView.pageDotColor = [UIColor colorWithWhite:1 alpha:0.5];
31 | adCycleScrollView.pageControlDotSize = CGSizeMake(20, 6); // pageControl小点的大小
32 | // adCycleScrollView.imageURLStringsGroup = @[]; // 网络图片
33 | adCycleScrollView.localizationImageNamesGroup = @[@"img1", @"img2", @"img3", @"img4"]; // 本地图片
34 | [self.view addSubview:adCycleScrollView];
35 | self.adCycleScrollView = adCycleScrollView;
36 |
37 | // 轮播公告
38 | SDCycleScrollView *noticeView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 350, Screen_Width, 30) delegate:self placeholderImage:nil];
39 | noticeView.backgroundColor = [UIColor lightGrayColor];
40 | noticeView.onlyDisplayText = YES;
41 | noticeView.titlesGroup = @[@"公告1", @"公告2", @"公告3"];
42 | noticeView.scrollDirection = UICollectionViewScrollDirectionVertical;
43 | [self.view addSubview:noticeView];
44 | self.noticeCycleScrollView = noticeView;
45 | }
46 |
47 | #pragma mark - SDCycleScrollViewDelegate
48 |
49 | // 点击轮播内容
50 | - (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index {
51 | if (cycleScrollView == self.adCycleScrollView) {
52 | [cycleScrollView adjustWhenControllerViewWillAppera];
53 | NSLog(@"点击轮播图");
54 | } else {
55 | NSLog(@"点击轮播公告");
56 | }
57 | }
58 |
59 | - (void)didReceiveMemoryWarning {
60 | [super didReceiveMemoryWarning];
61 | // Dispose of any resources that can be recreated.
62 | }
63 |
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/HTSDCycleScrollViewDemo/HTSDCycleScrollViewDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // HTSDCycleScrollViewDemo
4 | //
5 | // Created by iOS_zzy on 2018/3/27.
6 | // Copyright © 2018年 runThor. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Other/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/Other/.DS_Store
--------------------------------------------------------------------------------
/Other/normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/Other/normal.png
--------------------------------------------------------------------------------
/Other/zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/runThor/HTSDCycleScrollView/0547b39fee96520ef3150397ce8a61d20b38fd8b/Other/zoom.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HTSDCycleScrollView
2 | 一款基于SDScrollView的轮播图控件,对源码进行了修改,可切换为滚动时图片带有缩放效果的模式,可改变PageControl小圆点的样式。
3 |
4 | #### 更新:修改了卡片拖动的判断偏移量,更易拖动切换卡片。
5 | ## Show
6 | ### 普通模式:
7 | 
8 | ### 缩放模式:
9 | 
10 | ## Usage
11 | ```Objective-C
12 | // 基本使用方式与SDScrollView一致,新增zoomType属性,用于切换普通模式与缩放模式
13 | // ViewController.m
14 |
15 | #import "SDCycleScrollView.h"
16 |
17 | - (void)viewDidLoad {
18 | [super viewDidLoad];
19 |
20 | // 轮播图
21 | SDCycleScrollView *adCycleScrollView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 100, Screen_Width, 200) delegate:self placeholderImage:[UIImage new]];
22 | adCycleScrollView.zoomType = YES; // 是否使用缩放效果,YES为缩放模式,NO为普通模式
23 | adCycleScrollView.pageControlStyle = SDCycleScrollViewPageContolStyleAnimated;
24 | adCycleScrollView.scrollDirection = UICollectionViewScrollDirectionHorizontal;
25 | adCycleScrollView.currentPageDotColor = [UIColor whiteColor];
26 | adCycleScrollView.pageDotColor = [UIColor colorWithWhite:1 alpha:0.5];
27 | adCycleScrollView.pageControlDotSize = CGSizeMake(20, 6); // pageControl小圆点的大小
28 | // adCycleScrollView.imageURLStringsGroup = @[]; // 网络图片
29 | adCycleScrollView.localizationImageNamesGroup = @[@"img1", @"img2", @"img3", @"img4"]; // 本地图片
30 | [self.view addSubview:adCycleScrollView];
31 |
32 | // 轮播公告
33 | SDCycleScrollView *noticeView = [SDCycleScrollView cycleScrollViewWithFrame:CGRectMake(0, 350, Screen_Width, 30) delegate:self placeholderImage:nil];
34 | noticeView.backgroundColor = [UIColor lightGrayColor];
35 | noticeView.onlyDisplayText = YES;
36 | noticeView.titlesGroup = @[@"公告1", @"公告2", @"公告3"];
37 | noticeView.scrollDirection = UICollectionViewScrollDirectionVertical;
38 | [self.view addSubview:noticeView];
39 | }
40 |
41 | ```
42 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAAbstractDotView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TAAbstractDotView.h
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 | @interface TAAbstractDotView : UIView
13 |
14 |
15 | /**
16 | * A method call let view know which state appearance it should take. Active meaning it's current page. Inactive not the current page.
17 | *
18 | * @param active BOOL to tell if view is active or not
19 | */
20 | - (void)changeActivityState:(BOOL)active;
21 |
22 |
23 | @end
24 |
25 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAAbstractDotView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TAAbstractDotView.m
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TAAbstractDotView.h"
10 |
11 |
12 | @implementation TAAbstractDotView
13 |
14 |
15 | - (id)init
16 | {
17 | @throw [NSException exceptionWithName:NSInternalInconsistencyException
18 | reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class]
19 | userInfo:nil];
20 | }
21 |
22 |
23 | - (void)changeActivityState:(BOOL)active
24 | {
25 | @throw [NSException exceptionWithName:NSInternalInconsistencyException
26 | reason:[NSString stringWithFormat:@"You must override %@ in %@", NSStringFromSelector(_cmd), self.class]
27 | userInfo:nil];
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAAnimatedDotView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TAAnimatedDotView.h
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TAAbstractDotView.h"
10 |
11 | @interface TAAnimatedDotView : TAAbstractDotView
12 |
13 | @property (nonatomic, strong) UIColor *dotColor;
14 | @property (nonatomic, strong) UIColor *normalColor;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAAnimatedDotView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TAAnimatedDotView.m
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TAAnimatedDotView.h"
10 |
11 | static CGFloat const kAnimateDuration = 1;
12 |
13 | @implementation TAAnimatedDotView
14 |
15 | - (instancetype)init
16 | {
17 | self = [super init];
18 | if (self) {
19 | [self initialization];
20 | }
21 |
22 | return self;
23 | }
24 |
25 |
26 | - (id)initWithFrame:(CGRect)frame
27 | {
28 | self = [super initWithFrame:frame];
29 | if (self) {
30 | [self initialization];
31 | }
32 | return self;
33 | }
34 |
35 |
36 | - (id)initWithCoder:(NSCoder *)aDecoder
37 | {
38 | self = [super initWithCoder:aDecoder];
39 | if (self) {
40 | [self initialization];
41 | }
42 |
43 | return self;
44 | }
45 |
46 | - (void)setDotColor:(UIColor *)dotColor
47 | {
48 | _dotColor = dotColor;
49 | self.layer.borderColor = dotColor.CGColor;
50 | }
51 |
52 | - (void)initialization
53 | {
54 | _dotColor = [UIColor whiteColor];
55 | self.backgroundColor = [UIColor colorWithWhite:1 alpha:0.5];
56 | self.layer.cornerRadius = 2;
57 | // self.layer.borderColor = [UIColor whiteColor].CGColor;
58 | // self.layer.borderWidth = 2;
59 | }
60 |
61 |
62 | - (void)changeActivityState:(BOOL)active
63 | {
64 | if (active) {
65 | [self animateToActiveState];
66 | } else {
67 | [self animateToDeactiveState];
68 | }
69 | }
70 |
71 |
72 | - (void)animateToActiveState
73 | {
74 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:-20 options:UIViewAnimationOptionCurveLinear animations:^{
75 | self.backgroundColor = _dotColor;
76 | self.transform = CGAffineTransformMakeScale(1.4, 1.4);
77 | } completion:nil];
78 | }
79 |
80 | - (void)animateToDeactiveState
81 | {
82 | [UIView animateWithDuration:kAnimateDuration delay:0 usingSpringWithDamping:.5 initialSpringVelocity:0 options:UIViewAnimationOptionCurveLinear animations:^{
83 | self.backgroundColor = self.normalColor;
84 | self.transform = CGAffineTransformIdentity;
85 | } completion:nil];
86 | }
87 |
88 | @end
89 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TADotView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TADotView.h
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TAAbstractDotView.h"
10 |
11 | @interface TADotView : TAAbstractDotView
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TADotView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TADotView.m
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-22.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TADotView.h"
10 |
11 | @implementation TADotView
12 |
13 |
14 | - (instancetype)init
15 | {
16 | self = [super init];
17 | if (self) {
18 | [self initialization];
19 | }
20 |
21 | return self;
22 | }
23 |
24 |
25 | - (id)initWithFrame:(CGRect)frame
26 | {
27 | self = [super initWithFrame:frame];
28 | if (self) {
29 | [self initialization];
30 | }
31 | return self;
32 | }
33 |
34 |
35 | - (id)initWithCoder:(NSCoder *)aDecoder
36 | {
37 | self = [super initWithCoder:aDecoder];
38 | if (self) {
39 | [self initialization];
40 | }
41 |
42 | return self;
43 | }
44 |
45 | - (void)initialization
46 | {
47 | self.backgroundColor = [UIColor clearColor];
48 | self.layer.cornerRadius = CGRectGetWidth(self.frame) / 2;
49 | self.layer.borderColor = [UIColor whiteColor].CGColor;
50 | self.layer.borderWidth = 2;
51 | }
52 |
53 |
54 | - (void)changeActivityState:(BOOL)active
55 | {
56 | if (active) {
57 | self.backgroundColor = [UIColor whiteColor];
58 | } else {
59 | self.backgroundColor = [UIColor clearColor];
60 | }
61 | }
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAPageControl.h:
--------------------------------------------------------------------------------
1 | //
2 | // TAPageControl.h
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-21.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol TAPageControlDelegate;
12 |
13 |
14 | @interface TAPageControl : UIControl
15 |
16 |
17 | /**
18 | * Dot view customization properties
19 | */
20 |
21 | /**
22 | * The Class of your custom UIView, make sure to respect the TAAbstractDotView class.
23 | */
24 | @property (nonatomic) Class dotViewClass;
25 |
26 |
27 | /**
28 | * UIImage to represent a dot.
29 | */
30 | @property (nonatomic) UIImage *dotImage;
31 |
32 |
33 | /**
34 | * UIImage to represent current page dot.
35 | */
36 | @property (nonatomic) UIImage *currentDotImage;
37 |
38 |
39 | /**
40 | * Dot size for dot views. Default is 8 by 8.
41 | */
42 | @property (nonatomic) CGSize dotSize;
43 |
44 |
45 | @property (nonatomic, strong) UIColor *dotColor;
46 |
47 | @property (nonatomic, strong) UIColor *normalColor;
48 |
49 | /**
50 | * Spacing between two dot views. Default is 8.
51 | */
52 | @property (nonatomic) NSInteger spacingBetweenDots;
53 |
54 |
55 | /**
56 | * Page control setup properties
57 | */
58 |
59 |
60 | /**
61 | * Delegate for TAPageControl
62 | */
63 | @property(nonatomic,assign) id delegate;
64 |
65 |
66 | /**
67 | * Number of pages for control. Default is 0.
68 | */
69 | @property (nonatomic) NSInteger numberOfPages;
70 |
71 |
72 | /**
73 | * Current page on which control is active. Default is 0.
74 | */
75 | @property (nonatomic) NSInteger currentPage;
76 |
77 |
78 | /**
79 | * Hide the control if there is only one page. Default is NO.
80 | */
81 | @property (nonatomic) BOOL hidesForSinglePage;
82 |
83 |
84 | /**
85 | * Let the control know if should grow bigger by keeping center, or just get longer (right side expanding). By default YES.
86 | */
87 | @property (nonatomic) BOOL shouldResizeFromCenter;
88 |
89 |
90 | /**
91 | * Return the minimum size required to display control properly for the given page count.
92 | *
93 | * @param pageCount Number of dots that will require display
94 | *
95 | * @return The CGSize being the minimum size required.
96 | */
97 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount;
98 |
99 |
100 | @end
101 |
102 |
103 | @protocol TAPageControlDelegate
104 |
105 | @optional
106 | - (void)TAPageControl:(TAPageControl *)pageControl didSelectPageAtIndex:(NSInteger)index;
107 |
108 | @end
109 |
--------------------------------------------------------------------------------
/SDCycleScrollView/PageControl/TAPageControl.m:
--------------------------------------------------------------------------------
1 | //
2 | // TAPageControl.m
3 | // TAPageControl
4 | //
5 | // Created by Tanguy Aladenise on 2015-01-21.
6 | // Copyright (c) 2015 Tanguy Aladenise. All rights reserved.
7 | //
8 |
9 | #import "TAPageControl.h"
10 | #import "TAAbstractDotView.h"
11 | #import "TAAnimatedDotView.h"
12 | #import "TADotView.h"
13 |
14 | /**
15 | * Default number of pages for initialization
16 | */
17 | static NSInteger const kDefaultNumberOfPages = 0;
18 |
19 | /**
20 | * Default current page for initialization
21 | */
22 | static NSInteger const kDefaultCurrentPage = 0;
23 |
24 | /**
25 | * Default setting for hide for single page feature. For initialization
26 | */
27 | static BOOL const kDefaultHideForSinglePage = NO;
28 |
29 | /**
30 | * Default setting for shouldResizeFromCenter. For initialiation
31 | */
32 | static BOOL const kDefaultShouldResizeFromCenter = YES;
33 |
34 | /**
35 | * Default spacing between dots
36 | */
37 | static NSInteger const kDefaultSpacingBetweenDots = 8;
38 |
39 | /**
40 | * Default dot size
41 | */
42 | static CGSize const kDefaultDotSize = {8, 8};
43 |
44 |
45 | @interface TAPageControl()
46 |
47 |
48 | /**
49 | * Array of dot views for reusability and touch events.
50 | */
51 | @property (strong, nonatomic) NSMutableArray *dots;
52 |
53 |
54 | @end
55 |
56 | @implementation TAPageControl
57 |
58 |
59 | #pragma mark - Lifecycle
60 |
61 |
62 | - (id)init
63 | {
64 | self = [super init];
65 | if (self) {
66 | [self initialization];
67 | }
68 |
69 | return self;
70 | }
71 |
72 |
73 | - (id)initWithFrame:(CGRect)frame
74 | {
75 | self = [super initWithFrame:frame];
76 | if (self) {
77 | [self initialization];
78 | }
79 | return self;
80 | }
81 |
82 |
83 | - (id)initWithCoder:(NSCoder *)aDecoder
84 | {
85 | self = [super initWithCoder:aDecoder];
86 | if (self) {
87 | [self initialization];
88 | }
89 |
90 | return self;
91 | }
92 |
93 |
94 | /**
95 | * Default setup when initiating control
96 | */
97 | - (void)initialization
98 | {
99 | self.dotViewClass = [TAAnimatedDotView class];
100 | self.spacingBetweenDots = kDefaultSpacingBetweenDots;
101 | self.numberOfPages = kDefaultNumberOfPages;
102 | self.currentPage = kDefaultCurrentPage;
103 | self.hidesForSinglePage = kDefaultHideForSinglePage;
104 | self.shouldResizeFromCenter = kDefaultShouldResizeFromCenter;
105 | }
106 |
107 |
108 | #pragma mark - Touch event
109 |
110 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
111 | {
112 | UITouch *touch = [touches anyObject];
113 | if (touch.view != self) {
114 | NSInteger index = [self.dots indexOfObject:touch.view];
115 | if ([self.delegate respondsToSelector:@selector(TAPageControl:didSelectPageAtIndex:)]) {
116 | [self.delegate TAPageControl:self didSelectPageAtIndex:index];
117 | }
118 | }
119 | }
120 |
121 | #pragma mark - Layout
122 |
123 |
124 | /**
125 | * Resizes and moves the receiver view so it just encloses its subviews.
126 | */
127 | - (void)sizeToFit
128 | {
129 | [self updateFrame:YES];
130 | }
131 |
132 |
133 | - (CGSize)sizeForNumberOfPages:(NSInteger)pageCount
134 | {
135 | return CGSizeMake((self.dotSize.width + self.spacingBetweenDots) * pageCount - self.spacingBetweenDots , self.dotSize.height);
136 | }
137 |
138 |
139 | /**
140 | * Will update dots display and frame. Reuse existing views or instantiate one if required. Update their position in case frame changed.
141 | */
142 | - (void)updateDots
143 | {
144 | if (self.numberOfPages == 0) {
145 | return;
146 | }
147 |
148 | for (NSInteger i = 0; i < self.numberOfPages; i++) {
149 |
150 | UIView *dot;
151 | if (i < self.dots.count) {
152 | dot = [self.dots objectAtIndex:i];
153 | } else {
154 | dot = [self generateDotView];
155 | }
156 |
157 | [self updateDotFrame:dot atIndex:i];
158 | }
159 |
160 | [self changeActivity:YES atIndex:self.currentPage];
161 |
162 | [self hideForSinglePage];
163 | }
164 |
165 |
166 | /**
167 | * Update frame control to fit current number of pages. It will apply required size if authorize and required.
168 | *
169 | * @param overrideExistingFrame BOOL to allow frame to be overriden. Meaning the required size will be apply no mattter what.
170 | */
171 | - (void)updateFrame:(BOOL)overrideExistingFrame
172 | {
173 | CGPoint center = self.center;
174 | CGSize requiredSize = [self sizeForNumberOfPages:self.numberOfPages];
175 |
176 | // We apply requiredSize only if authorize to and necessary
177 | if (overrideExistingFrame || ((CGRectGetWidth(self.frame) < requiredSize.width || CGRectGetHeight(self.frame) < requiredSize.height) && !overrideExistingFrame)) {
178 | self.frame = CGRectMake(CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), requiredSize.width, requiredSize.height);
179 | if (self.shouldResizeFromCenter) {
180 | self.center = center;
181 | }
182 | }
183 |
184 | [self resetDotViews];
185 | }
186 |
187 |
188 | /**
189 | * Update the frame of a specific dot at a specific index
190 | *
191 | * @param dot Dot view
192 | * @param index Page index of dot
193 | */
194 | - (void)updateDotFrame:(UIView *)dot atIndex:(NSInteger)index
195 | {
196 | // Dots are always centered within view
197 | CGFloat x = (self.dotSize.width + self.spacingBetweenDots) * index + ( (CGRectGetWidth(self.frame) - [self sizeForNumberOfPages:self.numberOfPages].width) / 2);
198 | CGFloat y = (CGRectGetHeight(self.frame) - self.dotSize.height) / 2;
199 |
200 | dot.frame = CGRectMake(x, y, self.dotSize.width, self.dotSize.height);
201 | }
202 |
203 |
204 | #pragma mark - Utils
205 |
206 |
207 | /**
208 | * Generate a dot view and add it to the collection
209 | *
210 | * @return The UIView object representing a dot
211 | */
212 | - (UIView *)generateDotView
213 | {
214 | UIView *dotView;
215 |
216 | if (self.dotViewClass) {
217 | dotView = [[self.dotViewClass alloc] initWithFrame:CGRectMake(0, 0, self.dotSize.width, self.dotSize.height)];
218 | if ([dotView isKindOfClass:[TAAnimatedDotView class]] && self.dotColor) {
219 | ((TAAnimatedDotView *)dotView).dotColor = self.dotColor;
220 | ((TAAnimatedDotView *)dotView).normalColor = self.normalColor;
221 | }
222 | } else {
223 | dotView = [[UIImageView alloc] initWithImage:self.dotImage];
224 | dotView.frame = CGRectMake(0, 0, self.dotSize.width, self.dotSize.height);
225 | }
226 |
227 | if (dotView) {
228 | [self addSubview:dotView];
229 | [self.dots addObject:dotView];
230 | }
231 |
232 | dotView.userInteractionEnabled = YES;
233 | return dotView;
234 | }
235 |
236 |
237 | /**
238 | * Change activity state of a dot view. Current/not currrent.
239 | *
240 | * @param active Active state to apply
241 | * @param index Index of dot for state update
242 | */
243 | - (void)changeActivity:(BOOL)active atIndex:(NSInteger)index
244 | {
245 | if (self.dotViewClass) {
246 | TAAbstractDotView *abstractDotView = (TAAbstractDotView *)[self.dots objectAtIndex:index];
247 | if ([abstractDotView respondsToSelector:@selector(changeActivityState:)]) {
248 | [abstractDotView changeActivityState:active];
249 | } else {
250 | NSLog(@"Custom view : %@ must implement an 'changeActivityState' method or you can subclass %@ to help you.", self.dotViewClass, [TAAbstractDotView class]);
251 | }
252 | } else if (self.dotImage && self.currentDotImage) {
253 | UIImageView *dotView = (UIImageView *)[self.dots objectAtIndex:index];
254 | dotView.image = (active) ? self.currentDotImage : self.dotImage;
255 | }
256 | }
257 |
258 |
259 | - (void)resetDotViews
260 | {
261 | for (UIView *dotView in self.dots) {
262 | [dotView removeFromSuperview];
263 | }
264 |
265 | [self.dots removeAllObjects];
266 | [self updateDots];
267 | }
268 |
269 |
270 | - (void)hideForSinglePage
271 | {
272 | if (self.dots.count == 1 && self.hidesForSinglePage) {
273 | self.hidden = YES;
274 | } else {
275 | self.hidden = NO;
276 | }
277 | }
278 |
279 | #pragma mark - Setters
280 |
281 |
282 | - (void)setNumberOfPages:(NSInteger)numberOfPages
283 | {
284 | _numberOfPages = numberOfPages;
285 |
286 | // Update dot position to fit new number of pages
287 | [self resetDotViews];
288 | }
289 |
290 |
291 | - (void)setSpacingBetweenDots:(NSInteger)spacingBetweenDots
292 | {
293 | _spacingBetweenDots = spacingBetweenDots;
294 |
295 | [self resetDotViews];
296 | }
297 |
298 |
299 | - (void)setCurrentPage:(NSInteger)currentPage
300 | {
301 | // If no pages, no current page to treat.
302 | if (self.numberOfPages == 0 || currentPage == _currentPage) {
303 | _currentPage = currentPage;
304 | return;
305 | }
306 |
307 | // Pre set
308 | [self changeActivity:NO atIndex:_currentPage];
309 | _currentPage = currentPage;
310 | // Post set
311 | [self changeActivity:YES atIndex:_currentPage];
312 | }
313 |
314 |
315 | - (void)setDotImage:(UIImage *)dotImage
316 | {
317 | _dotImage = dotImage;
318 | [self resetDotViews];
319 | self.dotViewClass = nil;
320 | }
321 |
322 |
323 | - (void)setCurrentDotImage:(UIImage *)currentDotimage
324 | {
325 | _currentDotImage = currentDotimage;
326 | [self resetDotViews];
327 | self.dotViewClass = nil;
328 | }
329 |
330 |
331 | - (void)setDotViewClass:(Class)dotViewClass
332 | {
333 | _dotViewClass = dotViewClass;
334 | self.dotSize = CGSizeZero;
335 | [self resetDotViews];
336 | }
337 |
338 |
339 | #pragma mark - Getters
340 |
341 |
342 | - (NSMutableArray *)dots
343 | {
344 | if (!_dots) {
345 | _dots = [[NSMutableArray alloc] init];
346 | }
347 |
348 | return _dots;
349 | }
350 |
351 |
352 | - (CGSize)dotSize
353 | {
354 | // Dot size logic depending on the source of the dot view
355 | if (self.dotImage && CGSizeEqualToSize(_dotSize, CGSizeZero)) {
356 | _dotSize = self.dotImage.size;
357 | } else if (self.dotViewClass && CGSizeEqualToSize(_dotSize, CGSizeZero)) {
358 | _dotSize = kDefaultDotSize;
359 | return _dotSize;
360 | }
361 |
362 | return _dotSize;
363 | }
364 |
365 | @end
366 |
--------------------------------------------------------------------------------
/SDCycleScrollView/SDCollectionViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // SDCollectionViewCell.h
3 | // SDCycleScrollView
4 | //
5 | // Created by aier on 15-3-22.
6 | // Copyright (c) 2015年 GSD. All rights reserved.
7 | //
8 |
9 | /*
10 |
11 | *********************************************************************************
12 | *
13 | * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟
14 | *
15 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
16 | * 帮您解决问题。
17 | * 新浪微博:GSD_iOS
18 | * Email : gsdios@126.com
19 | * GitHub: https://github.com/gsdios
20 | *
21 | * 另(我的自动布局库SDAutoLayout):
22 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
23 | * 做最简单易用的AutoLayout库。
24 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
25 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
26 | * GitHub:https://github.com/gsdios/SDAutoLayout
27 | *********************************************************************************
28 |
29 | */
30 |
31 |
32 |
33 | #import
34 |
35 | #define MaxWidth 300
36 | #define MinWidth 250
37 | #define MaxHeight 200
38 | #define MinHeight 170
39 | #define Screen_Width [[UIScreen mainScreen] bounds].size.width
40 | #define Screen_Height [[UIScreen mainScreen] bounds].size.height
41 |
42 | @interface SDCollectionViewCell : UICollectionViewCell
43 |
44 | @property (weak, nonatomic) UIImageView *imageView;
45 | @property (copy, nonatomic) NSString *title;
46 |
47 | @property (nonatomic, strong) UIColor *titleLabelTextColor;
48 | @property (nonatomic, strong) UIFont *titleLabelTextFont;
49 | @property (nonatomic, strong) UIColor *titleLabelBackgroundColor;
50 | @property (nonatomic, assign) CGFloat titleLabelHeight;
51 |
52 | @property (nonatomic, assign) BOOL hasConfigured;
53 |
54 | /** 只展示文字轮播 */
55 | @property (nonatomic, assign) BOOL onlyDisplayText;
56 |
57 | @property (assign, nonatomic) BOOL zoomType;
58 |
59 | @end
60 |
--------------------------------------------------------------------------------
/SDCycleScrollView/SDCollectionViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // SDCollectionViewCell.m
3 | // SDCycleScrollView
4 | //
5 | // Created by aier on 15-3-22.
6 | // Copyright (c) 2015年 GSD. All rights reserved.
7 | //
8 |
9 |
10 | /*
11 |
12 | *********************************************************************************
13 | *
14 | * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟
15 | *
16 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
17 | * 帮您解决问题。
18 | * 新浪微博:GSD_iOS
19 | * Email : gsdios@126.com
20 | * GitHub: https://github.com/gsdios
21 | *
22 | * 另(我的自动布局库SDAutoLayout):
23 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
24 | * 做最简单易用的AutoLayout库。
25 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
26 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
27 | * GitHub:https://github.com/gsdios/SDAutoLayout
28 | *********************************************************************************
29 |
30 | */
31 |
32 |
33 | #import "SDCollectionViewCell.h"
34 | #import "UIView+SDExtension.h"
35 |
36 |
37 | @implementation SDCollectionViewCell
38 | {
39 | __weak UILabel *_titleLabel;
40 | }
41 |
42 |
43 | - (instancetype)initWithFrame:(CGRect)frame
44 | {
45 | if (self = [super initWithFrame:frame]) {
46 | [self setupImageView];
47 | [self setupTitleLabel];
48 | }
49 |
50 | return self;
51 | }
52 |
53 | - (void)setTitleLabelBackgroundColor:(UIColor *)titleLabelBackgroundColor
54 | {
55 | _titleLabelBackgroundColor = titleLabelBackgroundColor;
56 | _titleLabel.backgroundColor = titleLabelBackgroundColor;
57 | }
58 |
59 | - (void)setTitleLabelTextColor:(UIColor *)titleLabelTextColor
60 | {
61 | _titleLabelTextColor = titleLabelTextColor;
62 | _titleLabel.textColor = titleLabelTextColor;
63 | }
64 |
65 | - (void)setTitleLabelTextFont:(UIFont *)titleLabelTextFont
66 | {
67 | _titleLabelTextFont = titleLabelTextFont;
68 | _titleLabel.font = titleLabelTextFont;
69 | }
70 |
71 | - (void)setupImageView
72 | {
73 | UIImageView *imageView = [[UIImageView alloc] init];
74 | imageView.layer.cornerRadius = 5;
75 | imageView.layer.masksToBounds = YES;
76 | _imageView = imageView;
77 | [self.contentView addSubview:imageView];
78 | }
79 |
80 | - (void)setupTitleLabel
81 | {
82 | UILabel *titleLabel = [[UILabel alloc] init];
83 | _titleLabel = titleLabel;
84 | _titleLabel.hidden = YES;
85 | [self.contentView addSubview:titleLabel];
86 | }
87 |
88 | - (void)setTitle:(NSString *)title
89 | {
90 | _title = [title copy];
91 | _titleLabel.text = [NSString stringWithFormat:@" %@", title];
92 | if (_titleLabel.hidden) {
93 | _titleLabel.hidden = NO;
94 | }
95 | }
96 |
97 |
98 | - (void)layoutSubviews
99 | {
100 | [super layoutSubviews];
101 |
102 | if (self.onlyDisplayText) {
103 | _titleLabel.frame = self.bounds;
104 | } else {
105 | if (!self.zoomType) {
106 | _imageView.frame = self.bounds;
107 | }
108 |
109 | CGFloat titleLabelW = self.sd_width;
110 | CGFloat titleLabelH = _titleLabelHeight;
111 | CGFloat titleLabelX = 0;
112 | CGFloat titleLabelY = self.sd_height - titleLabelH;
113 | _titleLabel.frame = CGRectMake(titleLabelX, titleLabelY, titleLabelW, titleLabelH);
114 | }
115 | }
116 |
117 | @end
118 |
--------------------------------------------------------------------------------
/SDCycleScrollView/SDCycleScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // SDCycleScrollView.h
3 | // SDCycleScrollView
4 | //
5 | // Created by aier on 15-3-22.
6 | // Copyright (c) 2015年 GSD. All rights reserved.
7 | //
8 |
9 | /*
10 |
11 | *********************************************************************************
12 | *
13 | * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟
14 | *
15 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
16 | * 帮您解决问题。
17 | * 新浪微博:GSD_iOS
18 | * Email : gsdios@126.com
19 | * GitHub: https://github.com/gsdios
20 | *
21 | * 另(我的自动布局库SDAutoLayout):
22 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
23 | * 做最简单易用的AutoLayout库。
24 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
25 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
26 | * GitHub:https://github.com/gsdios/SDAutoLayout
27 | *********************************************************************************
28 |
29 | */
30 |
31 | /*
32 | * 当前版本为1.62
33 | * 更新日期:2016.04.21
34 | */
35 |
36 | #import
37 | #import "SDCollectionViewCell.h"
38 |
39 | typedef enum {
40 | SDCycleScrollViewPageContolAlimentRight,
41 | SDCycleScrollViewPageContolAlimentCenter
42 | } SDCycleScrollViewPageContolAliment;
43 |
44 | typedef enum {
45 | SDCycleScrollViewPageContolStyleClassic, // 系统自带经典样式
46 | SDCycleScrollViewPageContolStyleAnimated, // 动画效果pagecontrol
47 | SDCycleScrollViewPageContolStyleNone // 不显示pagecontrol
48 | } SDCycleScrollViewPageContolStyle;
49 |
50 | @class SDCycleScrollView;
51 |
52 | @protocol SDCycleScrollViewDelegate
53 |
54 | @optional
55 |
56 | /** 点击图片回调 */
57 | - (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index;
58 |
59 | /** 图片滚动回调 */
60 | - (void)cycleScrollView:(SDCycleScrollView *)cycleScrollView didScrollToIndex:(NSInteger)index;
61 |
62 | @end
63 |
64 | @interface SDCycleScrollView : UIView
65 |
66 |
67 | /** 初始轮播图(推荐使用) */
68 | + (instancetype)cycleScrollViewWithFrame:(CGRect)frame delegate:(id)delegate placeholderImage:(UIImage *)placeholderImage;
69 |
70 | + (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageURLStringsGroup:(NSArray *)imageURLStringsGroup;
71 |
72 |
73 | /** 本地图片轮播初始化方式 */
74 | + (instancetype)cycleScrollViewWithFrame:(CGRect)frame imageNamesGroup:(NSArray *)imageNamesGroup;
75 |
76 | /** 本地图片轮播初始化方式2,infiniteLoop:是否无限循环 */
77 | + (instancetype)cycleScrollViewWithFrame:(CGRect)frame shouldInfiniteLoop:(BOOL)infiniteLoop imageNamesGroup:(NSArray *)imageNamesGroup;
78 |
79 |
80 | ////////////////////// 数据源接口 //////////////////////
81 |
82 | /** 网络图片 url string 数组 */
83 | @property (nonatomic, strong) NSArray *imageURLStringsGroup;
84 |
85 | /** 每张图片对应要显示的文字数组 */
86 | @property (nonatomic, strong) NSArray *titlesGroup;
87 |
88 | /** 本地图片数组 */
89 | @property (nonatomic, strong) NSArray *localizationImageNamesGroup;
90 |
91 |
92 |
93 |
94 |
95 | ////////////////////// 滚动控制接口 //////////////////////
96 |
97 | /** 自动滚动间隔时间,默认2s */
98 | @property (nonatomic, assign) CGFloat autoScrollTimeInterval;
99 |
100 | /** 是否无限循环,默认Yes */
101 | @property (nonatomic,assign) BOOL infiniteLoop;
102 |
103 | /** 是否自动滚动,默认Yes */
104 | @property (nonatomic,assign) BOOL autoScroll;
105 |
106 | /** 图片滚动方向,默认为水平滚动 */
107 | @property (nonatomic, assign) UICollectionViewScrollDirection scrollDirection;
108 |
109 | @property (nonatomic, weak) id delegate;
110 |
111 | /** block方式监听点击 */
112 | @property (nonatomic, copy) void (^clickItemOperationBlock)(NSInteger currentIndex);
113 |
114 | /** block方式监听滚动 */
115 | @property (nonatomic, copy) void (^itemDidScrollOperationBlock)(NSInteger currentIndex);
116 |
117 | /** 解决viewWillAppear时出现时轮播图卡在一半的问题,在控制器viewWillAppear时调用此方法 */
118 | - (void)adjustWhenControllerViewWillAppera;
119 |
120 | ////////////////////// 自定义样式接口 //////////////////////
121 |
122 |
123 | /** 轮播图片的ContentMode,默认为 UIViewContentModeScaleToFill */
124 | @property (nonatomic, assign) UIViewContentMode bannerImageViewContentMode;
125 |
126 | /** 占位图,用于网络未加载到图片时 */
127 | @property (nonatomic, strong) UIImage *placeholderImage;
128 |
129 | /** 是否显示分页控件 */
130 | @property (nonatomic, assign) BOOL showPageControl;
131 |
132 | /** 是否在只有一张图时隐藏pagecontrol,默认为YES */
133 | @property(nonatomic) BOOL hidesForSinglePage;
134 |
135 | /** 只展示文字轮播 */
136 | @property (nonatomic, assign) BOOL onlyDisplayText;
137 |
138 | /** pagecontrol 样式,默认为动画样式 */
139 | @property (nonatomic, assign) SDCycleScrollViewPageContolStyle pageControlStyle;
140 |
141 | /** 分页控件位置 */
142 | @property (nonatomic, assign) SDCycleScrollViewPageContolAliment pageControlAliment;
143 |
144 | /** 分页控件距离轮播图的底部间距(在默认间距基础上)的偏移量 */
145 | @property (nonatomic, assign) CGFloat pageControlBottomOffset;
146 |
147 | /** 分页控件距离轮播图的右边间距(在默认间距基础上)的偏移量 */
148 | @property (nonatomic, assign) CGFloat pageControlRightOffset;
149 |
150 | /** 分页控件小圆标大小 */
151 | @property (nonatomic, assign) CGSize pageControlDotSize;
152 |
153 | /** 当前分页控件小圆标颜色 */
154 | @property (nonatomic, strong) UIColor *currentPageDotColor;
155 |
156 | /** 其他分页控件小圆标颜色 */
157 | @property (nonatomic, strong) UIColor *pageDotColor;
158 |
159 | /** 当前分页控件小圆标图片 */
160 | @property (nonatomic, strong) UIImage *currentPageDotImage;
161 |
162 | /** 其他分页控件小圆标图片 */
163 | @property (nonatomic, strong) UIImage *pageDotImage;
164 |
165 | /** 轮播文字label字体颜色 */
166 | @property (nonatomic, strong) UIColor *titleLabelTextColor;
167 |
168 | /** 轮播文字label字体大小 */
169 | @property (nonatomic, strong) UIFont *titleLabelTextFont;
170 |
171 | /** 轮播文字label背景颜色 */
172 | @property (nonatomic, strong) UIColor *titleLabelBackgroundColor;
173 |
174 | /** 轮播文字label高度 */
175 | @property (nonatomic, assign) CGFloat titleLabelHeight;
176 |
177 | @property (assign, nonatomic) BOOL zoomType;
178 |
179 | @property (nonatomic, weak) UICollectionView *mainView; // 显示图片的collectionView
180 |
181 | ////////////////////// 清除缓存接口 //////////////////////
182 |
183 | /** 清除图片缓存(此次升级后统一使用SDWebImage管理图片加载和缓存) */
184 | + (void)clearImagesCache;
185 |
186 | /** 清除图片缓存(兼容旧版本方法) */
187 | - (void)clearCache;
188 |
189 | @end
190 |
--------------------------------------------------------------------------------
/SDCycleScrollView/UIView+SDExtension.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+SDExtension.h
3 | // SDRefreshView
4 | //
5 | // Created by aier on 15-2-23.
6 | // Copyright (c) 2015年 GSD. All rights reserved.
7 | //
8 |
9 | /*
10 |
11 | *********************************************************************************
12 | *
13 | * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟
14 | *
15 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
16 | * 帮您解决问题。
17 | * 新浪微博:GSD_iOS
18 | * Email : gsdios@126.com
19 | * GitHub: https://github.com/gsdios
20 | *
21 | * 另(我的自动布局库SDAutoLayout):
22 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
23 | * 做最简单易用的AutoLayout库。
24 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
25 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
26 | * GitHub:https://github.com/gsdios/SDAutoLayout
27 | *********************************************************************************
28 |
29 | */
30 |
31 | #import
32 |
33 | #define SDColorCreater(r, g, b, a) [UIColor colorWithRed:(r / 255.0) green:(g / 255.0) blue:(b / 255.0) alpha:a]
34 |
35 |
36 | @interface UIView (SDExtension)
37 |
38 | @property (nonatomic, assign) CGFloat sd_height;
39 | @property (nonatomic, assign) CGFloat sd_width;
40 |
41 | @property (nonatomic, assign) CGFloat sd_y;
42 | @property (nonatomic, assign) CGFloat sd_x;
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/SDCycleScrollView/UIView+SDExtension.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+SDExtension.m
3 | // SDRefreshView
4 | //
5 | // Created by aier on 15-2-23.
6 | // Copyright (c) 2015年 GSD. All rights reserved.
7 | //
8 |
9 | /*
10 |
11 | *********************************************************************************
12 | *
13 | * 🌟🌟🌟 新建SDCycleScrollView交流QQ群:185534916 🌟🌟🌟
14 | *
15 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
16 | * 帮您解决问题。
17 | * 新浪微博:GSD_iOS
18 | * Email : gsdios@126.com
19 | * GitHub: https://github.com/gsdios
20 | *
21 | * 另(我的自动布局库SDAutoLayout):
22 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
23 | * 做最简单易用的AutoLayout库。
24 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
25 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
26 | * GitHub:https://github.com/gsdios/SDAutoLayout
27 | *********************************************************************************
28 |
29 | */
30 |
31 | /*
32 |
33 | *********************************************************************************
34 | *
35 | * 在您使用此自动轮播库的过程中如果出现bug请及时以以下任意一种方式联系我们,我们会及时修复bug并
36 | * 帮您解决问题。
37 | * 新浪微博:GSD_iOS
38 | * Email : gsdios@126.com
39 | * GitHub: https://github.com/gsdios
40 | *
41 | * 另(我的自动布局库SDAutoLayout):
42 | * 一行代码搞定自动布局!支持Cell和Tableview高度自适应,Label和ScrollView内容自适应,致力于
43 | * 做最简单易用的AutoLayout库。
44 | * 视频教程:http://www.letv.com/ptv/vplay/24038772.html
45 | * 用法示例:https://github.com/gsdios/SDAutoLayout/blob/master/README.md
46 | * GitHub:https://github.com/gsdios/SDAutoLayout
47 | *********************************************************************************
48 |
49 | */
50 |
51 |
52 | #import "UIView+SDExtension.h"
53 |
54 | @implementation UIView (SDExtension)
55 |
56 | - (CGFloat)sd_height
57 | {
58 | return self.frame.size.height;
59 | }
60 |
61 | - (void)setSd_height:(CGFloat)sd_height
62 | {
63 | CGRect temp = self.frame;
64 | temp.size.height = sd_height;
65 | self.frame = temp;
66 | }
67 |
68 | - (CGFloat)sd_width
69 | {
70 | return self.frame.size.width;
71 | }
72 |
73 | - (void)setSd_width:(CGFloat)sd_width
74 | {
75 | CGRect temp = self.frame;
76 | temp.size.width = sd_width;
77 | self.frame = temp;
78 | }
79 |
80 |
81 | - (CGFloat)sd_y
82 | {
83 | return self.frame.origin.y;
84 | }
85 |
86 | - (void)setSd_y:(CGFloat)sd_y
87 | {
88 | CGRect temp = self.frame;
89 | temp.origin.y = sd_y;
90 | self.frame = temp;
91 | }
92 |
93 | - (CGFloat)sd_x
94 | {
95 | return self.frame.origin.x;
96 | }
97 |
98 | - (void)setSd_x:(CGFloat)sd_x
99 | {
100 | CGRect temp = self.frame;
101 | temp.origin.x = sd_x;
102 | self.frame = temp;
103 | }
104 |
105 |
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/SDWebImage/MKAnnotationView+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import
14 | #import "SDWebImageManager.h"
15 |
16 | /**
17 | * Integrates SDWebImage async downloading and caching of remote images with MKAnnotationView.
18 | */
19 | @interface MKAnnotationView (WebCache)
20 |
21 | /**
22 | * Set the imageView `image` with an `url`.
23 | *
24 | * The download is asynchronous and cached.
25 | *
26 | * @param url The url for the image.
27 | */
28 | - (void)sd_setImageWithURL:(nullable NSURL *)url;
29 |
30 | /**
31 | * Set the imageView `image` with an `url` and a placeholder.
32 | *
33 | * The download is asynchronous and cached.
34 | *
35 | * @param url The url for the image.
36 | * @param placeholder The image to be set initially, until the image request finishes.
37 | * @see sd_setImageWithURL:placeholderImage:options:
38 | */
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url
40 | placeholderImage:(nullable UIImage *)placeholder;
41 |
42 | /**
43 | * Set the imageView `image` with an `url`, placeholder and custom options.
44 | *
45 | * The download is asynchronous and cached.
46 | *
47 | * @param url The url for the image.
48 | * @param placeholder The image to be set initially, until the image request finishes.
49 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
50 | */
51 |
52 | - (void)sd_setImageWithURL:(nullable NSURL *)url
53 | placeholderImage:(nullable UIImage *)placeholder
54 | options:(SDWebImageOptions)options;
55 |
56 | /**
57 | * Set the imageView `image` with an `url`.
58 | *
59 | * The download is asynchronous and cached.
60 | *
61 | * @param url The url for the image.
62 | * @param completedBlock A block called when operation has been completed. This block has no return value
63 | * and takes the requested UIImage as first parameter. In case of error the image parameter
64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
65 | * indicating if the image was retrieved from the local cache or from the network.
66 | * The fourth parameter is the original image url.
67 | */
68 | - (void)sd_setImageWithURL:(nullable NSURL *)url
69 | completed:(nullable SDExternalCompletionBlock)completedBlock;
70 |
71 | /**
72 | * Set the imageView `image` with an `url`, placeholder.
73 | *
74 | * The download is asynchronous and cached.
75 | *
76 | * @param url The url for the image.
77 | * @param placeholder The image to be set initially, until the image request finishes.
78 | * @param completedBlock A block called when operation has been completed. This block has no return value
79 | * and takes the requested UIImage as first parameter. In case of error the image parameter
80 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
81 | * indicating if the image was retrieved from the local cache or from the network.
82 | * The fourth parameter is the original image url.
83 | */
84 | - (void)sd_setImageWithURL:(nullable NSURL *)url
85 | placeholderImage:(nullable UIImage *)placeholder
86 | completed:(nullable SDExternalCompletionBlock)completedBlock;
87 |
88 | /**
89 | * Set the imageView `image` with an `url`, placeholder and custom options.
90 | *
91 | * The download is asynchronous and cached.
92 | *
93 | * @param url The url for the image.
94 | * @param placeholder The image to be set initially, until the image request finishes.
95 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
96 | * @param completedBlock A block called when operation has been completed. This block has no return value
97 | * and takes the requested UIImage as first parameter. In case of error the image parameter
98 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
99 | * indicating if the image was retrieved from the local cache or from the network.
100 | * The fourth parameter is the original image url.
101 | */
102 | - (void)sd_setImageWithURL:(nullable NSURL *)url
103 | placeholderImage:(nullable UIImage *)placeholder
104 | options:(SDWebImageOptions)options
105 | completed:(nullable SDExternalCompletionBlock)completedBlock;
106 |
107 | @end
108 |
109 | #endif
110 |
--------------------------------------------------------------------------------
/SDWebImage/MKAnnotationView+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "MKAnnotationView+WebCache.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 | #import "UIView+WebCache.h"
16 |
17 | @implementation MKAnnotationView (WebCache)
18 |
19 | - (void)sd_setImageWithURL:(nullable NSURL *)url {
20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:nil];
21 | }
22 |
23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:nil];
25 | }
26 |
27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options completed:nil];
29 | }
30 |
31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 completed:completedBlock];
33 | }
34 |
35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 completed:completedBlock];
37 | }
38 |
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url
40 | placeholderImage:(nullable UIImage *)placeholder
41 | options:(SDWebImageOptions)options
42 | completed:(nullable SDExternalCompletionBlock)completedBlock {
43 | __weak typeof(self)weakSelf = self;
44 | [self sd_internalSetImageWithURL:url
45 | placeholderImage:placeholder
46 | options:options
47 | operationKey:nil
48 | setImageBlock:^(UIImage *image, NSData *imageData) {
49 | weakSelf.image = image;
50 | }
51 | progress:nil
52 | completed:completedBlock];
53 | }
54 |
55 | @end
56 |
57 | #endif
58 |
--------------------------------------------------------------------------------
/SDWebImage/NSData+ImageContentType.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Fabrice Aneche
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 | #import "SDWebImageCompat.h"
12 |
13 | typedef NS_ENUM(NSInteger, SDImageFormat) {
14 | SDImageFormatUndefined = -1,
15 | SDImageFormatJPEG = 0,
16 | SDImageFormatPNG,
17 | SDImageFormatGIF,
18 | SDImageFormatTIFF,
19 | SDImageFormatWebP
20 | };
21 |
22 | @interface NSData (ImageContentType)
23 |
24 | /**
25 | * Return image format
26 | *
27 | * @param data the input image data
28 | *
29 | * @return the image format as `SDImageFormat` (enum)
30 | */
31 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/SDWebImage/NSData+ImageContentType.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Fabrice Aneche
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "NSData+ImageContentType.h"
11 |
12 |
13 | @implementation NSData (ImageContentType)
14 |
15 | + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data {
16 | if (!data) {
17 | return SDImageFormatUndefined;
18 | }
19 |
20 | uint8_t c;
21 | [data getBytes:&c length:1];
22 | switch (c) {
23 | case 0xFF:
24 | return SDImageFormatJPEG;
25 | case 0x89:
26 | return SDImageFormatPNG;
27 | case 0x47:
28 | return SDImageFormatGIF;
29 | case 0x49:
30 | case 0x4D:
31 | return SDImageFormatTIFF;
32 | case 0x52:
33 | // R as RIFF for WEBP
34 | if (data.length < 12) {
35 | return SDImageFormatUndefined;
36 | }
37 |
38 | NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
39 | if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
40 | return SDImageFormatWebP;
41 | }
42 | }
43 | return SDImageFormatUndefined;
44 | }
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/SDWebImage/NSImage+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_MAC
12 |
13 | #import
14 |
15 | @interface NSImage (WebCache)
16 |
17 | - (CGImageRef)CGImage;
18 | - (NSArray *)images;
19 | - (BOOL)isGIF;
20 |
21 | @end
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/SDWebImage/NSImage+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "NSImage+WebCache.h"
10 |
11 | #if SD_MAC
12 |
13 | @implementation NSImage (WebCache)
14 |
15 | - (CGImageRef)CGImage {
16 | NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
17 | CGImageRef cgImage = [self CGImageForProposedRect:&imageRect context:NULL hints:nil];
18 | return cgImage;
19 | }
20 |
21 | - (NSArray *)images {
22 | return nil;
23 | }
24 |
25 | - (BOOL)isGIF {
26 | return NO;
27 | }
28 |
29 | @end
30 |
31 | #endif
32 |
33 |
--------------------------------------------------------------------------------
/SDWebImage/SDImageCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 |
12 | @class SDImageCacheConfig;
13 |
14 | typedef NS_ENUM(NSInteger, SDImageCacheType) {
15 | /**
16 | * The image wasn't available the SDWebImage caches, but was downloaded from the web.
17 | */
18 | SDImageCacheTypeNone,
19 | /**
20 | * The image was obtained from the disk cache.
21 | */
22 | SDImageCacheTypeDisk,
23 | /**
24 | * The image was obtained from the memory cache.
25 | */
26 | SDImageCacheTypeMemory
27 | };
28 |
29 | typedef void(^SDCacheQueryCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, SDImageCacheType cacheType);
30 |
31 | typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
32 |
33 | typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
34 |
35 |
36 | /**
37 | * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
38 | * asynchronous so it doesn’t add unnecessary latency to the UI.
39 | */
40 | @interface SDImageCache : NSObject
41 |
42 | #pragma mark - Properties
43 |
44 | /**
45 | * Cache Config object - storing all kind of settings
46 | */
47 | @property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
48 |
49 | /**
50 | * The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
51 | */
52 | @property (assign, nonatomic) NSUInteger maxMemoryCost;
53 |
54 | /**
55 | * The maximum number of objects the cache should hold.
56 | */
57 | @property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
58 |
59 | #pragma mark - Singleton and initialization
60 |
61 | /**
62 | * Returns global shared cache instance
63 | *
64 | * @return SDImageCache global instance
65 | */
66 | + (nonnull instancetype)sharedImageCache;
67 |
68 | /**
69 | * Init a new cache store with a specific namespace
70 | *
71 | * @param ns The namespace to use for this cache store
72 | */
73 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;
74 |
75 | /**
76 | * Init a new cache store with a specific namespace and directory
77 | *
78 | * @param ns The namespace to use for this cache store
79 | * @param directory Directory to cache disk images in
80 | */
81 | - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
82 | diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
83 |
84 | #pragma mark - Cache paths
85 |
86 | - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
87 |
88 | /**
89 | * Add a read-only cache path to search for images pre-cached by SDImageCache
90 | * Useful if you want to bundle pre-loaded images with your app
91 | *
92 | * @param path The path to use for this read-only cache path
93 | */
94 | - (void)addReadOnlyCachePath:(nonnull NSString *)path;
95 |
96 | #pragma mark - Store Ops
97 |
98 | /**
99 | * Asynchronously store an image into memory and disk cache at the given key.
100 | *
101 | * @param image The image to store
102 | * @param key The unique image cache key, usually it's image absolute URL
103 | * @param completionBlock A block executed after the operation is finished
104 | */
105 | - (void)storeImage:(nullable UIImage *)image
106 | forKey:(nullable NSString *)key
107 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
108 |
109 | /**
110 | * Asynchronously store an image into memory and disk cache at the given key.
111 | *
112 | * @param image The image to store
113 | * @param key The unique image cache key, usually it's image absolute URL
114 | * @param toDisk Store the image to disk cache if YES
115 | * @param completionBlock A block executed after the operation is finished
116 | */
117 | - (void)storeImage:(nullable UIImage *)image
118 | forKey:(nullable NSString *)key
119 | toDisk:(BOOL)toDisk
120 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
121 |
122 | /**
123 | * Asynchronously store an image into memory and disk cache at the given key.
124 | *
125 | * @param image The image to store
126 | * @param imageData The image data as returned by the server, this representation will be used for disk storage
127 | * instead of converting the given image object into a storable/compressed image format in order
128 | * to save quality and CPU
129 | * @param key The unique image cache key, usually it's image absolute URL
130 | * @param toDisk Store the image to disk cache if YES
131 | * @param completionBlock A block executed after the operation is finished
132 | */
133 | - (void)storeImage:(nullable UIImage *)image
134 | imageData:(nullable NSData *)imageData
135 | forKey:(nullable NSString *)key
136 | toDisk:(BOOL)toDisk
137 | completion:(nullable SDWebImageNoParamsBlock)completionBlock;
138 |
139 | /**
140 | * Synchronously store image NSData into disk cache at the given key.
141 | *
142 | * @warning This method is synchronous, make sure to call it from the ioQueue
143 | *
144 | * @param imageData The image data to store
145 | * @param key The unique image cache key, usually it's image absolute URL
146 | */
147 | - (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;
148 |
149 | #pragma mark - Query and Retrieve Ops
150 |
151 | /**
152 | * Async check if image exists in disk cache already (does not load the image)
153 | *
154 | * @param key the key describing the url
155 | * @param completionBlock the block to be executed when the check is done.
156 | * @note the completion block will be always executed on the main queue
157 | */
158 | - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
159 |
160 | /**
161 | * Operation that queries the cache asynchronously and call the completion when done.
162 | *
163 | * @param key The unique key used to store the wanted image
164 | * @param doneBlock The completion block. Will not get called if the operation is cancelled
165 | *
166 | * @return a NSOperation instance containing the cache op
167 | */
168 | - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
169 |
170 | /**
171 | * Query the memory cache synchronously.
172 | *
173 | * @param key The unique key used to store the image
174 | */
175 | - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
176 |
177 | /**
178 | * Query the disk cache synchronously.
179 | *
180 | * @param key The unique key used to store the image
181 | */
182 | - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
183 |
184 | /**
185 | * Query the cache (memory and or disk) synchronously after checking the memory cache.
186 | *
187 | * @param key The unique key used to store the image
188 | */
189 | - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
190 |
191 | #pragma mark - Remove Ops
192 |
193 | /**
194 | * Remove the image from memory and disk cache asynchronously
195 | *
196 | * @param key The unique image cache key
197 | * @param completion A block that should be executed after the image has been removed (optional)
198 | */
199 | - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
200 |
201 | /**
202 | * Remove the image from memory and optionally disk cache asynchronously
203 | *
204 | * @param key The unique image cache key
205 | * @param fromDisk Also remove cache entry from disk if YES
206 | * @param completion A block that should be executed after the image has been removed (optional)
207 | */
208 | - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
209 |
210 | #pragma mark - Cache clean Ops
211 |
212 | /**
213 | * Clear all memory cached images
214 | */
215 | - (void)clearMemory;
216 |
217 | /**
218 | * Async clear all disk cached images. Non-blocking method - returns immediately.
219 | * @param completion A block that should be executed after cache expiration completes (optional)
220 | */
221 | - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
222 |
223 | /**
224 | * Async remove all expired cached image from disk. Non-blocking method - returns immediately.
225 | * @param completionBlock A block that should be executed after cache expiration completes (optional)
226 | */
227 | - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
228 |
229 | #pragma mark - Cache Info
230 |
231 | /**
232 | * Get the size used by the disk cache
233 | */
234 | - (NSUInteger)getSize;
235 |
236 | /**
237 | * Get the number of images in the disk cache
238 | */
239 | - (NSUInteger)getDiskCount;
240 |
241 | /**
242 | * Asynchronously calculate the disk cache's size.
243 | */
244 | - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
245 |
246 | #pragma mark - Cache Paths
247 |
248 | /**
249 | * Get the cache path for a certain key (needs the cache path root folder)
250 | *
251 | * @param key the key (can be obtained from url using cacheKeyForURL)
252 | * @param path the cache path root folder
253 | *
254 | * @return the cache path
255 | */
256 | - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
257 |
258 | /**
259 | * Get the default cache path for a certain key
260 | *
261 | * @param key the key (can be obtained from url using cacheKeyForURL)
262 | *
263 | * @return the default cache path
264 | */
265 | - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;
266 |
267 | @end
268 |
--------------------------------------------------------------------------------
/SDWebImage/SDImageCacheConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // SDImageCacheConfig.h
3 | // SDWebImage
4 | //
5 | // Created by Bogdan on 09/09/16.
6 | // Copyright © 2016 Dailymotion. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 |
12 | @interface SDImageCacheConfig : NSObject
13 |
14 | /**
15 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
16 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
17 | */
18 | @property (assign, nonatomic) BOOL shouldDecompressImages;
19 |
20 | /**
21 | * disable iCloud backup [defaults to YES]
22 | */
23 | @property (assign, nonatomic) BOOL shouldDisableiCloud;
24 |
25 | /**
26 | * use memory cache [defaults to YES]
27 | */
28 | @property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
29 |
30 | /**
31 | * The maximum length of time to keep an image in the cache, in seconds
32 | */
33 | @property (assign, nonatomic) NSInteger maxCacheAge;
34 |
35 | /**
36 | * The maximum size of the cache, in bytes.
37 | */
38 | @property (assign, nonatomic) NSUInteger maxCacheSize;
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/SDWebImage/SDImageCacheConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // SDImageCacheConfig.m
3 | // SDWebImage
4 | //
5 | // Created by Bogdan on 09/09/16.
6 | // Copyright © 2016 Dailymotion. All rights reserved.
7 | //
8 |
9 | #import "SDImageCacheConfig.h"
10 |
11 | static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
12 |
13 | @implementation SDImageCacheConfig
14 |
15 | - (instancetype)init {
16 | if (self = [super init]) {
17 | _shouldDecompressImages = YES;
18 | _shouldDisableiCloud = YES;
19 | _shouldCacheImagesInMemory = YES;
20 | _maxCacheAge = kDefaultCacheMaxCacheAge;
21 | _maxCacheSize = 0;
22 | }
23 | return self;
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageCompat.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Jamie Pinkham
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 |
12 | #ifdef __OBJC_GC__
13 | #error SDWebImage does not support Objective-C Garbage Collection
14 | #endif
15 |
16 | // Apple's defines from TargetConditionals.h are a bit weird.
17 | // Seems like TARGET_OS_MAC is always defined (on all platforms).
18 | // To determine if we are running on OSX, we can only relly on TARGET_OS_IPHONE=0 and all the other platforms
19 | #if !TARGET_OS_IPHONE && !TARGET_OS_IOS && !TARGET_OS_TV && !TARGET_OS_WATCH
20 | #define SD_MAC 1
21 | #else
22 | #define SD_MAC 0
23 | #endif
24 |
25 | // iOS and tvOS are very similar, UIKit exists on both platforms
26 | // Note: watchOS also has UIKit, but it's very limited
27 | #if TARGET_OS_IOS || TARGET_OS_TV
28 | #define SD_UIKIT 1
29 | #else
30 | #define SD_UIKIT 0
31 | #endif
32 |
33 | #if TARGET_OS_IOS
34 | #define SD_IOS 1
35 | #else
36 | #define SD_IOS 0
37 | #endif
38 |
39 | #if TARGET_OS_TV
40 | #define SD_TV 1
41 | #else
42 | #define SD_TV 0
43 | #endif
44 |
45 | #if TARGET_OS_WATCH
46 | #define SD_WATCH 1
47 | #else
48 | #define SD_WATCH 0
49 | #endif
50 |
51 |
52 | #if SD_MAC
53 | #import
54 | #ifndef UIImage
55 | #define UIImage NSImage
56 | #endif
57 | #ifndef UIImageView
58 | #define UIImageView NSImageView
59 | #endif
60 | #ifndef UIView
61 | #define UIView NSView
62 | #endif
63 | #else
64 | #if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
65 | #error SDWebImage doesn't support Deployment Target version < 5.0
66 | #endif
67 |
68 | #if SD_UIKIT
69 | #import
70 | #endif
71 | #if SD_WATCH
72 | #import
73 | #endif
74 | #endif
75 |
76 | #ifndef NS_ENUM
77 | #define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
78 | #endif
79 |
80 | #ifndef NS_OPTIONS
81 | #define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type
82 | #endif
83 |
84 | #if OS_OBJECT_USE_OBJC
85 | #undef SDDispatchQueueRelease
86 | #undef SDDispatchQueueSetterSementics
87 | #define SDDispatchQueueRelease(q)
88 | #define SDDispatchQueueSetterSementics strong
89 | #else
90 | #undef SDDispatchQueueRelease
91 | #undef SDDispatchQueueSetterSementics
92 | #define SDDispatchQueueRelease(q) (dispatch_release(q))
93 | #define SDDispatchQueueSetterSementics assign
94 | #endif
95 |
96 | extern UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
97 |
98 | typedef void(^SDWebImageNoParamsBlock)();
99 |
100 | extern NSString *const SDWebImageErrorDomain;
101 |
102 | #ifndef dispatch_main_async_safe
103 | #define dispatch_main_async_safe(block)\
104 | if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {\
105 | block();\
106 | } else {\
107 | dispatch_async(dispatch_get_main_queue(), block);\
108 | }
109 | #endif
110 |
111 | static int64_t kAsyncTestTimeout = 5;
112 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageCompat.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if !__has_feature(objc_arc)
12 | #error SDWebImage is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
13 | #endif
14 |
15 | inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullable image) {
16 | if (!image) {
17 | return nil;
18 | }
19 |
20 | #if SD_MAC
21 | return image;
22 | #elif SD_UIKIT || SD_WATCH
23 | if ((image.images).count > 0) {
24 | NSMutableArray *scaledImages = [NSMutableArray array];
25 |
26 | for (UIImage *tempImage in image.images) {
27 | [scaledImages addObject:SDScaledImageForKey(key, tempImage)];
28 | }
29 |
30 | return [UIImage animatedImageWithImages:scaledImages duration:image.duration];
31 | }
32 | else {
33 | #if SD_WATCH
34 | if ([[WKInterfaceDevice currentDevice] respondsToSelector:@selector(screenScale)]) {
35 | #elif SD_UIKIT
36 | if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
37 | #endif
38 | CGFloat scale = 1;
39 | if (key.length >= 8) {
40 | NSRange range = [key rangeOfString:@"@2x."];
41 | if (range.location != NSNotFound) {
42 | scale = 2.0;
43 | }
44 |
45 | range = [key rangeOfString:@"@3x."];
46 | if (range.location != NSNotFound) {
47 | scale = 3.0;
48 | }
49 | }
50 |
51 | UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation];
52 | image = scaledImage;
53 | }
54 | return image;
55 | }
56 | #endif
57 | }
58 |
59 | NSString *const SDWebImageErrorDomain = @"SDWebImageErrorDomain";
60 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageDecoder.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) james
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import
11 | #import "SDWebImageCompat.h"
12 |
13 | @interface UIImage (ForceDecode)
14 |
15 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image;
16 |
17 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageDecoder.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) james
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "SDWebImageDecoder.h"
11 |
12 | @implementation UIImage (ForceDecode)
13 |
14 | #if SD_UIKIT || SD_WATCH
15 | static const size_t kBytesPerPixel = 4;
16 | static const size_t kBitsPerComponent = 8;
17 |
18 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
19 | if (![UIImage shouldDecodeImage:image]) {
20 | return image;
21 | }
22 |
23 | // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.
24 | // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];
25 | @autoreleasepool{
26 |
27 | CGImageRef imageRef = image.CGImage;
28 | CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
29 |
30 | size_t width = CGImageGetWidth(imageRef);
31 | size_t height = CGImageGetHeight(imageRef);
32 | size_t bytesPerRow = kBytesPerPixel * width;
33 |
34 | // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
35 | // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
36 | // to create bitmap graphics contexts without alpha info.
37 | CGContextRef context = CGBitmapContextCreate(NULL,
38 | width,
39 | height,
40 | kBitsPerComponent,
41 | bytesPerRow,
42 | colorspaceRef,
43 | kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
44 | if (context == NULL) {
45 | return image;
46 | }
47 |
48 | // Draw the image into the context and retrieve the new bitmap image without alpha
49 | CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
50 | CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
51 | UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
52 | scale:image.scale
53 | orientation:image.imageOrientation];
54 |
55 | CGContextRelease(context);
56 | CGImageRelease(imageRefWithoutAlpha);
57 |
58 | return imageWithoutAlpha;
59 | }
60 | }
61 |
62 | /*
63 | * Defines the maximum size in MB of the decoded image when the flag `SDWebImageScaleDownLargeImages` is set
64 | * Suggested value for iPad1 and iPhone 3GS: 60.
65 | * Suggested value for iPad2 and iPhone 4: 120.
66 | * Suggested value for iPhone 3G and iPod 2 and earlier devices: 30.
67 | */
68 | static const CGFloat kDestImageSizeMB = 60.0f;
69 |
70 | /*
71 | * Defines the maximum size in MB of a tile used to decode image when the flag `SDWebImageScaleDownLargeImages` is set
72 | * Suggested value for iPad1 and iPhone 3GS: 20.
73 | * Suggested value for iPad2 and iPhone 4: 40.
74 | * Suggested value for iPhone 3G and iPod 2 and earlier devices: 10.
75 | */
76 | static const CGFloat kSourceImageTileSizeMB = 20.0f;
77 |
78 | static const CGFloat kBytesPerMB = 1024.0f * 1024.0f;
79 | static const CGFloat kPixelsPerMB = kBytesPerMB / kBytesPerPixel;
80 | static const CGFloat kDestTotalPixels = kDestImageSizeMB * kPixelsPerMB;
81 | static const CGFloat kTileTotalPixels = kSourceImageTileSizeMB * kPixelsPerMB;
82 |
83 | static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to overlap the seems where tiles meet.
84 |
85 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image {
86 | if (![UIImage shouldDecodeImage:image]) {
87 | return image;
88 | }
89 |
90 | if (![UIImage shouldScaleDownImage:image]) {
91 | return [UIImage decodedImageWithImage:image];
92 | }
93 |
94 | CGContextRef destContext;
95 |
96 | // autorelease the bitmap context and all vars to help system to free memory when there are memory warning.
97 | // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory];
98 | @autoreleasepool {
99 | CGImageRef sourceImageRef = image.CGImage;
100 |
101 | CGSize sourceResolution = CGSizeZero;
102 | sourceResolution.width = CGImageGetWidth(sourceImageRef);
103 | sourceResolution.height = CGImageGetHeight(sourceImageRef);
104 | float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
105 | // Determine the scale ratio to apply to the input image
106 | // that results in an output image of the defined size.
107 | // see kDestImageSizeMB, and how it relates to destTotalPixels.
108 | float imageScale = kDestTotalPixels / sourceTotalPixels;
109 | CGSize destResolution = CGSizeZero;
110 | destResolution.width = (int)(sourceResolution.width*imageScale);
111 | destResolution.height = (int)(sourceResolution.height*imageScale);
112 |
113 | // current color space
114 | CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:sourceImageRef];
115 |
116 | size_t bytesPerRow = kBytesPerPixel * destResolution.width;
117 |
118 | // Allocate enough pixel data to hold the output image.
119 | void* destBitmapData = malloc( bytesPerRow * destResolution.height );
120 | if (destBitmapData == NULL) {
121 | return image;
122 | }
123 |
124 | // kCGImageAlphaNone is not supported in CGBitmapContextCreate.
125 | // Since the original image here has no alpha info, use kCGImageAlphaNoneSkipLast
126 | // to create bitmap graphics contexts without alpha info.
127 | destContext = CGBitmapContextCreate(destBitmapData,
128 | destResolution.width,
129 | destResolution.height,
130 | kBitsPerComponent,
131 | bytesPerRow,
132 | colorspaceRef,
133 | kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
134 |
135 | if (destContext == NULL) {
136 | free(destBitmapData);
137 | return image;
138 | }
139 | CGContextSetInterpolationQuality(destContext, kCGInterpolationHigh);
140 |
141 | // Now define the size of the rectangle to be used for the
142 | // incremental blits from the input image to the output image.
143 | // we use a source tile width equal to the width of the source
144 | // image due to the way that iOS retrieves image data from disk.
145 | // iOS must decode an image from disk in full width 'bands', even
146 | // if current graphics context is clipped to a subrect within that
147 | // band. Therefore we fully utilize all of the pixel data that results
148 | // from a decoding opertion by achnoring our tile size to the full
149 | // width of the input image.
150 | CGRect sourceTile = CGRectZero;
151 | sourceTile.size.width = sourceResolution.width;
152 | // The source tile height is dynamic. Since we specified the size
153 | // of the source tile in MB, see how many rows of pixels high it
154 | // can be given the input image width.
155 | sourceTile.size.height = (int)(kTileTotalPixels / sourceTile.size.width );
156 | sourceTile.origin.x = 0.0f;
157 | // The output tile is the same proportions as the input tile, but
158 | // scaled to image scale.
159 | CGRect destTile;
160 | destTile.size.width = destResolution.width;
161 | destTile.size.height = sourceTile.size.height * imageScale;
162 | destTile.origin.x = 0.0f;
163 | // The source seem overlap is proportionate to the destination seem overlap.
164 | // this is the amount of pixels to overlap each tile as we assemble the ouput image.
165 | float sourceSeemOverlap = (int)((kDestSeemOverlap/destResolution.height)*sourceResolution.height);
166 | CGImageRef sourceTileImageRef;
167 | // calculate the number of read/write operations required to assemble the
168 | // output image.
169 | int iterations = (int)( sourceResolution.height / sourceTile.size.height );
170 | // If tile height doesn't divide the image height evenly, add another iteration
171 | // to account for the remaining pixels.
172 | int remainder = (int)sourceResolution.height % (int)sourceTile.size.height;
173 | if(remainder) {
174 | iterations++;
175 | }
176 | // Add seem overlaps to the tiles, but save the original tile height for y coordinate calculations.
177 | float sourceTileHeightMinusOverlap = sourceTile.size.height;
178 | sourceTile.size.height += sourceSeemOverlap;
179 | destTile.size.height += kDestSeemOverlap;
180 | for( int y = 0; y < iterations; ++y ) {
181 | @autoreleasepool {
182 | sourceTile.origin.y = y * sourceTileHeightMinusOverlap + sourceSeemOverlap;
183 | destTile.origin.y = destResolution.height - (( y + 1 ) * sourceTileHeightMinusOverlap * imageScale + kDestSeemOverlap);
184 | sourceTileImageRef = CGImageCreateWithImageInRect( sourceImageRef, sourceTile );
185 | if( y == iterations - 1 && remainder ) {
186 | float dify = destTile.size.height;
187 | destTile.size.height = CGImageGetHeight( sourceTileImageRef ) * imageScale;
188 | dify -= destTile.size.height;
189 | destTile.origin.y += dify;
190 | }
191 | CGContextDrawImage( destContext, destTile, sourceTileImageRef );
192 | CGImageRelease( sourceTileImageRef );
193 | }
194 | }
195 |
196 | CGImageRef destImageRef = CGBitmapContextCreateImage(destContext);
197 | CGContextRelease(destContext);
198 | if (destImageRef == NULL) {
199 | return image;
200 | }
201 | UIImage *destImage = [UIImage imageWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation];
202 | CGImageRelease(destImageRef);
203 | if (destImage == nil) {
204 | return image;
205 | }
206 | return destImage;
207 | }
208 | }
209 |
210 | + (BOOL)shouldDecodeImage:(nullable UIImage *)image {
211 | // Prevent "CGBitmapContextCreateImage: invalid context 0x0" error
212 | if (image == nil) {
213 | return NO;
214 | }
215 |
216 | // do not decode animated images
217 | if (image.images != nil) {
218 | return NO;
219 | }
220 |
221 | CGImageRef imageRef = image.CGImage;
222 |
223 | CGImageAlphaInfo alpha = CGImageGetAlphaInfo(imageRef);
224 | BOOL anyAlpha = (alpha == kCGImageAlphaFirst ||
225 | alpha == kCGImageAlphaLast ||
226 | alpha == kCGImageAlphaPremultipliedFirst ||
227 | alpha == kCGImageAlphaPremultipliedLast);
228 | // do not decode images with alpha
229 | if (anyAlpha) {
230 | return NO;
231 | }
232 |
233 | return YES;
234 | }
235 |
236 | + (BOOL)shouldScaleDownImage:(nonnull UIImage *)image {
237 | BOOL shouldScaleDown = YES;
238 |
239 | CGImageRef sourceImageRef = image.CGImage;
240 | CGSize sourceResolution = CGSizeZero;
241 | sourceResolution.width = CGImageGetWidth(sourceImageRef);
242 | sourceResolution.height = CGImageGetHeight(sourceImageRef);
243 | float sourceTotalPixels = sourceResolution.width * sourceResolution.height;
244 | float imageScale = kDestTotalPixels / sourceTotalPixels;
245 | if (imageScale < 1) {
246 | shouldScaleDown = YES;
247 | } else {
248 | shouldScaleDown = NO;
249 | }
250 |
251 | return shouldScaleDown;
252 | }
253 |
254 | + (CGColorSpaceRef)colorSpaceForImageRef:(CGImageRef)imageRef {
255 | // current
256 | CGColorSpaceModel imageColorSpaceModel = CGColorSpaceGetModel(CGImageGetColorSpace(imageRef));
257 | CGColorSpaceRef colorspaceRef = CGImageGetColorSpace(imageRef);
258 |
259 | BOOL unsupportedColorSpace = (imageColorSpaceModel == kCGColorSpaceModelUnknown ||
260 | imageColorSpaceModel == kCGColorSpaceModelMonochrome ||
261 | imageColorSpaceModel == kCGColorSpaceModelCMYK ||
262 | imageColorSpaceModel == kCGColorSpaceModelIndexed);
263 | if (unsupportedColorSpace) {
264 | colorspaceRef = CGColorSpaceCreateDeviceRGB();
265 | CFAutorelease(colorspaceRef);
266 | }
267 | return colorspaceRef;
268 | }
269 | #elif SD_MAC
270 | + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image {
271 | return image;
272 | }
273 |
274 | + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image {
275 | return image;
276 | }
277 | #endif
278 |
279 | @end
280 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageDownloader.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageCompat.h"
11 | #import "SDWebImageOperation.h"
12 |
13 | typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
14 | SDWebImageDownloaderLowPriority = 1 << 0,
15 | SDWebImageDownloaderProgressiveDownload = 1 << 1,
16 |
17 | /**
18 | * By default, request prevent the use of NSURLCache. With this flag, NSURLCache
19 | * is used with default policies.
20 | */
21 | SDWebImageDownloaderUseNSURLCache = 1 << 2,
22 |
23 | /**
24 | * Call completion block with nil image/imageData if the image was read from NSURLCache
25 | * (to be combined with `SDWebImageDownloaderUseNSURLCache`).
26 | */
27 |
28 | SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
29 | /**
30 | * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
31 | * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
32 | */
33 |
34 | SDWebImageDownloaderContinueInBackground = 1 << 4,
35 |
36 | /**
37 | * Handles cookies stored in NSHTTPCookieStore by setting
38 | * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
39 | */
40 | SDWebImageDownloaderHandleCookies = 1 << 5,
41 |
42 | /**
43 | * Enable to allow untrusted SSL certificates.
44 | * Useful for testing purposes. Use with caution in production.
45 | */
46 | SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,
47 |
48 | /**
49 | * Put the image in the high priority queue.
50 | */
51 | SDWebImageDownloaderHighPriority = 1 << 7,
52 |
53 | /**
54 | * Scale down the image
55 | */
56 | SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
57 | };
58 |
59 | typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
60 | /**
61 | * Default value. All download operations will execute in queue style (first-in-first-out).
62 | */
63 | SDWebImageDownloaderFIFOExecutionOrder,
64 |
65 | /**
66 | * All download operations will execute in stack style (last-in-first-out).
67 | */
68 | SDWebImageDownloaderLIFOExecutionOrder
69 | };
70 |
71 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
72 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
73 |
74 | typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL);
75 |
76 | typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished);
77 |
78 | typedef NSDictionary SDHTTPHeadersDictionary;
79 | typedef NSMutableDictionary SDHTTPHeadersMutableDictionary;
80 |
81 | typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
82 |
83 | /**
84 | * A token associated with each download. Can be used to cancel a download
85 | */
86 | @interface SDWebImageDownloadToken : NSObject
87 |
88 | @property (nonatomic, strong, nullable) NSURL *url;
89 | @property (nonatomic, strong, nullable) id downloadOperationCancelToken;
90 |
91 | @end
92 |
93 |
94 | /**
95 | * Asynchronous downloader dedicated and optimized for image loading.
96 | */
97 | @interface SDWebImageDownloader : NSObject
98 |
99 | /**
100 | * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
101 | * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
102 | */
103 | @property (assign, nonatomic) BOOL shouldDecompressImages;
104 |
105 | /**
106 | * The maximum number of concurrent downloads
107 | */
108 | @property (assign, nonatomic) NSInteger maxConcurrentDownloads;
109 |
110 | /**
111 | * Shows the current amount of downloads that still need to be downloaded
112 | */
113 | @property (readonly, nonatomic) NSUInteger currentDownloadCount;
114 |
115 |
116 | /**
117 | * The timeout value (in seconds) for the download operation. Default: 15.0.
118 | */
119 | @property (assign, nonatomic) NSTimeInterval downloadTimeout;
120 |
121 |
122 | /**
123 | * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`.
124 | */
125 | @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
126 |
127 | /**
128 | * Singleton method, returns the shared instance
129 | *
130 | * @return global shared instance of downloader class
131 | */
132 | + (nonnull instancetype)sharedDownloader;
133 |
134 | /**
135 | * Set the default URL credential to be set for request operations.
136 | */
137 | @property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
138 |
139 | /**
140 | * Set username
141 | */
142 | @property (strong, nonatomic, nullable) NSString *username;
143 |
144 | /**
145 | * Set password
146 | */
147 | @property (strong, nonatomic, nullable) NSString *password;
148 |
149 | /**
150 | * Set filter to pick headers for downloading image HTTP request.
151 | *
152 | * This block will be invoked for each downloading image request, returned
153 | * NSDictionary will be used as headers in corresponding HTTP request.
154 | */
155 | @property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
156 |
157 | /**
158 | * Creates an instance of a downloader with specified session configuration.
159 | * *Note*: `timeoutIntervalForRequest` is going to be overwritten.
160 | * @return new instance of downloader class
161 | */
162 | - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
163 |
164 | /**
165 | * Set a value for a HTTP header to be appended to each download HTTP request.
166 | *
167 | * @param value The value for the header field. Use `nil` value to remove the header.
168 | * @param field The name of the header field to set.
169 | */
170 | - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;
171 |
172 | /**
173 | * Returns the value of the specified HTTP header field.
174 | *
175 | * @return The value associated with the header field field, or `nil` if there is no corresponding header field.
176 | */
177 | - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field;
178 |
179 | /**
180 | * Sets a subclass of `SDWebImageDownloaderOperation` as the default
181 | * `NSOperation` to be used each time SDWebImage constructs a request
182 | * operation to download an image.
183 | *
184 | * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set
185 | * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`.
186 | */
187 | - (void)setOperationClass:(nullable Class)operationClass;
188 |
189 | /**
190 | * Creates a SDWebImageDownloader async downloader instance with a given URL
191 | *
192 | * The delegate will be informed when the image is finish downloaded or an error has happen.
193 | *
194 | * @see SDWebImageDownloaderDelegate
195 | *
196 | * @param url The URL to the image to download
197 | * @param options The options to be used for this download
198 | * @param progressBlock A block called repeatedly while the image is downloading
199 | * @note the progress block is executed on a background queue
200 | * @param completedBlock A block called once the download is completed.
201 | * If the download succeeded, the image parameter is set, in case of error,
202 | * error parameter is set with the error. The last parameter is always YES
203 | * if SDWebImageDownloaderProgressiveDownload isn't use. With the
204 | * SDWebImageDownloaderProgressiveDownload option, this block is called
205 | * repeatedly with the partial image object and the finished argument set to NO
206 | * before to be called a last time with the full image and finished argument
207 | * set to YES. In case of error, the finished argument is always YES.
208 | *
209 | * @return A token (SDWebImageDownloadToken) that can be passed to -cancel: to cancel this operation
210 | */
211 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
212 | options:(SDWebImageDownloaderOptions)options
213 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
214 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
215 |
216 | /**
217 | * Cancels a download that was previously queued using -downloadImageWithURL:options:progress:completed:
218 | *
219 | * @param token The token received from -downloadImageWithURL:options:progress:completed: that should be canceled.
220 | */
221 | - (void)cancel:(nullable SDWebImageDownloadToken *)token;
222 |
223 | /**
224 | * Sets the download queue suspension state
225 | */
226 | - (void)setSuspended:(BOOL)suspended;
227 |
228 | /**
229 | * Cancels all download operations in the queue
230 | */
231 | - (void)cancelAllDownloads;
232 |
233 | @end
234 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageDownloader.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageDownloader.h"
10 | #import "SDWebImageDownloaderOperation.h"
11 | #import
12 |
13 | @implementation SDWebImageDownloadToken
14 | @end
15 |
16 |
17 | @interface SDWebImageDownloader ()
18 |
19 | @property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
20 | @property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;
21 | @property (assign, nonatomic, nullable) Class operationClass;
22 | @property (strong, nonatomic, nonnull) NSMutableDictionary *URLOperations;
23 | @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;
24 | // This queue is used to serialize the handling of the network responses of all the download operation in a single queue
25 | @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
26 |
27 | // The session in which data tasks will run
28 | @property (strong, nonatomic) NSURLSession *session;
29 |
30 | @end
31 |
32 | @implementation SDWebImageDownloader
33 |
34 | + (void)initialize {
35 | // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
36 | // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
37 | if (NSClassFromString(@"SDNetworkActivityIndicator")) {
38 |
39 | #pragma clang diagnostic push
40 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
41 | id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
42 | #pragma clang diagnostic pop
43 |
44 | // Remove observer in case it was previously added.
45 | [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil];
46 | [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil];
47 |
48 | [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
49 | selector:NSSelectorFromString(@"startActivity")
50 | name:SDWebImageDownloadStartNotification object:nil];
51 | [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
52 | selector:NSSelectorFromString(@"stopActivity")
53 | name:SDWebImageDownloadStopNotification object:nil];
54 | }
55 | }
56 |
57 | + (nonnull instancetype)sharedDownloader {
58 | static dispatch_once_t once;
59 | static id instance;
60 | dispatch_once(&once, ^{
61 | instance = [self new];
62 | });
63 | return instance;
64 | }
65 |
66 | - (nonnull instancetype)init {
67 | return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
68 | }
69 |
70 | - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
71 | if ((self = [super init])) {
72 | _operationClass = [SDWebImageDownloaderOperation class];
73 | _shouldDecompressImages = YES;
74 | _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
75 | _downloadQueue = [NSOperationQueue new];
76 | _downloadQueue.maxConcurrentOperationCount = 6;
77 | _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
78 | _URLOperations = [NSMutableDictionary new];
79 | #ifdef SD_WEBP
80 | _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy];
81 | #else
82 | _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
83 | #endif
84 | _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
85 | _downloadTimeout = 15.0;
86 |
87 | sessionConfiguration.timeoutIntervalForRequest = _downloadTimeout;
88 |
89 | /**
90 | * Create the session for this task
91 | * We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
92 | * method calls and completion handler calls.
93 | */
94 | self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
95 | delegate:self
96 | delegateQueue:nil];
97 | }
98 | return self;
99 | }
100 |
101 | - (void)dealloc {
102 | [self.session invalidateAndCancel];
103 | self.session = nil;
104 |
105 | [self.downloadQueue cancelAllOperations];
106 | SDDispatchQueueRelease(_barrierQueue);
107 | }
108 |
109 | - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
110 | if (value) {
111 | self.HTTPHeaders[field] = value;
112 | }
113 | else {
114 | [self.HTTPHeaders removeObjectForKey:field];
115 | }
116 | }
117 |
118 | - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
119 | return self.HTTPHeaders[field];
120 | }
121 |
122 | - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
123 | _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
124 | }
125 |
126 | - (NSUInteger)currentDownloadCount {
127 | return _downloadQueue.operationCount;
128 | }
129 |
130 | - (NSInteger)maxConcurrentDownloads {
131 | return _downloadQueue.maxConcurrentOperationCount;
132 | }
133 |
134 | - (void)setOperationClass:(nullable Class)operationClass {
135 | if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
136 | _operationClass = operationClass;
137 | } else {
138 | _operationClass = [SDWebImageDownloaderOperation class];
139 | }
140 | }
141 |
142 | - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
143 | options:(SDWebImageDownloaderOptions)options
144 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
145 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
146 | __weak SDWebImageDownloader *wself = self;
147 |
148 | return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
149 | __strong __typeof (wself) sself = wself;
150 | NSTimeInterval timeoutInterval = sself.downloadTimeout;
151 | if (timeoutInterval == 0.0) {
152 | timeoutInterval = 15.0;
153 | }
154 |
155 | // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
156 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
157 | request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
158 | request.HTTPShouldUsePipelining = YES;
159 | if (sself.headersFilter) {
160 | request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
161 | }
162 | else {
163 | request.allHTTPHeaderFields = sself.HTTPHeaders;
164 | }
165 | SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
166 | operation.shouldDecompressImages = sself.shouldDecompressImages;
167 |
168 | if (sself.urlCredential) {
169 | operation.credential = sself.urlCredential;
170 | } else if (sself.username && sself.password) {
171 | operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
172 | }
173 |
174 | if (options & SDWebImageDownloaderHighPriority) {
175 | operation.queuePriority = NSOperationQueuePriorityHigh;
176 | } else if (options & SDWebImageDownloaderLowPriority) {
177 | operation.queuePriority = NSOperationQueuePriorityLow;
178 | }
179 |
180 | [sself.downloadQueue addOperation:operation];
181 | if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
182 | // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
183 | [sself.lastAddedOperation addDependency:operation];
184 | sself.lastAddedOperation = operation;
185 | }
186 |
187 | return operation;
188 | }];
189 | }
190 |
191 | - (void)cancel:(nullable SDWebImageDownloadToken *)token {
192 | dispatch_barrier_async(self.barrierQueue, ^{
193 | SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];
194 | BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
195 | if (canceled) {
196 | [self.URLOperations removeObjectForKey:token.url];
197 | }
198 | });
199 | }
200 |
201 | - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
202 | completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
203 | forURL:(nullable NSURL *)url
204 | createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
205 | // 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.
206 | if (url == nil) {
207 | if (completedBlock != nil) {
208 | completedBlock(nil, nil, nil, NO);
209 | }
210 | return nil;
211 | }
212 |
213 | __block SDWebImageDownloadToken *token = nil;
214 |
215 | dispatch_barrier_sync(self.barrierQueue, ^{
216 | SDWebImageDownloaderOperation *operation = self.URLOperations[url];
217 | if (!operation) {
218 | operation = createCallback();
219 | self.URLOperations[url] = operation;
220 |
221 | __weak SDWebImageDownloaderOperation *woperation = operation;
222 | operation.completionBlock = ^{
223 | SDWebImageDownloaderOperation *soperation = woperation;
224 | if (!soperation) return;
225 | if (self.URLOperations[url] == soperation) {
226 | [self.URLOperations removeObjectForKey:url];
227 | };
228 | };
229 | }
230 | id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
231 |
232 | token = [SDWebImageDownloadToken new];
233 | token.url = url;
234 | token.downloadOperationCancelToken = downloadOperationCancelToken;
235 | });
236 |
237 | return token;
238 | }
239 |
240 | - (void)setSuspended:(BOOL)suspended {
241 | (self.downloadQueue).suspended = suspended;
242 | }
243 |
244 | - (void)cancelAllDownloads {
245 | [self.downloadQueue cancelAllOperations];
246 | }
247 |
248 | #pragma mark Helper methods
249 |
250 | - (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
251 | SDWebImageDownloaderOperation *returnOperation = nil;
252 | for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {
253 | if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
254 | returnOperation = operation;
255 | break;
256 | }
257 | }
258 | return returnOperation;
259 | }
260 |
261 | #pragma mark NSURLSessionDataDelegate
262 |
263 | - (void)URLSession:(NSURLSession *)session
264 | dataTask:(NSURLSessionDataTask *)dataTask
265 | didReceiveResponse:(NSURLResponse *)response
266 | completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
267 |
268 | // Identify the operation that runs this task and pass it the delegate method
269 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
270 |
271 | [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
272 | }
273 |
274 | - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
275 |
276 | // Identify the operation that runs this task and pass it the delegate method
277 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
278 |
279 | [dataOperation URLSession:session dataTask:dataTask didReceiveData:data];
280 | }
281 |
282 | - (void)URLSession:(NSURLSession *)session
283 | dataTask:(NSURLSessionDataTask *)dataTask
284 | willCacheResponse:(NSCachedURLResponse *)proposedResponse
285 | completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
286 |
287 | // Identify the operation that runs this task and pass it the delegate method
288 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
289 |
290 | [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
291 | }
292 |
293 | #pragma mark NSURLSessionTaskDelegate
294 |
295 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
296 | // Identify the operation that runs this task and pass it the delegate method
297 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
298 |
299 | [dataOperation URLSession:session task:task didCompleteWithError:error];
300 | }
301 |
302 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
303 |
304 | completionHandler(request);
305 | }
306 |
307 | - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
308 |
309 | // Identify the operation that runs this task and pass it the delegate method
310 | SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
311 |
312 | [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
313 | }
314 |
315 | @end
316 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageDownloaderOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageDownloader.h"
11 | #import "SDWebImageOperation.h"
12 |
13 | extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
14 | extern NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
15 | extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
16 | extern NSString * _Nonnull const SDWebImageDownloadFinishNotification;
17 |
18 |
19 |
20 | /**
21 | Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol
22 | */
23 | @protocol SDWebImageDownloaderOperationInterface
24 |
25 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
26 | inSession:(nullable NSURLSession *)session
27 | options:(SDWebImageDownloaderOptions)options;
28 |
29 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
30 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
31 |
32 | - (BOOL)shouldDecompressImages;
33 | - (void)setShouldDecompressImages:(BOOL)value;
34 |
35 | - (nullable NSURLCredential *)credential;
36 | - (void)setCredential:(nullable NSURLCredential *)value;
37 |
38 | @end
39 |
40 |
41 | @interface SDWebImageDownloaderOperation : NSOperation
42 |
43 | /**
44 | * The request used by the operation's task.
45 | */
46 | @property (strong, nonatomic, readonly, nullable) NSURLRequest *request;
47 |
48 | /**
49 | * The operation's task
50 | */
51 | @property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
52 |
53 |
54 | @property (assign, nonatomic) BOOL shouldDecompressImages;
55 |
56 | /**
57 | * Was used to determine whether the URL connection should consult the credential storage for authenticating the connection.
58 | * @deprecated Not used for a couple of versions
59 | */
60 | @property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility");
61 |
62 | /**
63 | * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
64 | *
65 | * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
66 | */
67 | @property (nonatomic, strong, nullable) NSURLCredential *credential;
68 |
69 | /**
70 | * The SDWebImageDownloaderOptions for the receiver.
71 | */
72 | @property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
73 |
74 | /**
75 | * The expected size of data.
76 | */
77 | @property (assign, nonatomic) NSInteger expectedSize;
78 |
79 | /**
80 | * The response returned by the operation's connection.
81 | */
82 | @property (strong, nonatomic, nullable) NSURLResponse *response;
83 |
84 | /**
85 | * Initializes a `SDWebImageDownloaderOperation` object
86 | *
87 | * @see SDWebImageDownloaderOperation
88 | *
89 | * @param request the URL request
90 | * @param session the URL session in which this operation will run
91 | * @param options downloader options
92 | *
93 | * @return the initialized instance
94 | */
95 | - (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
96 | inSession:(nullable NSURLSession *)session
97 | options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;
98 |
99 | /**
100 | * Adds handlers for progress and completion. Returns a tokent that can be passed to -cancel: to cancel this set of
101 | * callbacks.
102 | *
103 | * @param progressBlock the block executed when a new chunk of data arrives.
104 | * @note the progress block is executed on a background queue
105 | * @param completedBlock the block executed when the download is done.
106 | * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
107 | *
108 | * @return the token to use to cancel this set of handlers
109 | */
110 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
111 | completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
112 |
113 | /**
114 | * Cancels a set of callbacks. Once all callbacks are canceled, the operation is cancelled.
115 | *
116 | * @param token the token representing a set of callbacks to cancel
117 | *
118 | * @return YES if the operation was stopped because this was the last token to be canceled. NO otherwise.
119 | */
120 | - (BOOL)cancel:(nullable id)token;
121 |
122 | @end
123 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageManager.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 | #import "SDWebImageOperation.h"
11 | #import "SDWebImageDownloader.h"
12 | #import "SDImageCache.h"
13 |
14 | typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
15 | /**
16 | * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
17 | * This flag disable this blacklisting.
18 | */
19 | SDWebImageRetryFailed = 1 << 0,
20 |
21 | /**
22 | * By default, image downloads are started during UI interactions, this flags disable this feature,
23 | * leading to delayed download on UIScrollView deceleration for instance.
24 | */
25 | SDWebImageLowPriority = 1 << 1,
26 |
27 | /**
28 | * This flag disables on-disk caching
29 | */
30 | SDWebImageCacheMemoryOnly = 1 << 2,
31 |
32 | /**
33 | * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
34 | * By default, the image is only displayed once completely downloaded.
35 | */
36 | SDWebImageProgressiveDownload = 1 << 3,
37 |
38 | /**
39 | * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
40 | * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
41 | * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
42 | * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
43 | *
44 | * Use this flag only if you can't make your URLs static with embedded cache busting parameter.
45 | */
46 | SDWebImageRefreshCached = 1 << 4,
47 |
48 | /**
49 | * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
50 | * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
51 | */
52 | SDWebImageContinueInBackground = 1 << 5,
53 |
54 | /**
55 | * Handles cookies stored in NSHTTPCookieStore by setting
56 | * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
57 | */
58 | SDWebImageHandleCookies = 1 << 6,
59 |
60 | /**
61 | * Enable to allow untrusted SSL certificates.
62 | * Useful for testing purposes. Use with caution in production.
63 | */
64 | SDWebImageAllowInvalidSSLCertificates = 1 << 7,
65 |
66 | /**
67 | * By default, images are loaded in the order in which they were queued. This flag moves them to
68 | * the front of the queue.
69 | */
70 | SDWebImageHighPriority = 1 << 8,
71 |
72 | /**
73 | * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
74 | * of the placeholder image until after the image has finished loading.
75 | */
76 | SDWebImageDelayPlaceholder = 1 << 9,
77 |
78 | /**
79 | * We usually don't call transformDownloadedImage delegate method on animated images,
80 | * as most transformation code would mangle it.
81 | * Use this flag to transform them anyway.
82 | */
83 | SDWebImageTransformAnimatedImage = 1 << 10,
84 |
85 | /**
86 | * By default, image is added to the imageView after download. But in some cases, we want to
87 | * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
88 | * Use this flag if you want to manually set the image in the completion when success
89 | */
90 | SDWebImageAvoidAutoSetImage = 1 << 11,
91 |
92 | /**
93 | * By default, images are decoded respecting their original size. On iOS, this flag will scale down the
94 | * images to a size compatible with the constrained memory of devices.
95 | * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated.
96 | */
97 | SDWebImageScaleDownLargeImages = 1 << 12
98 | };
99 |
100 | typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
101 |
102 | typedef void(^SDInternalCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL);
103 |
104 | typedef NSString * _Nullable (^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url);
105 |
106 |
107 | @class SDWebImageManager;
108 |
109 | @protocol SDWebImageManagerDelegate
110 |
111 | @optional
112 |
113 | /**
114 | * Controls which image should be downloaded when the image is not found in the cache.
115 | *
116 | * @param imageManager The current `SDWebImageManager`
117 | * @param imageURL The url of the image to be downloaded
118 | *
119 | * @return Return NO to prevent the downloading of the image on cache misses. If not implemented, YES is implied.
120 | */
121 | - (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;
122 |
123 | /**
124 | * Allows to transform the image immediately after it has been downloaded and just before to cache it on disk and memory.
125 | * NOTE: This method is called from a global queue in order to not to block the main thread.
126 | *
127 | * @param imageManager The current `SDWebImageManager`
128 | * @param image The image to transform
129 | * @param imageURL The url of the image to transform
130 | *
131 | * @return The transformed image object.
132 | */
133 | - (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;
134 |
135 | @end
136 |
137 | /**
138 | * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.
139 | * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).
140 | * You can use this class directly to benefit from web image downloading with caching in another context than
141 | * a UIView.
142 | *
143 | * Here is a simple example of how to use SDWebImageManager:
144 | *
145 | * @code
146 |
147 | SDWebImageManager *manager = [SDWebImageManager sharedManager];
148 | [manager loadImageWithURL:imageURL
149 | options:0
150 | progress:nil
151 | completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
152 | if (image) {
153 | // do something with image
154 | }
155 | }];
156 |
157 | * @endcode
158 | */
159 | @interface SDWebImageManager : NSObject
160 |
161 | @property (weak, nonatomic, nullable) id delegate;
162 |
163 | @property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;
164 | @property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;
165 |
166 | /**
167 | * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can
168 | * be used to remove dynamic part of an image URL.
169 | *
170 | * The following example sets a filter in the application delegate that will remove any query-string from the
171 | * URL before to use it as a cache key:
172 | *
173 | * @code
174 |
175 | [[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
176 | url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
177 | return [url absoluteString];
178 | }];
179 |
180 | * @endcode
181 | */
182 | @property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
183 |
184 | /**
185 | * Returns global SDWebImageManager instance.
186 | *
187 | * @return SDWebImageManager shared instance
188 | */
189 | + (nonnull instancetype)sharedManager;
190 |
191 | /**
192 | * Allows to specify instance of cache and image downloader used with image manager.
193 | * @return new instance of `SDWebImageManager` with specified cache and downloader.
194 | */
195 | - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER;
196 |
197 | /**
198 | * Downloads the image at the given URL if not present in cache or return the cached version otherwise.
199 | *
200 | * @param url The URL to the image
201 | * @param options A mask to specify options to use for this request
202 | * @param progressBlock A block called while image is downloading
203 | * @note the progress block is executed on a background queue
204 | * @param completedBlock A block called when operation has been completed.
205 | *
206 | * This parameter is required.
207 | *
208 | * This block has no return value and takes the requested UIImage as first parameter and the NSData representation as second parameter.
209 | * In case of error the image parameter is nil and the third parameter may contain an NSError.
210 | *
211 | * The forth parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache
212 | * or from the memory cache or from the network.
213 | *
214 | * The fith parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is
215 | * downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the
216 | * block is called a last time with the full image and the last parameter set to YES.
217 | *
218 | * The last parameter is the original image URL
219 | *
220 | * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation
221 | */
222 | - (nullable id )loadImageWithURL:(nullable NSURL *)url
223 | options:(SDWebImageOptions)options
224 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
225 | completed:(nullable SDInternalCompletionBlock)completedBlock;
226 |
227 | /**
228 | * Saves image to cache for given URL
229 | *
230 | * @param image The image to cache
231 | * @param url The URL to the image
232 | *
233 | */
234 |
235 | - (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;
236 |
237 | /**
238 | * Cancel all current operations
239 | */
240 | - (void)cancelAll;
241 |
242 | /**
243 | * Check one or more operations running
244 | */
245 | - (BOOL)isRunning;
246 |
247 | /**
248 | * Async check if image has already been cached
249 | *
250 | * @param url image url
251 | * @param completionBlock the block to be executed when the check is finished
252 | *
253 | * @note the completion block is always executed on the main queue
254 | */
255 | - (void)cachedImageExistsForURL:(nullable NSURL *)url
256 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
257 |
258 | /**
259 | * Async check if image has already been cached on disk only
260 | *
261 | * @param url image url
262 | * @param completionBlock the block to be executed when the check is finished
263 | *
264 | * @note the completion block is always executed on the main queue
265 | */
266 | - (void)diskImageExistsForURL:(nullable NSURL *)url
267 | completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
268 |
269 |
270 | /**
271 | *Return the cache key for a given URL
272 | */
273 | - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;
274 |
275 | @end
276 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImageOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 |
11 | @protocol SDWebImageOperation
12 |
13 | - (void)cancel;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImagePrefetcher.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import
10 | #import "SDWebImageManager.h"
11 |
12 | @class SDWebImagePrefetcher;
13 |
14 | @protocol SDWebImagePrefetcherDelegate
15 |
16 | @optional
17 |
18 | /**
19 | * Called when an image was prefetched.
20 | *
21 | * @param imagePrefetcher The current image prefetcher
22 | * @param imageURL The image url that was prefetched
23 | * @param finishedCount The total number of images that were prefetched (successful or not)
24 | * @param totalCount The total number of images that were to be prefetched
25 | */
26 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
27 |
28 | /**
29 | * Called when all images are prefetched.
30 | * @param imagePrefetcher The current image prefetcher
31 | * @param totalCount The total number of images that were prefetched (whether successful or not)
32 | * @param skippedCount The total number of images that were skipped
33 | */
34 | - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
35 |
36 | @end
37 |
38 | typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
39 | typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
40 |
41 | /**
42 | * Prefetch some URLs in the cache for future use. Images are downloaded in low priority.
43 | */
44 | @interface SDWebImagePrefetcher : NSObject
45 |
46 | /**
47 | * The web image manager
48 | */
49 | @property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;
50 |
51 | /**
52 | * Maximum number of URLs to prefetch at the same time. Defaults to 3.
53 | */
54 | @property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
55 |
56 | /**
57 | * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority.
58 | */
59 | @property (nonatomic, assign) SDWebImageOptions options;
60 |
61 | /**
62 | * Queue options for Prefetcher. Defaults to Main Queue.
63 | */
64 | @property (nonatomic, assign, nonnull) dispatch_queue_t prefetcherQueue;
65 |
66 | @property (weak, nonatomic, nullable) id delegate;
67 |
68 | /**
69 | * Return the global image prefetcher instance.
70 | */
71 | + (nonnull instancetype)sharedImagePrefetcher;
72 |
73 | /**
74 | * Allows you to instantiate a prefetcher with any arbitrary image manager.
75 | */
76 | - (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
77 |
78 | /**
79 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
80 | * currently one image is downloaded at a time,
81 | * and skips images for failed downloads and proceed to the next image in the list
82 | *
83 | * @param urls list of URLs to prefetch
84 | */
85 | - (void)prefetchURLs:(nullable NSArray *)urls;
86 |
87 | /**
88 | * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
89 | * currently one image is downloaded at a time,
90 | * and skips images for failed downloads and proceed to the next image in the list
91 | *
92 | * @param urls list of URLs to prefetch
93 | * @param progressBlock block to be called when progress updates;
94 | * first parameter is the number of completed (successful or not) requests,
95 | * second parameter is the total number of images originally requested to be prefetched
96 | * @param completionBlock block to be called when prefetching is completed
97 | * first param is the number of completed (successful or not) requests,
98 | * second parameter is the number of skipped requests
99 | */
100 | - (void)prefetchURLs:(nullable NSArray *)urls
101 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
102 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
103 |
104 | /**
105 | * Remove and cancel queued list
106 | */
107 | - (void)cancelPrefetching;
108 |
109 |
110 | @end
111 |
--------------------------------------------------------------------------------
/SDWebImage/SDWebImagePrefetcher.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImagePrefetcher.h"
10 |
11 | @interface SDWebImagePrefetcher ()
12 |
13 | @property (strong, nonatomic, nonnull) SDWebImageManager *manager;
14 | @property (strong, nonatomic, nullable) NSArray *prefetchURLs;
15 | @property (assign, nonatomic) NSUInteger requestedCount;
16 | @property (assign, nonatomic) NSUInteger skippedCount;
17 | @property (assign, nonatomic) NSUInteger finishedCount;
18 | @property (assign, nonatomic) NSTimeInterval startedTime;
19 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
20 | @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
21 |
22 | @end
23 |
24 | @implementation SDWebImagePrefetcher
25 |
26 | + (nonnull instancetype)sharedImagePrefetcher {
27 | static dispatch_once_t once;
28 | static id instance;
29 | dispatch_once(&once, ^{
30 | instance = [self new];
31 | });
32 | return instance;
33 | }
34 |
35 | - (nonnull instancetype)init {
36 | return [self initWithImageManager:[SDWebImageManager new]];
37 | }
38 |
39 | - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
40 | if ((self = [super init])) {
41 | _manager = manager;
42 | _options = SDWebImageLowPriority;
43 | _prefetcherQueue = dispatch_get_main_queue();
44 | self.maxConcurrentDownloads = 3;
45 | }
46 | return self;
47 | }
48 |
49 | - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
50 | self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
51 | }
52 |
53 | - (NSUInteger)maxConcurrentDownloads {
54 | return self.manager.imageDownloader.maxConcurrentDownloads;
55 | }
56 |
57 | - (void)startPrefetchingAtIndex:(NSUInteger)index {
58 | if (index >= self.prefetchURLs.count) return;
59 | self.requestedCount++;
60 | [self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
61 | if (!finished) return;
62 | self.finishedCount++;
63 |
64 | if (image) {
65 | if (self.progressBlock) {
66 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
67 | }
68 | }
69 | else {
70 | if (self.progressBlock) {
71 | self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
72 | }
73 | // Add last failed
74 | self.skippedCount++;
75 | }
76 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
77 | [self.delegate imagePrefetcher:self
78 | didPrefetchURL:self.prefetchURLs[index]
79 | finishedCount:self.finishedCount
80 | totalCount:self.prefetchURLs.count
81 | ];
82 | }
83 | if (self.prefetchURLs.count > self.requestedCount) {
84 | dispatch_async(self.prefetcherQueue, ^{
85 | [self startPrefetchingAtIndex:self.requestedCount];
86 | });
87 | } else if (self.finishedCount == self.requestedCount) {
88 | [self reportStatus];
89 | if (self.completionBlock) {
90 | self.completionBlock(self.finishedCount, self.skippedCount);
91 | self.completionBlock = nil;
92 | }
93 | self.progressBlock = nil;
94 | }
95 | }];
96 | }
97 |
98 | - (void)reportStatus {
99 | NSUInteger total = (self.prefetchURLs).count;
100 | if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
101 | [self.delegate imagePrefetcher:self
102 | didFinishWithTotalCount:(total - self.skippedCount)
103 | skippedCount:self.skippedCount
104 | ];
105 | }
106 | }
107 |
108 | - (void)prefetchURLs:(nullable NSArray *)urls {
109 | [self prefetchURLs:urls progress:nil completed:nil];
110 | }
111 |
112 | - (void)prefetchURLs:(nullable NSArray *)urls
113 | progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
114 | completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
115 | [self cancelPrefetching]; // Prevent duplicate prefetch request
116 | self.startedTime = CFAbsoluteTimeGetCurrent();
117 | self.prefetchURLs = urls;
118 | self.completionBlock = completionBlock;
119 | self.progressBlock = progressBlock;
120 |
121 | if (urls.count == 0) {
122 | if (completionBlock) {
123 | completionBlock(0,0);
124 | }
125 | } else {
126 | // Starts prefetching from the very first image on the list with the max allowed concurrency
127 | NSUInteger listCount = self.prefetchURLs.count;
128 | for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
129 | [self startPrefetchingAtIndex:i];
130 | }
131 | }
132 | }
133 |
134 | - (void)cancelPrefetching {
135 | self.prefetchURLs = nil;
136 | self.skippedCount = 0;
137 | self.requestedCount = 0;
138 | self.finishedCount = 0;
139 | [self.manager cancelAll];
140 | }
141 |
142 | @end
143 |
--------------------------------------------------------------------------------
/SDWebImage/UIButton+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | /**
16 | * Integrates SDWebImage async downloading and caching of remote images with UIButtonView.
17 | */
18 | @interface UIButton (WebCache)
19 |
20 | /**
21 | * Get the current image URL.
22 | */
23 | - (nullable NSURL *)sd_currentImageURL;
24 |
25 | #pragma mark - Image
26 |
27 | /**
28 | * Get the image URL for a control state.
29 | *
30 | * @param state Which state you want to know the URL for. The values are described in UIControlState.
31 | */
32 | - (nullable NSURL *)sd_imageURLForState:(UIControlState)state;
33 |
34 | /**
35 | * Set the imageView `image` with an `url`.
36 | *
37 | * The download is asynchronous and cached.
38 | *
39 | * @param url The url for the image.
40 | * @param state The state that uses the specified title. The values are described in UIControlState.
41 | */
42 | - (void)sd_setImageWithURL:(nullable NSURL *)url
43 | forState:(UIControlState)state;
44 |
45 | /**
46 | * Set the imageView `image` with an `url` and a placeholder.
47 | *
48 | * The download is asynchronous and cached.
49 | *
50 | * @param url The url for the image.
51 | * @param state The state that uses the specified title. The values are described in UIControlState.
52 | * @param placeholder The image to be set initially, until the image request finishes.
53 | * @see sd_setImageWithURL:placeholderImage:options:
54 | */
55 | - (void)sd_setImageWithURL:(nullable NSURL *)url
56 | forState:(UIControlState)state
57 | placeholderImage:(nullable UIImage *)placeholder;
58 |
59 | /**
60 | * Set the imageView `image` with an `url`, placeholder and custom options.
61 | *
62 | * The download is asynchronous and cached.
63 | *
64 | * @param url The url for the image.
65 | * @param state The state that uses the specified title. The values are described in UIControlState.
66 | * @param placeholder The image to be set initially, until the image request finishes.
67 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
68 | */
69 | - (void)sd_setImageWithURL:(nullable NSURL *)url
70 | forState:(UIControlState)state
71 | placeholderImage:(nullable UIImage *)placeholder
72 | options:(SDWebImageOptions)options;
73 |
74 | /**
75 | * Set the imageView `image` with an `url`.
76 | *
77 | * The download is asynchronous and cached.
78 | *
79 | * @param url The url for the image.
80 | * @param state The state that uses the specified title. The values are described in UIControlState.
81 | * @param completedBlock A block called when operation has been completed. This block has no return value
82 | * and takes the requested UIImage as first parameter. In case of error the image parameter
83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
84 | * indicating if the image was retrieved from the local cache or from the network.
85 | * The fourth parameter is the original image url.
86 | */
87 | - (void)sd_setImageWithURL:(nullable NSURL *)url
88 | forState:(UIControlState)state
89 | completed:(nullable SDExternalCompletionBlock)completedBlock;
90 |
91 | /**
92 | * Set the imageView `image` with an `url`, placeholder.
93 | *
94 | * The download is asynchronous and cached.
95 | *
96 | * @param url The url for the image.
97 | * @param state The state that uses the specified title. The values are described in UIControlState.
98 | * @param placeholder The image to be set initially, until the image request finishes.
99 | * @param completedBlock A block called when operation has been completed. This block has no return value
100 | * and takes the requested UIImage as first parameter. In case of error the image parameter
101 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
102 | * indicating if the image was retrieved from the local cache or from the network.
103 | * The fourth parameter is the original image url.
104 | */
105 | - (void)sd_setImageWithURL:(nullable NSURL *)url
106 | forState:(UIControlState)state
107 | placeholderImage:(nullable UIImage *)placeholder
108 | completed:(nullable SDExternalCompletionBlock)completedBlock;
109 |
110 | /**
111 | * Set the imageView `image` with an `url`, placeholder and custom options.
112 | *
113 | * The download is asynchronous and cached.
114 | *
115 | * @param url The url for the image.
116 | * @param state The state that uses the specified title. The values are described in UIControlState.
117 | * @param placeholder The image to be set initially, until the image request finishes.
118 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
119 | * @param completedBlock A block called when operation has been completed. This block has no return value
120 | * and takes the requested UIImage as first parameter. In case of error the image parameter
121 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
122 | * indicating if the image was retrieved from the local cache or from the network.
123 | * The fourth parameter is the original image url.
124 | */
125 | - (void)sd_setImageWithURL:(nullable NSURL *)url
126 | forState:(UIControlState)state
127 | placeholderImage:(nullable UIImage *)placeholder
128 | options:(SDWebImageOptions)options
129 | completed:(nullable SDExternalCompletionBlock)completedBlock;
130 |
131 | #pragma mark - Background image
132 |
133 | /**
134 | * Set the backgroundImageView `image` with an `url`.
135 | *
136 | * The download is asynchronous and cached.
137 | *
138 | * @param url The url for the image.
139 | * @param state The state that uses the specified title. The values are described in UIControlState.
140 | */
141 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
142 | forState:(UIControlState)state;
143 |
144 | /**
145 | * Set the backgroundImageView `image` with an `url` and a placeholder.
146 | *
147 | * The download is asynchronous and cached.
148 | *
149 | * @param url The url for the image.
150 | * @param state The state that uses the specified title. The values are described in UIControlState.
151 | * @param placeholder The image to be set initially, until the image request finishes.
152 | * @see sd_setImageWithURL:placeholderImage:options:
153 | */
154 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
155 | forState:(UIControlState)state
156 | placeholderImage:(nullable UIImage *)placeholder;
157 |
158 | /**
159 | * Set the backgroundImageView `image` with an `url`, placeholder and custom options.
160 | *
161 | * The download is asynchronous and cached.
162 | *
163 | * @param url The url for the image.
164 | * @param state The state that uses the specified title. The values are described in UIControlState.
165 | * @param placeholder The image to be set initially, until the image request finishes.
166 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
167 | */
168 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
169 | forState:(UIControlState)state
170 | placeholderImage:(nullable UIImage *)placeholder
171 | options:(SDWebImageOptions)options;
172 |
173 | /**
174 | * Set the backgroundImageView `image` with an `url`.
175 | *
176 | * The download is asynchronous and cached.
177 | *
178 | * @param url The url for the image.
179 | * @param state The state that uses the specified title. The values are described in UIControlState.
180 | * @param completedBlock A block called when operation has been completed. This block has no return value
181 | * and takes the requested UIImage as first parameter. In case of error the image parameter
182 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
183 | * indicating if the image was retrieved from the local cache or from the network.
184 | * The fourth parameter is the original image url.
185 | */
186 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
187 | forState:(UIControlState)state
188 | completed:(nullable SDExternalCompletionBlock)completedBlock;
189 |
190 | /**
191 | * Set the backgroundImageView `image` with an `url`, placeholder.
192 | *
193 | * The download is asynchronous and cached.
194 | *
195 | * @param url The url for the image.
196 | * @param state The state that uses the specified title. The values are described in UIControlState.
197 | * @param placeholder The image to be set initially, until the image request finishes.
198 | * @param completedBlock A block called when operation has been completed. This block has no return value
199 | * and takes the requested UIImage as first parameter. In case of error the image parameter
200 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
201 | * indicating if the image was retrieved from the local cache or from the network.
202 | * The fourth parameter is the original image url.
203 | */
204 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
205 | forState:(UIControlState)state
206 | placeholderImage:(nullable UIImage *)placeholder
207 | completed:(nullable SDExternalCompletionBlock)completedBlock;
208 |
209 | /**
210 | * Set the backgroundImageView `image` with an `url`, placeholder and custom options.
211 | *
212 | * The download is asynchronous and cached.
213 | *
214 | * @param url The url for the image.
215 | * @param placeholder The image to be set initially, until the image request finishes.
216 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
217 | * @param completedBlock A block called when operation has been completed. This block has no return value
218 | * and takes the requested UIImage as first parameter. In case of error the image parameter
219 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
220 | * indicating if the image was retrieved from the local cache or from the network.
221 | * The fourth parameter is the original image url.
222 | */
223 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
224 | forState:(UIControlState)state
225 | placeholderImage:(nullable UIImage *)placeholder
226 | options:(SDWebImageOptions)options
227 | completed:(nullable SDExternalCompletionBlock)completedBlock;
228 |
229 | #pragma mark - Cancel
230 |
231 | /**
232 | * Cancel the current image download
233 | */
234 | - (void)sd_cancelImageLoadForState:(UIControlState)state;
235 |
236 | /**
237 | * Cancel the current backgroundImage download
238 | */
239 | - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state;
240 |
241 | @end
242 |
243 | #endif
244 |
--------------------------------------------------------------------------------
/SDWebImage/UIButton+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIButton+WebCache.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 | #import "UIView+WebCache.h"
16 |
17 | static char imageURLStorageKey;
18 |
19 | typedef NSMutableDictionary SDStateImageURLDictionary;
20 |
21 | @implementation UIButton (WebCache)
22 |
23 | - (nullable NSURL *)sd_currentImageURL {
24 | NSURL *url = self.imageURLStorage[@(self.state)];
25 |
26 | if (!url) {
27 | url = self.imageURLStorage[@(UIControlStateNormal)];
28 | }
29 |
30 | return url;
31 | }
32 |
33 | - (nullable NSURL *)sd_imageURLForState:(UIControlState)state {
34 | return self.imageURLStorage[@(state)];
35 | }
36 |
37 | #pragma mark - Image
38 |
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
40 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
41 | }
42 |
43 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
44 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
45 | }
46 |
47 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
48 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
49 | }
50 |
51 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
52 | [self sd_setImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
53 | }
54 |
55 | - (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
56 | [self sd_setImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
57 | }
58 |
59 | - (void)sd_setImageWithURL:(nullable NSURL *)url
60 | forState:(UIControlState)state
61 | placeholderImage:(nullable UIImage *)placeholder
62 | options:(SDWebImageOptions)options
63 | completed:(nullable SDExternalCompletionBlock)completedBlock {
64 | if (!url) {
65 | [self.imageURLStorage removeObjectForKey:@(state)];
66 | return;
67 | }
68 |
69 | self.imageURLStorage[@(state)] = url;
70 |
71 | __weak typeof(self)weakSelf = self;
72 | [self sd_internalSetImageWithURL:url
73 | placeholderImage:placeholder
74 | options:options
75 | operationKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]
76 | setImageBlock:^(UIImage *image, NSData *imageData) {
77 | [weakSelf setImage:image forState:state];
78 | }
79 | progress:nil
80 | completed:completedBlock];
81 | }
82 |
83 | #pragma mark - Background image
84 |
85 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state {
86 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:nil];
87 | }
88 |
89 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder {
90 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:nil];
91 | }
92 |
93 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
94 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:options completed:nil];
95 | }
96 |
97 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock {
98 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:nil options:0 completed:completedBlock];
99 | }
100 |
101 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url forState:(UIControlState)state placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
102 | [self sd_setBackgroundImageWithURL:url forState:state placeholderImage:placeholder options:0 completed:completedBlock];
103 | }
104 |
105 | - (void)sd_setBackgroundImageWithURL:(nullable NSURL *)url
106 | forState:(UIControlState)state
107 | placeholderImage:(nullable UIImage *)placeholder
108 | options:(SDWebImageOptions)options
109 | completed:(nullable SDExternalCompletionBlock)completedBlock {
110 | if (!url) {
111 | [self.imageURLStorage removeObjectForKey:@(state)];
112 | return;
113 | }
114 |
115 | self.imageURLStorage[@(state)] = url;
116 |
117 | __weak typeof(self)weakSelf = self;
118 | [self sd_internalSetImageWithURL:url
119 | placeholderImage:placeholder
120 | options:options
121 | operationKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]
122 | setImageBlock:^(UIImage *image, NSData *imageData) {
123 | [weakSelf setBackgroundImage:image forState:state];
124 | }
125 | progress:nil
126 | completed:completedBlock];
127 | }
128 |
129 | - (void)sd_setImageLoadOperation:(id)operation forState:(UIControlState)state {
130 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
131 | }
132 |
133 | - (void)sd_cancelImageLoadForState:(UIControlState)state {
134 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonImageOperation%@", @(state)]];
135 | }
136 |
137 | - (void)sd_setBackgroundImageLoadOperation:(id)operation forState:(UIControlState)state {
138 | [self sd_setImageLoadOperation:operation forKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
139 | }
140 |
141 | - (void)sd_cancelBackgroundImageLoadForState:(UIControlState)state {
142 | [self sd_cancelImageLoadOperationWithKey:[NSString stringWithFormat:@"UIButtonBackgroundImageOperation%@", @(state)]];
143 | }
144 |
145 | - (SDStateImageURLDictionary *)imageURLStorage {
146 | SDStateImageURLDictionary *storage = objc_getAssociatedObject(self, &imageURLStorageKey);
147 | if (!storage) {
148 | storage = [NSMutableDictionary dictionary];
149 | objc_setAssociatedObject(self, &imageURLStorageKey, storage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
150 | }
151 |
152 | return storage;
153 | }
154 |
155 | @end
156 |
157 | #endif
158 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+GIF.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Laurin Brandner
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "SDWebImageCompat.h"
11 |
12 | @interface UIImage (GIF)
13 |
14 | /**
15 | * Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image
16 | */
17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data;
18 |
19 | /**
20 | * Checks if an UIImage instance is a GIF. Will use the `images` array
21 | */
22 | - (BOOL)isGIF;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+GIF.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | * (c) Laurin Brandner
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | #import "UIImage+GIF.h"
11 | #import
12 | #import "objc/runtime.h"
13 | #import "NSImage+WebCache.h"
14 |
15 | @implementation UIImage (GIF)
16 |
17 | + (UIImage *)sd_animatedGIFWithData:(NSData *)data {
18 | if (!data) {
19 | return nil;
20 | }
21 |
22 | CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
23 |
24 | size_t count = CGImageSourceGetCount(source);
25 |
26 | UIImage *staticImage;
27 |
28 | if (count <= 1) {
29 | staticImage = [[UIImage alloc] initWithData:data];
30 | } else {
31 | // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
32 | // this here is only code to allow drawing animated images as static ones
33 | #if SD_WATCH
34 | CGFloat scale = 1;
35 | scale = [WKInterfaceDevice currentDevice].screenScale;
36 | #elif SD_UIKIT
37 | CGFloat scale = 1;
38 | scale = [UIScreen mainScreen].scale;
39 | #endif
40 |
41 | CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
42 | #if SD_UIKIT || SD_WATCH
43 | UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
44 | staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
45 | #elif SD_MAC
46 | staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];
47 | #endif
48 | CGImageRelease(CGImage);
49 | }
50 |
51 | CFRelease(source);
52 |
53 | return staticImage;
54 | }
55 |
56 | - (BOOL)isGIF {
57 | return (self.images != nil);
58 | }
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+MultiFormat.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 | #import "NSData+ImageContentType.h"
11 |
12 | @interface UIImage (MultiFormat)
13 |
14 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data;
15 | - (nullable NSData *)sd_imageData;
16 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+MultiFormat.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImage+MultiFormat.h"
10 | #import "UIImage+GIF.h"
11 | #import "NSData+ImageContentType.h"
12 | #import
13 |
14 | #ifdef SD_WEBP
15 | #import "UIImage+WebP.h"
16 | #endif
17 |
18 | @implementation UIImage (MultiFormat)
19 |
20 | + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data {
21 | if (!data) {
22 | return nil;
23 | }
24 |
25 | UIImage *image;
26 | SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
27 | if (imageFormat == SDImageFormatGIF) {
28 | image = [UIImage sd_animatedGIFWithData:data];
29 | }
30 | #ifdef SD_WEBP
31 | else if (imageFormat == SDImageFormatWebP)
32 | {
33 | image = [UIImage sd_imageWithWebPData:data];
34 | }
35 | #endif
36 | else {
37 | image = [[UIImage alloc] initWithData:data];
38 | #if SD_UIKIT || SD_WATCH
39 | UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
40 | if (orientation != UIImageOrientationUp) {
41 | image = [UIImage imageWithCGImage:image.CGImage
42 | scale:image.scale
43 | orientation:orientation];
44 | }
45 | #endif
46 | }
47 |
48 |
49 | return image;
50 | }
51 |
52 | #if SD_UIKIT || SD_WATCH
53 | +(UIImageOrientation)sd_imageOrientationFromImageData:(nonnull NSData *)imageData {
54 | UIImageOrientation result = UIImageOrientationUp;
55 | CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL);
56 | if (imageSource) {
57 | CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
58 | if (properties) {
59 | CFTypeRef val;
60 | int exifOrientation;
61 | val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
62 | if (val) {
63 | CFNumberGetValue(val, kCFNumberIntType, &exifOrientation);
64 | result = [self sd_exifOrientationToiOSOrientation:exifOrientation];
65 | } // else - if it's not set it remains at up
66 | CFRelease((CFTypeRef) properties);
67 | } else {
68 | //NSLog(@"NO PROPERTIES, FAIL");
69 | }
70 | CFRelease(imageSource);
71 | }
72 | return result;
73 | }
74 |
75 | #pragma mark EXIF orientation tag converter
76 | // Convert an EXIF image orientation to an iOS one.
77 | // reference see here: http://sylvana.net/jpegcrop/exif_orientation.html
78 | + (UIImageOrientation) sd_exifOrientationToiOSOrientation:(int)exifOrientation {
79 | UIImageOrientation orientation = UIImageOrientationUp;
80 | switch (exifOrientation) {
81 | case 1:
82 | orientation = UIImageOrientationUp;
83 | break;
84 |
85 | case 3:
86 | orientation = UIImageOrientationDown;
87 | break;
88 |
89 | case 8:
90 | orientation = UIImageOrientationLeft;
91 | break;
92 |
93 | case 6:
94 | orientation = UIImageOrientationRight;
95 | break;
96 |
97 | case 2:
98 | orientation = UIImageOrientationUpMirrored;
99 | break;
100 |
101 | case 4:
102 | orientation = UIImageOrientationDownMirrored;
103 | break;
104 |
105 | case 5:
106 | orientation = UIImageOrientationLeftMirrored;
107 | break;
108 |
109 | case 7:
110 | orientation = UIImageOrientationRightMirrored;
111 | break;
112 | default:
113 | break;
114 | }
115 | return orientation;
116 | }
117 | #endif
118 |
119 | - (nullable NSData *)sd_imageData {
120 | return [self sd_imageDataAsFormat:SDImageFormatUndefined];
121 | }
122 |
123 | - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {
124 | NSData *imageData = nil;
125 | if (self) {
126 | #if SD_UIKIT || SD_WATCH
127 | int alphaInfo = CGImageGetAlphaInfo(self.CGImage);
128 | BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
129 | alphaInfo == kCGImageAlphaNoneSkipFirst ||
130 | alphaInfo == kCGImageAlphaNoneSkipLast);
131 |
132 | BOOL usePNG = hasAlpha;
133 |
134 | // the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel
135 | if (imageFormat != SDImageFormatUndefined) {
136 | usePNG = (imageFormat == SDImageFormatPNG);
137 | }
138 |
139 | if (usePNG) {
140 | imageData = UIImagePNGRepresentation(self);
141 | } else {
142 | imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);
143 | }
144 | #else
145 | NSBitmapImageFileType imageFileType = NSJPEGFileType;
146 | if (imageFormat == SDImageFormatGIF) {
147 | imageFileType = NSGIFFileType;
148 | } else if (imageFormat == SDImageFormatPNG) {
149 | imageFileType = NSPNGFileType;
150 | }
151 |
152 | imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations
153 | usingType:imageFileType
154 | properties:@{}];
155 | #endif
156 | }
157 | return imageData;
158 | }
159 |
160 |
161 | @end
162 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+WebP.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #ifdef SD_WEBP
10 |
11 | #import "SDWebImageCompat.h"
12 |
13 | @interface UIImage (WebP)
14 |
15 | + (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data;
16 |
17 | @end
18 |
19 | #endif
20 |
--------------------------------------------------------------------------------
/SDWebImage/UIImage+WebP.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #ifdef SD_WEBP
10 |
11 | #import "UIImage+WebP.h"
12 | #import "webp/decode.h"
13 | #import "webp/mux_types.h"
14 | #import "webp/demux.h"
15 | #import "NSImage+WebCache.h"
16 |
17 | // Callback for CGDataProviderRelease
18 | static void FreeImageData(void *info, const void *data, size_t size) {
19 | free((void *)data);
20 | }
21 |
22 | @implementation UIImage (WebP)
23 |
24 | + (nullable UIImage *)sd_imageWithWebPData:(nullable NSData *)data {
25 | if (!data) {
26 | return nil;
27 | }
28 |
29 | WebPData webpData;
30 | WebPDataInit(&webpData);
31 | webpData.bytes = data.bytes;
32 | webpData.size = data.length;
33 | WebPDemuxer *demuxer = WebPDemux(&webpData);
34 | if (!demuxer) {
35 | return nil;
36 | }
37 |
38 | uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
39 | if (!(flags & ANIMATION_FLAG)) {
40 | // for static single webp image
41 | UIImage *staticImage = [self sd_rawWepImageWithData:webpData];
42 | WebPDemuxDelete(demuxer);
43 | return staticImage;
44 | }
45 |
46 | WebPIterator iter;
47 | if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
48 | WebPDemuxReleaseIterator(&iter);
49 | WebPDemuxDelete(demuxer);
50 | return nil;
51 | }
52 |
53 | NSMutableArray *images = [NSMutableArray array];
54 | NSTimeInterval duration = 0;
55 |
56 | do {
57 | UIImage *image;
58 | if (iter.blend_method == WEBP_MUX_BLEND) {
59 | image = [self sd_blendWebpImageWithOriginImage:[images lastObject] iterator:iter];
60 | } else {
61 | image = [self sd_rawWepImageWithData:iter.fragment];
62 | }
63 |
64 | if (!image) {
65 | continue;
66 | }
67 |
68 | [images addObject:image];
69 | duration += iter.duration / 1000.0f;
70 |
71 | } while (WebPDemuxNextFrame(&iter));
72 |
73 | WebPDemuxReleaseIterator(&iter);
74 | WebPDemuxDelete(demuxer);
75 |
76 | UIImage *finalImage = nil;
77 | #if SD_UIKIT || SD_WATCH
78 | finalImage = [UIImage animatedImageWithImages:images duration:duration];
79 | #elif SD_MAC
80 | if ([images count] > 0) {
81 | finalImage = images[0];
82 | }
83 | #endif
84 | return finalImage;
85 | }
86 |
87 |
88 | + (nullable UIImage *)sd_blendWebpImageWithOriginImage:(nullable UIImage *)originImage iterator:(WebPIterator)iter {
89 | if (!originImage) {
90 | return nil;
91 | }
92 |
93 | CGSize size = originImage.size;
94 | CGFloat tmpX = iter.x_offset;
95 | CGFloat tmpY = size.height - iter.height - iter.y_offset;
96 | CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height);
97 |
98 | UIImage *image = [self sd_rawWepImageWithData:iter.fragment];
99 | if (!image) {
100 | return nil;
101 | }
102 |
103 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
104 | uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0;
105 | CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo);
106 | CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage);
107 | CGContextDrawImage(blendCanvas, imageRect, image.CGImage);
108 | CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas);
109 |
110 | #if SD_UIKIT || SD_WATCH
111 | image = [UIImage imageWithCGImage:newImageRef];
112 | #elif SD_MAC
113 | image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize];
114 | #endif
115 |
116 | CGImageRelease(newImageRef);
117 | CGContextRelease(blendCanvas);
118 | CGColorSpaceRelease(colorSpaceRef);
119 |
120 | return image;
121 | }
122 |
123 | + (nullable UIImage *)sd_rawWepImageWithData:(WebPData)webpData {
124 | WebPDecoderConfig config;
125 | if (!WebPInitDecoderConfig(&config)) {
126 | return nil;
127 | }
128 |
129 | if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) {
130 | return nil;
131 | }
132 |
133 | config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB;
134 | config.options.use_threads = 1;
135 |
136 | // Decode the WebP image data into a RGBA value array.
137 | if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
138 | return nil;
139 | }
140 |
141 | int width = config.input.width;
142 | int height = config.input.height;
143 | if (config.options.use_scaling) {
144 | width = config.options.scaled_width;
145 | height = config.options.scaled_height;
146 | }
147 |
148 | // Construct a UIImage from the decoded RGBA value array.
149 | CGDataProviderRef provider =
150 | CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData);
151 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
152 | CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0;
153 | size_t components = config.input.has_alpha ? 4 : 3;
154 | CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
155 | CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
156 |
157 | CGColorSpaceRelease(colorSpaceRef);
158 | CGDataProviderRelease(provider);
159 |
160 | #if SD_UIKIT || SD_WATCH
161 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
162 | #else
163 | UIImage *image = [[UIImage alloc] initWithCGImage:imageRef size:NSZeroSize];
164 | #endif
165 | CGImageRelease(imageRef);
166 |
167 | return image;
168 | }
169 |
170 | @end
171 |
172 | #endif
173 |
--------------------------------------------------------------------------------
/SDWebImage/UIImageView+HighlightedWebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | /**
16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView for highlighted state.
17 | */
18 | @interface UIImageView (HighlightedWebCache)
19 |
20 | /**
21 | * Set the imageView `highlightedImage` with an `url`.
22 | *
23 | * The download is asynchronous and cached.
24 | *
25 | * @param url The url for the image.
26 | */
27 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url;
28 |
29 | /**
30 | * Set the imageView `highlightedImage` with an `url` and custom options.
31 | *
32 | * The download is asynchronous and cached.
33 | *
34 | * @param url The url for the image.
35 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
36 | */
37 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
38 | options:(SDWebImageOptions)options;
39 |
40 | /**
41 | * Set the imageView `highlightedImage` with an `url`.
42 | *
43 | * The download is asynchronous and cached.
44 | *
45 | * @param url The url for the image.
46 | * @param completedBlock A block called when operation has been completed. This block has no return value
47 | * and takes the requested UIImage as first parameter. In case of error the image parameter
48 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
49 | * indicating if the image was retrieved from the local cache or from the network.
50 | * The fourth parameter is the original image url.
51 | */
52 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
53 | completed:(nullable SDExternalCompletionBlock)completedBlock;
54 |
55 | /**
56 | * Set the imageView `highlightedImage` with an `url` and custom options.
57 | *
58 | * The download is asynchronous and cached.
59 | *
60 | * @param url The url for the image.
61 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
62 | * @param completedBlock A block called when operation has been completed. This block has no return value
63 | * and takes the requested UIImage as first parameter. In case of error the image parameter
64 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
65 | * indicating if the image was retrieved from the local cache or from the network.
66 | * The fourth parameter is the original image url.
67 | */
68 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
69 | options:(SDWebImageOptions)options
70 | completed:(nullable SDExternalCompletionBlock)completedBlock;
71 |
72 | /**
73 | * Set the imageView `highlightedImage` with an `url` and custom options.
74 | *
75 | * The download is asynchronous and cached.
76 | *
77 | * @param url The url for the image.
78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
79 | * @param progressBlock A block called while image is downloading
80 | * @note the progress block is executed on a background queue
81 | * @param completedBlock A block called when operation has been completed. This block has no return value
82 | * and takes the requested UIImage as first parameter. In case of error the image parameter
83 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
84 | * indicating if the image was retrieved from the local cache or from the network.
85 | * The fourth parameter is the original image url.
86 | */
87 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
88 | options:(SDWebImageOptions)options
89 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
90 | completed:(nullable SDExternalCompletionBlock)completedBlock;
91 |
92 | @end
93 |
94 | #endif
95 |
--------------------------------------------------------------------------------
/SDWebImage/UIImageView+HighlightedWebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImageView+HighlightedWebCache.h"
10 |
11 | #if SD_UIKIT
12 |
13 | #import "UIView+WebCacheOperation.h"
14 | #import "UIView+WebCache.h"
15 |
16 | @implementation UIImageView (HighlightedWebCache)
17 |
18 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url {
19 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:nil];
20 | }
21 |
22 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options {
23 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:nil];
24 | }
25 |
26 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
27 | [self sd_setHighlightedImageWithURL:url options:0 progress:nil completed:completedBlock];
28 | }
29 |
30 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
31 | [self sd_setHighlightedImageWithURL:url options:options progress:nil completed:completedBlock];
32 | }
33 |
34 | - (void)sd_setHighlightedImageWithURL:(nullable NSURL *)url
35 | options:(SDWebImageOptions)options
36 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
37 | completed:(nullable SDExternalCompletionBlock)completedBlock {
38 | __weak typeof(self)weakSelf = self;
39 | [self sd_internalSetImageWithURL:url
40 | placeholderImage:nil
41 | options:options
42 | operationKey:@"UIImageViewImageOperationHighlighted"
43 | setImageBlock:^(UIImage *image, NSData *imageData) {
44 | weakSelf.highlightedImage = image;
45 | }
46 | progress:progressBlock
47 | completed:completedBlock];
48 | }
49 |
50 | @end
51 |
52 | #endif
53 |
--------------------------------------------------------------------------------
/SDWebImage/UIImageView+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | /**
16 | * Integrates SDWebImage async downloading and caching of remote images with UIImageView.
17 | *
18 | * Usage with a UITableViewCell sub-class:
19 | *
20 | * @code
21 |
22 | #import
23 |
24 | ...
25 |
26 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
27 | {
28 | static NSString *MyIdentifier = @"MyIdentifier";
29 |
30 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
31 |
32 | if (cell == nil) {
33 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier]
34 | autorelease];
35 | }
36 |
37 | // Here we use the provided sd_setImageWithURL: method to load the web image
38 | // Ensure you use a placeholder image otherwise cells will be initialized with no image
39 | [cell.imageView sd_setImageWithURL:[NSURL URLWithString:@"http://example.com/image.jpg"]
40 | placeholderImage:[UIImage imageNamed:@"placeholder"]];
41 |
42 | cell.textLabel.text = @"My Text";
43 | return cell;
44 | }
45 |
46 | * @endcode
47 | */
48 | @interface UIImageView (WebCache)
49 |
50 | /**
51 | * Set the imageView `image` with an `url`.
52 | *
53 | * The download is asynchronous and cached.
54 | *
55 | * @param url The url for the image.
56 | */
57 | - (void)sd_setImageWithURL:(nullable NSURL *)url;
58 |
59 | /**
60 | * Set the imageView `image` with an `url` and a placeholder.
61 | *
62 | * The download is asynchronous and cached.
63 | *
64 | * @param url The url for the image.
65 | * @param placeholder The image to be set initially, until the image request finishes.
66 | * @see sd_setImageWithURL:placeholderImage:options:
67 | */
68 | - (void)sd_setImageWithURL:(nullable NSURL *)url
69 | placeholderImage:(nullable UIImage *)placeholder;
70 |
71 | /**
72 | * Set the imageView `image` with an `url`, placeholder and custom options.
73 | *
74 | * The download is asynchronous and cached.
75 | *
76 | * @param url The url for the image.
77 | * @param placeholder The image to be set initially, until the image request finishes.
78 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
79 | */
80 | - (void)sd_setImageWithURL:(nullable NSURL *)url
81 | placeholderImage:(nullable UIImage *)placeholder
82 | options:(SDWebImageOptions)options;
83 |
84 | /**
85 | * Set the imageView `image` with an `url`.
86 | *
87 | * The download is asynchronous and cached.
88 | *
89 | * @param url The url for the image.
90 | * @param completedBlock A block called when operation has been completed. This block has no return value
91 | * and takes the requested UIImage as first parameter. In case of error the image parameter
92 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
93 | * indicating if the image was retrieved from the local cache or from the network.
94 | * The fourth parameter is the original image url.
95 | */
96 | - (void)sd_setImageWithURL:(nullable NSURL *)url
97 | completed:(nullable SDExternalCompletionBlock)completedBlock;
98 |
99 | /**
100 | * Set the imageView `image` with an `url`, placeholder.
101 | *
102 | * The download is asynchronous and cached.
103 | *
104 | * @param url The url for the image.
105 | * @param placeholder The image to be set initially, until the image request finishes.
106 | * @param completedBlock A block called when operation has been completed. This block has no return value
107 | * and takes the requested UIImage as first parameter. In case of error the image parameter
108 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
109 | * indicating if the image was retrieved from the local cache or from the network.
110 | * The fourth parameter is the original image url.
111 | */
112 | - (void)sd_setImageWithURL:(nullable NSURL *)url
113 | placeholderImage:(nullable UIImage *)placeholder
114 | completed:(nullable SDExternalCompletionBlock)completedBlock;
115 |
116 | /**
117 | * Set the imageView `image` with an `url`, placeholder and custom options.
118 | *
119 | * The download is asynchronous and cached.
120 | *
121 | * @param url The url for the image.
122 | * @param placeholder The image to be set initially, until the image request finishes.
123 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
124 | * @param completedBlock A block called when operation has been completed. This block has no return value
125 | * and takes the requested UIImage as first parameter. In case of error the image parameter
126 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
127 | * indicating if the image was retrieved from the local cache or from the network.
128 | * The fourth parameter is the original image url.
129 | */
130 | - (void)sd_setImageWithURL:(nullable NSURL *)url
131 | placeholderImage:(nullable UIImage *)placeholder
132 | options:(SDWebImageOptions)options
133 | completed:(nullable SDExternalCompletionBlock)completedBlock;
134 |
135 | /**
136 | * Set the imageView `image` with an `url`, placeholder and custom options.
137 | *
138 | * The download is asynchronous and cached.
139 | *
140 | * @param url The url for the image.
141 | * @param placeholder The image to be set initially, until the image request finishes.
142 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
143 | * @param progressBlock A block called while image is downloading
144 | * @note the progress block is executed on a background queue
145 | * @param completedBlock A block called when operation has been completed. This block has no return value
146 | * and takes the requested UIImage as first parameter. In case of error the image parameter
147 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
148 | * indicating if the image was retrieved from the local cache or from the network.
149 | * The fourth parameter is the original image url.
150 | */
151 | - (void)sd_setImageWithURL:(nullable NSURL *)url
152 | placeholderImage:(nullable UIImage *)placeholder
153 | options:(SDWebImageOptions)options
154 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
155 | completed:(nullable SDExternalCompletionBlock)completedBlock;
156 |
157 | /**
158 | * Set the imageView `image` with an `url` and optionally a placeholder image.
159 | *
160 | * The download is asynchronous and cached.
161 | *
162 | * @param url The url for the image.
163 | * @param placeholder The image to be set initially, until the image request finishes.
164 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
165 | * @param progressBlock A block called while image is downloading
166 | * @note the progress block is executed on a background queue
167 | * @param completedBlock A block called when operation has been completed. This block has no return value
168 | * and takes the requested UIImage as first parameter. In case of error the image parameter
169 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
170 | * indicating if the image was retrieved from the local cache or from the network.
171 | * The fourth parameter is the original image url.
172 | */
173 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
174 | placeholderImage:(nullable UIImage *)placeholder
175 | options:(SDWebImageOptions)options
176 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
177 | completed:(nullable SDExternalCompletionBlock)completedBlock;
178 |
179 | #if SD_UIKIT
180 |
181 | #pragma mark - Animation of multiple images
182 |
183 | /**
184 | * Download an array of images and starts them in an animation loop
185 | *
186 | * @param arrayOfURLs An array of NSURL
187 | */
188 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs;
189 |
190 | - (void)sd_cancelCurrentAnimationImagesLoad;
191 |
192 | #endif
193 |
194 | @end
195 |
196 | #endif
197 |
--------------------------------------------------------------------------------
/SDWebImage/UIImageView+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIImageView+WebCache.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 | #import "UIView+WebCache.h"
16 |
17 | @implementation UIImageView (WebCache)
18 |
19 | - (void)sd_setImageWithURL:(nullable NSURL *)url {
20 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
21 | }
22 |
23 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder {
24 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
25 | }
26 |
27 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
28 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
29 | }
30 |
31 | - (void)sd_setImageWithURL:(nullable NSURL *)url completed:(nullable SDExternalCompletionBlock)completedBlock {
32 | [self sd_setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
33 | }
34 |
35 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder completed:(nullable SDExternalCompletionBlock)completedBlock {
36 | [self sd_setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
37 | }
38 |
39 | - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options completed:(nullable SDExternalCompletionBlock)completedBlock {
40 | [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
41 | }
42 |
43 | - (void)sd_setImageWithURL:(nullable NSURL *)url
44 | placeholderImage:(nullable UIImage *)placeholder
45 | options:(SDWebImageOptions)options
46 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
47 | completed:(nullable SDExternalCompletionBlock)completedBlock {
48 | [self sd_internalSetImageWithURL:url
49 | placeholderImage:placeholder
50 | options:options
51 | operationKey:nil
52 | setImageBlock:nil
53 | progress:progressBlock
54 | completed:completedBlock];
55 | }
56 |
57 | - (void)sd_setImageWithPreviousCachedImageWithURL:(nullable NSURL *)url
58 | placeholderImage:(nullable UIImage *)placeholder
59 | options:(SDWebImageOptions)options
60 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
61 | completed:(nullable SDExternalCompletionBlock)completedBlock {
62 | NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url];
63 | UIImage *lastPreviousCachedImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:key];
64 |
65 | [self sd_setImageWithURL:url placeholderImage:lastPreviousCachedImage ?: placeholder options:options progress:progressBlock completed:completedBlock];
66 | }
67 |
68 | #if SD_UIKIT
69 |
70 | #pragma mark - Animation of multiple images
71 |
72 | - (void)sd_setAnimationImagesWithURLs:(nonnull NSArray *)arrayOfURLs {
73 | [self sd_cancelCurrentAnimationImagesLoad];
74 | __weak __typeof(self)wself = self;
75 |
76 | NSMutableArray> *operationsArray = [[NSMutableArray alloc] init];
77 |
78 | for (NSURL *logoImageURL in arrayOfURLs) {
79 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
80 | if (!wself) return;
81 | dispatch_main_async_safe(^{
82 | __strong UIImageView *sself = wself;
83 | [sself stopAnimating];
84 | if (sself && image) {
85 | NSMutableArray *currentImages = [[sself animationImages] mutableCopy];
86 | if (!currentImages) {
87 | currentImages = [[NSMutableArray alloc] init];
88 | }
89 | [currentImages addObject:image];
90 |
91 | sself.animationImages = currentImages;
92 | [sself setNeedsLayout];
93 | }
94 | [sself startAnimating];
95 | });
96 | }];
97 | [operationsArray addObject:operation];
98 | }
99 |
100 | [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
101 | }
102 |
103 | - (void)sd_cancelCurrentAnimationImagesLoad {
104 | [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
105 | }
106 | #endif
107 |
108 | @end
109 |
110 | #endif
111 |
--------------------------------------------------------------------------------
/SDWebImage/UIView+WebCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
16 |
17 | @interface UIView (WebCache)
18 |
19 | /**
20 | * Get the current image URL.
21 | *
22 | * Note that because of the limitations of categories this property can get out of sync
23 | * if you use setImage: directly.
24 | */
25 | - (nullable NSURL *)sd_imageURL;
26 |
27 | /**
28 | * Set the imageView `image` with an `url` and optionally a placeholder image.
29 | *
30 | * The download is asynchronous and cached.
31 | *
32 | * @param url The url for the image.
33 | * @param placeholder The image to be set initially, until the image request finishes.
34 | * @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
35 | * @param operationKey A string to be used as the operation key. If nil, will use the class name
36 | * @param setImageBlock Block used for custom set image code
37 | * @param progressBlock A block called while image is downloading
38 | * @note the progress block is executed on a background queue
39 | * @param completedBlock A block called when operation has been completed. This block has no return value
40 | * and takes the requested UIImage as first parameter. In case of error the image parameter
41 | * is nil and the second parameter may contain an NSError. The third parameter is a Boolean
42 | * indicating if the image was retrieved from the local cache or from the network.
43 | * The fourth parameter is the original image url.
44 | */
45 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
46 | placeholderImage:(nullable UIImage *)placeholder
47 | options:(SDWebImageOptions)options
48 | operationKey:(nullable NSString *)operationKey
49 | setImageBlock:(nullable SDSetImageBlock)setImageBlock
50 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
51 | completed:(nullable SDExternalCompletionBlock)completedBlock;
52 |
53 | /**
54 | * Cancel the current download
55 | */
56 | - (void)sd_cancelCurrentImageLoad;
57 |
58 | #if SD_UIKIT
59 |
60 | #pragma mark - Activity indicator
61 |
62 | /**
63 | * Show activity UIActivityIndicatorView
64 | */
65 | - (void)sd_setShowActivityIndicatorView:(BOOL)show;
66 |
67 | /**
68 | * set desired UIActivityIndicatorViewStyle
69 | *
70 | * @param style The style of the UIActivityIndicatorView
71 | */
72 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style;
73 |
74 | - (BOOL)sd_showActivityIndicatorView;
75 | - (void)sd_addActivityIndicator;
76 | - (void)sd_removeActivityIndicator;
77 |
78 | #endif
79 |
80 | @end
81 |
82 | #endif
83 |
--------------------------------------------------------------------------------
/SDWebImage/UIView+WebCache.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIView+WebCache.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 | #import "UIView+WebCacheOperation.h"
15 |
16 | static char imageURLKey;
17 |
18 | #if SD_UIKIT
19 | static char TAG_ACTIVITY_INDICATOR;
20 | static char TAG_ACTIVITY_STYLE;
21 | #endif
22 | static char TAG_ACTIVITY_SHOW;
23 |
24 | @implementation UIView (WebCache)
25 |
26 | - (nullable NSURL *)sd_imageURL {
27 | return objc_getAssociatedObject(self, &imageURLKey);
28 | }
29 |
30 | - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
31 | placeholderImage:(nullable UIImage *)placeholder
32 | options:(SDWebImageOptions)options
33 | operationKey:(nullable NSString *)operationKey
34 | setImageBlock:(nullable SDSetImageBlock)setImageBlock
35 | progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
36 | completed:(nullable SDExternalCompletionBlock)completedBlock {
37 | NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
38 | [self sd_cancelImageLoadOperationWithKey:validOperationKey];
39 | objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
40 |
41 | if (!(options & SDWebImageDelayPlaceholder)) {
42 | dispatch_main_async_safe(^{
43 | [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
44 | });
45 | }
46 |
47 | if (url) {
48 | // check if activityView is enabled or not
49 | if ([self sd_showActivityIndicatorView]) {
50 | [self sd_addActivityIndicator];
51 | }
52 |
53 | __weak __typeof(self)wself = self;
54 | id operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
55 | __strong __typeof (wself) sself = wself;
56 | [sself sd_removeActivityIndicator];
57 | if (!sself) {
58 | return;
59 | }
60 | dispatch_main_async_safe(^{
61 | if (!sself) {
62 | return;
63 | }
64 | if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
65 | completedBlock(image, error, cacheType, url);
66 | return;
67 | } else if (image) {
68 | [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
69 | [sself sd_setNeedsLayout];
70 | } else {
71 | if ((options & SDWebImageDelayPlaceholder)) {
72 | [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
73 | [sself sd_setNeedsLayout];
74 | }
75 | }
76 | if (completedBlock && finished) {
77 | completedBlock(image, error, cacheType, url);
78 | }
79 | });
80 | }];
81 | [self sd_setImageLoadOperation:operation forKey:validOperationKey];
82 | } else {
83 | dispatch_main_async_safe(^{
84 | [self sd_removeActivityIndicator];
85 | if (completedBlock) {
86 | NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
87 | completedBlock(nil, error, SDImageCacheTypeNone, url);
88 | }
89 | });
90 | }
91 | }
92 |
93 | - (void)sd_cancelCurrentImageLoad {
94 | [self sd_cancelImageLoadOperationWithKey:NSStringFromClass([self class])];
95 | }
96 |
97 | - (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
98 | if (setImageBlock) {
99 | setImageBlock(image, imageData);
100 | return;
101 | }
102 |
103 | #if SD_UIKIT || SD_MAC
104 | if ([self isKindOfClass:[UIImageView class]]) {
105 | UIImageView *imageView = (UIImageView *)self;
106 | imageView.image = image;
107 | }
108 | #endif
109 |
110 | #if SD_UIKIT
111 | if ([self isKindOfClass:[UIButton class]]) {
112 | UIButton *button = (UIButton *)self;
113 | [button setImage:image forState:UIControlStateNormal];
114 | }
115 | #endif
116 | }
117 |
118 | - (void)sd_setNeedsLayout {
119 | #if SD_UIKIT
120 | [self setNeedsLayout];
121 | #elif SD_MAC
122 | [self setNeedsLayout:YES];
123 | #endif
124 | }
125 |
126 | #pragma mark - Activity indicator
127 |
128 | #pragma mark -
129 | #if SD_UIKIT
130 | - (UIActivityIndicatorView *)activityIndicator {
131 | return (UIActivityIndicatorView *)objc_getAssociatedObject(self, &TAG_ACTIVITY_INDICATOR);
132 | }
133 |
134 | - (void)setActivityIndicator:(UIActivityIndicatorView *)activityIndicator {
135 | objc_setAssociatedObject(self, &TAG_ACTIVITY_INDICATOR, activityIndicator, OBJC_ASSOCIATION_RETAIN);
136 | }
137 | #endif
138 |
139 | - (void)sd_setShowActivityIndicatorView:(BOOL)show {
140 | objc_setAssociatedObject(self, &TAG_ACTIVITY_SHOW, @(show), OBJC_ASSOCIATION_RETAIN);
141 | }
142 |
143 | - (BOOL)sd_showActivityIndicatorView {
144 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_SHOW) boolValue];
145 | }
146 |
147 | #if SD_UIKIT
148 | - (void)sd_setIndicatorStyle:(UIActivityIndicatorViewStyle)style{
149 | objc_setAssociatedObject(self, &TAG_ACTIVITY_STYLE, [NSNumber numberWithInt:style], OBJC_ASSOCIATION_RETAIN);
150 | }
151 |
152 | - (int)sd_getIndicatorStyle{
153 | return [objc_getAssociatedObject(self, &TAG_ACTIVITY_STYLE) intValue];
154 | }
155 | #endif
156 |
157 | - (void)sd_addActivityIndicator {
158 | #if SD_UIKIT
159 | if (!self.activityIndicator) {
160 | self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:[self sd_getIndicatorStyle]];
161 | self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
162 |
163 | dispatch_main_async_safe(^{
164 | [self addSubview:self.activityIndicator];
165 |
166 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
167 | attribute:NSLayoutAttributeCenterX
168 | relatedBy:NSLayoutRelationEqual
169 | toItem:self
170 | attribute:NSLayoutAttributeCenterX
171 | multiplier:1.0
172 | constant:0.0]];
173 | [self addConstraint:[NSLayoutConstraint constraintWithItem:self.activityIndicator
174 | attribute:NSLayoutAttributeCenterY
175 | relatedBy:NSLayoutRelationEqual
176 | toItem:self
177 | attribute:NSLayoutAttributeCenterY
178 | multiplier:1.0
179 | constant:0.0]];
180 | });
181 | }
182 |
183 | dispatch_main_async_safe(^{
184 | [self.activityIndicator startAnimating];
185 | });
186 | #endif
187 | }
188 |
189 | - (void)sd_removeActivityIndicator {
190 | #if SD_UIKIT
191 | if (self.activityIndicator) {
192 | [self.activityIndicator removeFromSuperview];
193 | self.activityIndicator = nil;
194 | }
195 | #endif
196 | }
197 |
198 | @end
199 |
200 | #endif
201 |
--------------------------------------------------------------------------------
/SDWebImage/UIView+WebCacheOperation.h:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "SDWebImageCompat.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "SDWebImageManager.h"
14 |
15 | @interface UIView (WebCacheOperation)
16 |
17 | /**
18 | * Set the image load operation (storage in a UIView based dictionary)
19 | *
20 | * @param operation the operation
21 | * @param key key for storing the operation
22 | */
23 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key;
24 |
25 | /**
26 | * Cancel all operations for the current UIView and key
27 | *
28 | * @param key key for identifying the operations
29 | */
30 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key;
31 |
32 | /**
33 | * Just remove the operations corresponding to the current UIView and key without cancelling them
34 | *
35 | * @param key key for identifying the operations
36 | */
37 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key;
38 |
39 | @end
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/SDWebImage/UIView+WebCacheOperation.m:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of the SDWebImage package.
3 | * (c) Olivier Poitrey
4 | *
5 | * For the full copyright and license information, please view the LICENSE
6 | * file that was distributed with this source code.
7 | */
8 |
9 | #import "UIView+WebCacheOperation.h"
10 |
11 | #if SD_UIKIT || SD_MAC
12 |
13 | #import "objc/runtime.h"
14 |
15 | static char loadOperationKey;
16 |
17 | typedef NSMutableDictionary SDOperationsDictionary;
18 |
19 | @implementation UIView (WebCacheOperation)
20 |
21 | - (SDOperationsDictionary *)operationDictionary {
22 | SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
23 | if (operations) {
24 | return operations;
25 | }
26 | operations = [NSMutableDictionary dictionary];
27 | objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
28 | return operations;
29 | }
30 |
31 | - (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {
32 | if (key) {
33 | [self sd_cancelImageLoadOperationWithKey:key];
34 | if (operation) {
35 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
36 | operationDictionary[key] = operation;
37 | }
38 | }
39 | }
40 |
41 | - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
42 | // Cancel in progress downloader from queue
43 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
44 | id operations = operationDictionary[key];
45 | if (operations) {
46 | if ([operations isKindOfClass:[NSArray class]]) {
47 | for (id operation in operations) {
48 | if (operation) {
49 | [operation cancel];
50 | }
51 | }
52 | } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){
53 | [(id) operations cancel];
54 | }
55 | [operationDictionary removeObjectForKey:key];
56 | }
57 | }
58 |
59 | - (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key {
60 | if (key) {
61 | SDOperationsDictionary *operationDictionary = [self operationDictionary];
62 | [operationDictionary removeObjectForKey:key];
63 | }
64 | }
65 |
66 | @end
67 |
68 | #endif
69 |
--------------------------------------------------------------------------------