├── screenshots.png
├── SRCarouselViewDemo
├── SRCarouselViewDemo
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── logo01.jpg
│ ├── logo02.jpg
│ ├── logo03.jpg
│ ├── logo04.jpg
│ ├── logo05.jpg
│ ├── placeholder.png
│ ├── ViewController.h
│ ├── AppDelegate.h
│ ├── main.m
│ ├── Info.plist
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── Main.storyboard
│ ├── ViewController.m
│ └── LaunchScreen.storyboard
└── SRCarouselViewDemo.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── project.pbxproj
├── LICENSE
├── .gitignore
├── SRCarouselView
├── SRCarouselView.h
└── SRCarouselView.m
└── README.md
/screenshots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/screenshots.png
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/logo01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/logo01.jpg
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/logo02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/logo02.jpg
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/logo03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/logo03.jpg
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/logo04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/logo04.jpg
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/logo05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/logo05.jpg
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/guowilling/SRCarouselView/HEAD/SRCarouselViewDemo/SRCarouselViewDemo/placeholder.png
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // SRCarouselViewDemo
4 | //
5 | // Created by 郭伟林 on 16/9/3.
6 | // Copyright © 2016年 SR. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // SRCarouselViewDemo
4 | //
5 | // Created by 郭伟林 on 16/9/3.
6 | // Copyright © 2016年 SR. 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 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SRCarouselViewDemo
4 | //
5 | // Created by 郭伟林 on 16/9/3.
6 | // Copyright © 2016年 SR. 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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Willing Guo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/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" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UIStatusBarHidden
39 |
40 | UISupportedInterfaceOrientations
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xcuserstate
23 |
24 | ## Obj-C/Swift specific
25 | *.hmap
26 | *.ipa
27 | *.dSYM.zip
28 | *.dSYM
29 |
30 | # CocoaPods
31 | #
32 | # We recommend against adding the Pods directory to your .gitignore. However
33 | # you should judge for yourself, the pros and cons are mentioned at:
34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
35 | #
36 | # Pods/
37 |
38 | # Carthage
39 | #
40 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
41 | # Carthage/Checkouts
42 |
43 | Carthage/Build
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
51 |
52 | fastlane/report.xml
53 | fastlane/screenshots
54 |
55 | #Code Injection
56 | #
57 | # After new code Injection tools there's a generated folder /iOSInjectionProject
58 | # https://github.com/johnno1962/injectionforxcode
59 |
60 | iOSInjectionProject/
61 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // SRCarouselViewDemo
4 | //
5 | // Created by 郭伟林 on 16/9/3.
6 | // Copyright © 2016年 SR. 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 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // 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.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // 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.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // 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.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/SRCarouselView/SRCarouselView.h:
--------------------------------------------------------------------------------
1 | //
2 | // SRCarouselView.h
3 | // SRCarouselView
4 | //
5 | // Created by https://github.com/guowilling on 17/1/10.
6 | // Copyright © 2017年 SR. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol SRCarouselViewDelegate
12 |
13 | - (void)carouselViewDidTapCarouselViewAtIndex:(NSInteger)index;
14 |
15 | @end
16 |
17 | typedef void (^SRDidTapCarouselViewAtIndexBlock)(NSInteger index);
18 |
19 | @interface SRCarouselView : UIView
20 |
21 | /**
22 | The time interval of auto paging, default is 5.0s.
23 | */
24 | @property (nonatomic, assign) NSTimeInterval autoPagingInterval;
25 |
26 | @property (nonatomic, strong) UIColor *currentPageIndicatorTintColor;
27 | @property (nonatomic, strong) UIColor *pageIndicatorTintColor;
28 |
29 | @property (nonatomic, strong) UIImage *currentPageIndicatorImage;
30 | @property (nonatomic, strong) UIImage *pageIndicatorImage;
31 |
32 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary;
33 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray;
34 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray placeholderImage:(UIImage *)placeholderImage;
35 |
36 | /**
37 | Creates and returns a infinite carousel view with imageArrary, describeArray, placeholderImage and delegate.
38 |
39 | @param imageArrary An array contains local images, or urls of images, or mixed of them.
40 | @param describeArray An array contains image describes which in the same order as the images.
41 | @param placeholderImage The placeholder image when network image have not downloaded.
42 | @param delegate The receiver’s delegate object.
43 | @return A newly carousel view.
44 | */
45 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary
46 | describeArray:(NSArray *)describeArray
47 | placeholderImage:(UIImage *)placeholderImage
48 | delegate:(id)delegate;
49 |
50 | /**
51 | Creates and returns a infinite carousel view with imageArrary, describeArray, placeholderImage and block.
52 |
53 | @param imageArrary An array contains local images, or urls of images, or mixed of them.
54 | @param describeArray An array contains image describes which in the same order as the images.
55 | @param placeholderImage The placeholder image when network image have not downloaded.
56 | @param block A block object to be executed when tap the carousel view.
57 | @return A newly carousel view.
58 | */
59 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary
60 | describeArray:(NSArray *)describeArray
61 | placeholderImage:(UIImage *)placeholderImage
62 | block:(SRDidTapCarouselViewAtIndexBlock)block;
63 |
64 | + (void)clearCachedImages;
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // SRCarouselViewDemo
4 | //
5 | // Created by 郭伟林 on 16/9/3.
6 | // Copyright © 2016年 SR. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "SRCarouselView.h"
11 |
12 | @interface ViewController ()
13 |
14 | @end
15 |
16 | @implementation ViewController
17 |
18 | - (BOOL)prefersStatusBarHidden {
19 | return YES;
20 | }
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 |
25 | [self carouselViewWithLocalImages];
26 |
27 | [self carouselViewWithNetworkImages];
28 |
29 | [self carouselViewWithMixedImages];
30 | }
31 |
32 | - (void)carouselViewWithLocalImages {
33 | NSArray *imageArray = @[[UIImage imageNamed:@"logo01.jpg"],
34 | [UIImage imageNamed:@"logo02.jpg"],
35 | [UIImage imageNamed:@"logo03.jpg"],
36 | [UIImage imageNamed:@"logo04.jpg"],
37 | [UIImage imageNamed:@"logo05.jpg"]];
38 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
39 | for (NSInteger i = 0; i < imageArray.count; i++) {
40 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description %zd", i + 1];
41 | [describeArray addObject:tempDesc];
42 | }
43 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:nil placeholderImage:nil delegate:self];
44 | carouselView.frame = CGRectMake(0, 44, self.view.frame.size.width, 200);
45 | carouselView.autoPagingInterval = 10.0;
46 | [self.view addSubview:carouselView];
47 | }
48 |
49 | - (void)carouselViewWithNetworkImages {
50 | NSArray *imageArray = @[@"https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?x-oss-process=image/format,png",
51 | @"https://yixunfiles-ali.yixun.arhieason.com/8f8b02a025a7b62e81cafc4e9d89f70e_jpg.jpg?x-oss-process=image/format,png",
52 | @"https://yixunfiles-ali.yixun.arhieason.com/6c72618dc3412974369d544a2734d5cb_jpg.jpg?x-oss-process=image/format,png",
53 | @"https://yixunfiles-ali.yixun.arhieason.com/63a0bb8cd057e12c45ecbcb7f24e4ecf_jpg.jpg?x-oss-process=image/format,png",
54 | @"https://yixunfiles-ali.yixun.arhieason.com/2393d812560715fb9a38df9550b1f749_jpg.jpg?x-oss-process=image/format,png"];
55 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
56 | for (NSInteger i = 0; i < imageArray.count; i++) {
57 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description %zd", i];
58 | [describeArray addObject:tempDesc];
59 | }
60 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:nil placeholderImage:[UIImage imageNamed:@"placeholder_image.jpg"] block:^(NSInteger index) {
61 | NSLog(@"index: %zd", index);
62 | }];
63 | carouselView.frame = CGRectMake(0, 245, self.view.frame.size.width, 200);
64 | [self.view addSubview:carouselView];
65 | }
66 |
67 | - (void)carouselViewWithMixedImages {
68 | NSArray *imageArray = @[@"https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?x-oss-process=image/format,png",
69 | @"https://yixunfiles-ali.yixun.arhieason.com/8f8b02a025a7b62e81cafc4e9d89f70e_jpg.jpg?x-oss-process=image/format,png",
70 | [UIImage imageNamed:@"logo03.jpg"],
71 | [UIImage imageNamed:@"logo04.jpg"],
72 | [UIImage imageNamed:@"logo05.jpg"]];
73 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
74 | for (NSInteger i = 0; i < imageArray.count; i++) {
75 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description"];
76 | [describeArray addObject:tempDesc];
77 | }
78 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:describeArray placeholderImage:[UIImage imageNamed:@"placeholder.png"] delegate:self];
79 | carouselView.frame = CGRectMake(0, 446, self.view.frame.size.width, 200);
80 | carouselView.autoPagingInterval = 10.0;
81 | [self.view addSubview:carouselView];
82 | }
83 |
84 | - (void)carouselViewDidTapCarouselViewAtIndex:(NSInteger)index {
85 | NSLog(@"carouselViewDidTapCarouselViewAtIndex index: %zd", index);
86 | }
87 |
88 | @end
89 |
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo/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 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SRCarouselView
2 |
3 | A carousel view that only uses two UIImageView to achieve infinite carousel.
4 |
5 | 
6 |
7 | ## Features
8 |
9 | * [x] Creates with an array that can contain local image, network image or both of them.
10 | * [x] Not rely on any third-party libraries, use the native api to download and cache image.
11 | * [x] UIPageControl will be displayed on the right If there are descriptions, otherwise displayed on the center.
12 |
13 | ## APIs
14 |
15 | ````objc
16 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary;
17 |
18 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray;
19 |
20 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray placeholderImage:(UIImage *)placeholderImage;
21 |
22 | /**
23 | Creates and returns a infinite carousel view with imageArrary, describeArray, placeholderImage and delegate.
24 |
25 | @param imageArrary An array contains local images, or urls of images, or mixed of them.
26 | @param describeArray An array contains image describes which in the same order as the images.
27 | @param placeholderImage The placeholder image when network image have not downloaded.
28 | @param delegate The receiver’s delegate object.
29 | @return A newly carousel view.
30 | */
31 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray placeholderImage:(UIImage *)placeholderImage delegate:(id)delegate;
32 |
33 | /**
34 | Creates and returns a infinite carousel view with imageArrary, describeArray, placeholderImage and block.
35 |
36 | @param imageArrary An array contains local images, or urls of images, or mixed of them.
37 | @param describeArray An array contains image describes which in the same order as the images.
38 | @param placeholderImage The placeholder image when network image have not downloaded.
39 | @param block A block object to be executed when tap the carousel view.
40 | @return A newly carousel view.
41 | */
42 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray placeholderImage:(UIImage *)placeholderImage block:(DidTapCarouselViewAtIndexBlock)block;
43 | ````
44 |
45 | ## Usage
46 |
47 | ````objc
48 | // local images
49 | NSArray *imageArray = @[[UIImage imageNamed:@"logo01.jpg"],
50 | [UIImage imageNamed:@"logo02.jpg"],
51 | [UIImage imageNamed:@"logo03.jpg"],
52 | [UIImage imageNamed:@"logo04.jpg"],
53 | [UIImage imageNamed:@"logo05.jpg"]];
54 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
55 | for (NSInteger i = 0; i < imageArray.count; i++) {
56 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description %zd", i + 1];
57 | [describeArray addObject:tempDesc];
58 | }
59 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:nil placeholderImage:nil delegate:self];
60 | carouselView.frame = CGRectMake(0, 44, self.view.frame.size.width, 200);
61 | carouselView.autoPagingInterval = 10.0;
62 | [self.view addSubview:carouselView];
63 | ````
64 |
65 | ````objc
66 | // network images
67 | NSArray *imageArray = @[@"https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?x-oss-process=image/format,png",
68 | @"https://yixunfiles-ali.yixun.arhieason.com/8f8b02a025a7b62e81cafc4e9d89f70e_jpg.jpg?x-oss-process=image/format,png",
69 | @"https://yixunfiles-ali.yixun.arhieason.com/6c72618dc3412974369d544a2734d5cb_jpg.jpg?x-oss-process=image/format,png",
70 | @"https://yixunfiles-ali.yixun.arhieason.com/63a0bb8cd057e12c45ecbcb7f24e4ecf_jpg.jpg?x-oss-process=image/format,png",
71 | @"https://yixunfiles-ali.yixun.arhieason.com/2393d812560715fb9a38df9550b1f749_jpg.jpg?x-oss-process=image/format,png"];
72 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
73 | for (NSInteger i = 0; i < imageArray.count; i++) {
74 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description %zd", i];
75 | [describeArray addObject:tempDesc];
76 | }
77 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:nil placeholderImage:[UIImage imageNamed:@"placeholder_image.jpg"] block:^(NSInteger index) {
78 | NSLog(@"index: %zd", index);
79 | }];
80 | carouselView.frame = CGRectMake(0, 245, self.view.frame.size.width, 200);
81 | [self.view addSubview:carouselView];
82 | ````
83 |
84 | ````objc
85 | // local and network Images
86 | NSArray *imageArray = @[@"https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?x-oss-process=image/format,png",
87 | @"https://yixunfiles-ali.yixun.arhieason.com/8f8b02a025a7b62e81cafc4e9d89f70e_jpg.jpg?x-oss-process=image/format,png",
88 | [UIImage imageNamed:@"logo03.jpg"],
89 | [UIImage imageNamed:@"logo04.jpg"],
90 | [UIImage imageNamed:@"logo05.jpg"]];
91 | NSMutableArray *describeArray = [[NSMutableArray alloc] init];
92 | for (NSInteger i = 0; i < imageArray.count; i++) {
93 | NSString *tempDesc = [NSString stringWithFormat:@"Image Description"];
94 | [describeArray addObject:tempDesc];
95 | }
96 | SRCarouselView *carouselView = [SRCarouselView sr_carouselViewWithImageArrary:imageArray describeArray:describeArray placeholderImage:[UIImage imageNamed:@"placeholder"] delegate:self];
97 | carouselView.frame = CGRectMake(0, 446, self.view.frame.size.width, 200);
98 | carouselView.autoPagingInterval = 10.0;
99 | [self.view addSubview:carouselView];
100 | ````
101 |
102 | ## Custom
103 |
104 | ````objc
105 | /**
106 | The time interval of auto paging, default is 5.0s.
107 | */
108 | @property (nonatomic, assign) NSTimeInterval autoPagingInterval;
109 |
110 | @property (nonatomic, strong) UIColor *currentPageIndicatorTintColor;
111 | @property (nonatomic, strong) UIColor *pageIndicatorTintColor;
112 |
113 | @property (nonatomic, strong) UIImage *currentPageIndicatorImage;
114 | @property (nonatomic, strong) UIImage *pageIndicatorImage;
115 | ````
--------------------------------------------------------------------------------
/SRCarouselViewDemo/SRCarouselViewDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 685FD2C02148EF5D000ED53D /* placeholder.png in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BA2148EF5D000ED53D /* placeholder.png */; };
11 | 685FD2C12148EF5D000ED53D /* logo02.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BB2148EF5D000ED53D /* logo02.jpg */; };
12 | 685FD2C22148EF5D000ED53D /* logo03.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BC2148EF5D000ED53D /* logo03.jpg */; };
13 | 685FD2C32148EF5D000ED53D /* logo05.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BD2148EF5D000ED53D /* logo05.jpg */; };
14 | 685FD2C42148EF5D000ED53D /* logo04.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BE2148EF5D000ED53D /* logo04.jpg */; };
15 | 685FD2C52148EF5D000ED53D /* logo01.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 685FD2BF2148EF5D000ED53D /* logo01.jpg */; };
16 | 68E05AE21D7AC0BF00F214D5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E05AE11D7AC0BF00F214D5 /* main.m */; };
17 | 68E05AE51D7AC0BF00F214D5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E05AE41D7AC0BF00F214D5 /* AppDelegate.m */; };
18 | 68E05AE81D7AC0C000F214D5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68E05AE71D7AC0BF00F214D5 /* ViewController.m */; };
19 | 68E05AEB1D7AC0C000F214D5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 68E05AE91D7AC0C000F214D5 /* Main.storyboard */; };
20 | 68E05AED1D7AC0C000F214D5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 68E05AEC1D7AC0C000F214D5 /* Assets.xcassets */; };
21 | 6EBF581C1EB1164600B2B986 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6EBF581B1EB1164600B2B986 /* LaunchScreen.storyboard */; };
22 | 6ED4F85B1EBE467E0074633B /* SRCarouselView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6ED4F85A1EBE467E0074633B /* SRCarouselView.m */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXFileReference section */
26 | 685FD2BA2148EF5D000ED53D /* placeholder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = placeholder.png; sourceTree = ""; };
27 | 685FD2BB2148EF5D000ED53D /* logo02.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = logo02.jpg; sourceTree = ""; };
28 | 685FD2BC2148EF5D000ED53D /* logo03.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = logo03.jpg; sourceTree = ""; };
29 | 685FD2BD2148EF5D000ED53D /* logo05.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = logo05.jpg; sourceTree = ""; };
30 | 685FD2BE2148EF5D000ED53D /* logo04.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = logo04.jpg; sourceTree = ""; };
31 | 685FD2BF2148EF5D000ED53D /* logo01.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = logo01.jpg; sourceTree = ""; };
32 | 68E05ADD1D7AC0BF00F214D5 /* SRCarouselViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SRCarouselViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
33 | 68E05AE11D7AC0BF00F214D5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
34 | 68E05AE31D7AC0BF00F214D5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
35 | 68E05AE41D7AC0BF00F214D5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
36 | 68E05AE61D7AC0BF00F214D5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
37 | 68E05AE71D7AC0BF00F214D5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
38 | 68E05AEA1D7AC0C000F214D5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
39 | 68E05AEC1D7AC0C000F214D5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
40 | 68E05AF11D7AC0C000F214D5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
41 | 6EBF581B1EB1164600B2B986 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
42 | 6ED4F8591EBE467E0074633B /* SRCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRCarouselView.h; sourceTree = ""; };
43 | 6ED4F85A1EBE467E0074633B /* SRCarouselView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRCarouselView.m; sourceTree = ""; };
44 | /* End PBXFileReference section */
45 |
46 | /* Begin PBXFrameworksBuildPhase section */
47 | 68E05ADA1D7AC0BF00F214D5 /* Frameworks */ = {
48 | isa = PBXFrameworksBuildPhase;
49 | buildActionMask = 2147483647;
50 | files = (
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | 685FD2B92148EEE1000ED53D /* Resource */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 685FD2BF2148EF5D000ED53D /* logo01.jpg */,
61 | 685FD2BB2148EF5D000ED53D /* logo02.jpg */,
62 | 685FD2BC2148EF5D000ED53D /* logo03.jpg */,
63 | 685FD2BE2148EF5D000ED53D /* logo04.jpg */,
64 | 685FD2BD2148EF5D000ED53D /* logo05.jpg */,
65 | 685FD2BA2148EF5D000ED53D /* placeholder.png */,
66 | );
67 | name = Resource;
68 | sourceTree = "";
69 | };
70 | 68E05AD41D7AC0BF00F214D5 = {
71 | isa = PBXGroup;
72 | children = (
73 | 68E05ADF1D7AC0BF00F214D5 /* SRCarouselViewDemo */,
74 | 68E05ADE1D7AC0BF00F214D5 /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 68E05ADE1D7AC0BF00F214D5 /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 68E05ADD1D7AC0BF00F214D5 /* SRCarouselViewDemo.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 68E05ADF1D7AC0BF00F214D5 /* SRCarouselViewDemo */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 68E05B131D7AC1F200F214D5 /* SRCarouselView */,
90 | 68E05AE61D7AC0BF00F214D5 /* ViewController.h */,
91 | 68E05AE71D7AC0BF00F214D5 /* ViewController.m */,
92 | 68E05AE91D7AC0C000F214D5 /* Main.storyboard */,
93 | 68E05AE01D7AC0BF00F214D5 /* Supporting Files */,
94 | );
95 | path = SRCarouselViewDemo;
96 | sourceTree = "";
97 | };
98 | 68E05AE01D7AC0BF00F214D5 /* Supporting Files */ = {
99 | isa = PBXGroup;
100 | children = (
101 | 685FD2B92148EEE1000ED53D /* Resource */,
102 | 68E05AE11D7AC0BF00F214D5 /* main.m */,
103 | 68E05AE31D7AC0BF00F214D5 /* AppDelegate.h */,
104 | 68E05AE41D7AC0BF00F214D5 /* AppDelegate.m */,
105 | 6EBF581B1EB1164600B2B986 /* LaunchScreen.storyboard */,
106 | 68E05AEC1D7AC0C000F214D5 /* Assets.xcassets */,
107 | 68E05AF11D7AC0C000F214D5 /* Info.plist */,
108 | );
109 | name = "Supporting Files";
110 | sourceTree = "";
111 | };
112 | 68E05B131D7AC1F200F214D5 /* SRCarouselView */ = {
113 | isa = PBXGroup;
114 | children = (
115 | 6ED4F8591EBE467E0074633B /* SRCarouselView.h */,
116 | 6ED4F85A1EBE467E0074633B /* SRCarouselView.m */,
117 | );
118 | name = SRCarouselView;
119 | path = ../../SRCarouselView;
120 | sourceTree = "";
121 | };
122 | /* End PBXGroup section */
123 |
124 | /* Begin PBXNativeTarget section */
125 | 68E05ADC1D7AC0BF00F214D5 /* SRCarouselViewDemo */ = {
126 | isa = PBXNativeTarget;
127 | buildConfigurationList = 68E05B0A1D7AC0C000F214D5 /* Build configuration list for PBXNativeTarget "SRCarouselViewDemo" */;
128 | buildPhases = (
129 | 68E05AD91D7AC0BF00F214D5 /* Sources */,
130 | 68E05ADA1D7AC0BF00F214D5 /* Frameworks */,
131 | 68E05ADB1D7AC0BF00F214D5 /* Resources */,
132 | );
133 | buildRules = (
134 | );
135 | dependencies = (
136 | );
137 | name = SRCarouselViewDemo;
138 | productName = SRInfiniteCarouselViewDemo;
139 | productReference = 68E05ADD1D7AC0BF00F214D5 /* SRCarouselViewDemo.app */;
140 | productType = "com.apple.product-type.application";
141 | };
142 | /* End PBXNativeTarget section */
143 |
144 | /* Begin PBXProject section */
145 | 68E05AD51D7AC0BF00F214D5 /* Project object */ = {
146 | isa = PBXProject;
147 | attributes = {
148 | LastUpgradeCheck = 0830;
149 | ORGANIZATIONNAME = SR;
150 | TargetAttributes = {
151 | 68E05ADC1D7AC0BF00F214D5 = {
152 | CreatedOnToolsVersion = 7.3;
153 | DevelopmentTeam = L8C6M6B8E9;
154 | ProvisioningStyle = Manual;
155 | };
156 | };
157 | };
158 | buildConfigurationList = 68E05AD81D7AC0BF00F214D5 /* Build configuration list for PBXProject "SRCarouselViewDemo" */;
159 | compatibilityVersion = "Xcode 3.2";
160 | developmentRegion = English;
161 | hasScannedForEncodings = 0;
162 | knownRegions = (
163 | en,
164 | Base,
165 | );
166 | mainGroup = 68E05AD41D7AC0BF00F214D5;
167 | productRefGroup = 68E05ADE1D7AC0BF00F214D5 /* Products */;
168 | projectDirPath = "";
169 | projectRoot = "";
170 | targets = (
171 | 68E05ADC1D7AC0BF00F214D5 /* SRCarouselViewDemo */,
172 | );
173 | };
174 | /* End PBXProject section */
175 |
176 | /* Begin PBXResourcesBuildPhase section */
177 | 68E05ADB1D7AC0BF00F214D5 /* Resources */ = {
178 | isa = PBXResourcesBuildPhase;
179 | buildActionMask = 2147483647;
180 | files = (
181 | 6EBF581C1EB1164600B2B986 /* LaunchScreen.storyboard in Resources */,
182 | 685FD2C02148EF5D000ED53D /* placeholder.png in Resources */,
183 | 685FD2C12148EF5D000ED53D /* logo02.jpg in Resources */,
184 | 685FD2C22148EF5D000ED53D /* logo03.jpg in Resources */,
185 | 68E05AED1D7AC0C000F214D5 /* Assets.xcassets in Resources */,
186 | 685FD2C52148EF5D000ED53D /* logo01.jpg in Resources */,
187 | 68E05AEB1D7AC0C000F214D5 /* Main.storyboard in Resources */,
188 | 685FD2C42148EF5D000ED53D /* logo04.jpg in Resources */,
189 | 685FD2C32148EF5D000ED53D /* logo05.jpg in Resources */,
190 | );
191 | runOnlyForDeploymentPostprocessing = 0;
192 | };
193 | /* End PBXResourcesBuildPhase section */
194 |
195 | /* Begin PBXSourcesBuildPhase section */
196 | 68E05AD91D7AC0BF00F214D5 /* Sources */ = {
197 | isa = PBXSourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | 68E05AE81D7AC0C000F214D5 /* ViewController.m in Sources */,
201 | 6ED4F85B1EBE467E0074633B /* SRCarouselView.m in Sources */,
202 | 68E05AE51D7AC0BF00F214D5 /* AppDelegate.m in Sources */,
203 | 68E05AE21D7AC0BF00F214D5 /* main.m in Sources */,
204 | );
205 | runOnlyForDeploymentPostprocessing = 0;
206 | };
207 | /* End PBXSourcesBuildPhase section */
208 |
209 | /* Begin PBXVariantGroup section */
210 | 68E05AE91D7AC0C000F214D5 /* Main.storyboard */ = {
211 | isa = PBXVariantGroup;
212 | children = (
213 | 68E05AEA1D7AC0C000F214D5 /* Base */,
214 | );
215 | name = Main.storyboard;
216 | sourceTree = "";
217 | };
218 | /* End PBXVariantGroup section */
219 |
220 | /* Begin XCBuildConfiguration section */
221 | 68E05B081D7AC0C000F214D5 /* Debug */ = {
222 | isa = XCBuildConfiguration;
223 | buildSettings = {
224 | ALWAYS_SEARCH_USER_PATHS = NO;
225 | CLANG_ANALYZER_NONNULL = YES;
226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
227 | CLANG_CXX_LIBRARY = "libc++";
228 | CLANG_ENABLE_MODULES = YES;
229 | CLANG_ENABLE_OBJC_ARC = YES;
230 | CLANG_WARN_BOOL_CONVERSION = YES;
231 | CLANG_WARN_CONSTANT_CONVERSION = YES;
232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
233 | CLANG_WARN_EMPTY_BODY = YES;
234 | CLANG_WARN_ENUM_CONVERSION = YES;
235 | CLANG_WARN_INFINITE_RECURSION = YES;
236 | CLANG_WARN_INT_CONVERSION = YES;
237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
238 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
239 | CLANG_WARN_UNREACHABLE_CODE = YES;
240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
241 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
242 | COPY_PHASE_STRIP = NO;
243 | DEBUG_INFORMATION_FORMAT = dwarf;
244 | ENABLE_STRICT_OBJC_MSGSEND = YES;
245 | ENABLE_TESTABILITY = YES;
246 | GCC_C_LANGUAGE_STANDARD = gnu99;
247 | GCC_DYNAMIC_NO_PIC = NO;
248 | GCC_NO_COMMON_BLOCKS = YES;
249 | GCC_OPTIMIZATION_LEVEL = 0;
250 | GCC_PREPROCESSOR_DEFINITIONS = (
251 | "DEBUG=1",
252 | "$(inherited)",
253 | );
254 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
255 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
256 | GCC_WARN_UNDECLARED_SELECTOR = YES;
257 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
258 | GCC_WARN_UNUSED_FUNCTION = YES;
259 | GCC_WARN_UNUSED_VARIABLE = YES;
260 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
261 | MTL_ENABLE_DEBUG_INFO = YES;
262 | ONLY_ACTIVE_ARCH = YES;
263 | SDKROOT = iphoneos;
264 | };
265 | name = Debug;
266 | };
267 | 68E05B091D7AC0C000F214D5 /* Release */ = {
268 | isa = XCBuildConfiguration;
269 | buildSettings = {
270 | ALWAYS_SEARCH_USER_PATHS = NO;
271 | CLANG_ANALYZER_NONNULL = YES;
272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
273 | CLANG_CXX_LIBRARY = "libc++";
274 | CLANG_ENABLE_MODULES = YES;
275 | CLANG_ENABLE_OBJC_ARC = YES;
276 | CLANG_WARN_BOOL_CONVERSION = YES;
277 | CLANG_WARN_CONSTANT_CONVERSION = YES;
278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
279 | CLANG_WARN_EMPTY_BODY = YES;
280 | CLANG_WARN_ENUM_CONVERSION = YES;
281 | CLANG_WARN_INFINITE_RECURSION = YES;
282 | CLANG_WARN_INT_CONVERSION = YES;
283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
284 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
285 | CLANG_WARN_UNREACHABLE_CODE = YES;
286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
288 | COPY_PHASE_STRIP = NO;
289 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
290 | ENABLE_NS_ASSERTIONS = NO;
291 | ENABLE_STRICT_OBJC_MSGSEND = YES;
292 | GCC_C_LANGUAGE_STANDARD = gnu99;
293 | GCC_NO_COMMON_BLOCKS = YES;
294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
296 | GCC_WARN_UNDECLARED_SELECTOR = YES;
297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
298 | GCC_WARN_UNUSED_FUNCTION = YES;
299 | GCC_WARN_UNUSED_VARIABLE = YES;
300 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
301 | MTL_ENABLE_DEBUG_INFO = NO;
302 | SDKROOT = iphoneos;
303 | VALIDATE_PRODUCT = YES;
304 | };
305 | name = Release;
306 | };
307 | 68E05B0B1D7AC0C000F214D5 /* Debug */ = {
308 | isa = XCBuildConfiguration;
309 | buildSettings = {
310 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
311 | DEVELOPMENT_TEAM = L8C6M6B8E9;
312 | INFOPLIST_FILE = SRCarouselViewDemo/Info.plist;
313 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
314 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
315 | PRODUCT_BUNDLE_IDENTIFIER = com.willing.SRCarouselViewDemo;
316 | PRODUCT_NAME = "$(TARGET_NAME)";
317 | PROVISIONING_PROFILE = "96079369-07f1-4278-b161-c73a21b11f47";
318 | PROVISIONING_PROFILE_SPECIFIER = "PP For All";
319 | };
320 | name = Debug;
321 | };
322 | 68E05B0C1D7AC0C000F214D5 /* Release */ = {
323 | isa = XCBuildConfiguration;
324 | buildSettings = {
325 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
326 | DEVELOPMENT_TEAM = L8C6M6B8E9;
327 | INFOPLIST_FILE = SRCarouselViewDemo/Info.plist;
328 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
329 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
330 | PRODUCT_BUNDLE_IDENTIFIER = com.willing.SRCarouselViewDemo;
331 | PRODUCT_NAME = "$(TARGET_NAME)";
332 | PROVISIONING_PROFILE = "96079369-07f1-4278-b161-c73a21b11f47";
333 | PROVISIONING_PROFILE_SPECIFIER = "PP For All";
334 | };
335 | name = Release;
336 | };
337 | /* End XCBuildConfiguration section */
338 |
339 | /* Begin XCConfigurationList section */
340 | 68E05AD81D7AC0BF00F214D5 /* Build configuration list for PBXProject "SRCarouselViewDemo" */ = {
341 | isa = XCConfigurationList;
342 | buildConfigurations = (
343 | 68E05B081D7AC0C000F214D5 /* Debug */,
344 | 68E05B091D7AC0C000F214D5 /* Release */,
345 | );
346 | defaultConfigurationIsVisible = 0;
347 | defaultConfigurationName = Release;
348 | };
349 | 68E05B0A1D7AC0C000F214D5 /* Build configuration list for PBXNativeTarget "SRCarouselViewDemo" */ = {
350 | isa = XCConfigurationList;
351 | buildConfigurations = (
352 | 68E05B0B1D7AC0C000F214D5 /* Debug */,
353 | 68E05B0C1D7AC0C000F214D5 /* Release */,
354 | );
355 | defaultConfigurationIsVisible = 0;
356 | defaultConfigurationName = Release;
357 | };
358 | /* End XCConfigurationList section */
359 | };
360 | rootObject = 68E05AD51D7AC0BF00F214D5 /* Project object */;
361 | }
362 |
--------------------------------------------------------------------------------
/SRCarouselView/SRCarouselView.m:
--------------------------------------------------------------------------------
1 | //
2 | // SRCarouselView.m
3 | // SRCarouselView
4 | //
5 | // Created by https://github.com/guowilling on 17/1/10.
6 | // Copyright © 2017年 SR. All rights reserved.
7 | //
8 |
9 | #import "SRCarouselView.h"
10 |
11 | @interface SRCarouselImageManager : NSObject
12 |
13 | @property (nonatomic, assign) NSUInteger repeatCountWhenDownloadFailed;
14 |
15 | @property (nonatomic, copy) void(^downloadImageSuccess)(UIImage *image, NSInteger imageIndex);
16 |
17 | @property (nonatomic, copy) void(^downloadImageFailure)(NSError *error, NSString *imageURLString);
18 |
19 | - (void)downloadImage:(NSString *)imageURLString index:(NSInteger)imageIndex;
20 |
21 | + (void)clearCachedImages;
22 |
23 | @end
24 |
25 | #pragma mark -
26 |
27 | @interface SRCarouselView ()
28 |
29 | @property (nonatomic, weak) id delegate;
30 | @property (nonatomic, copy) SRDidTapCarouselViewAtIndexBlock block;
31 |
32 | @property (nonatomic, strong) SRCarouselImageManager *imageManager;
33 | @property (nonatomic, strong) NSMutableArray *images;
34 |
35 | @property (nonatomic, strong) NSArray *imageArray;
36 | @property (nonatomic, strong) NSArray *describeArray;
37 | @property (nonatomic, strong) UIImage *placeholderImage;
38 |
39 | @property (nonatomic, strong) UIScrollView *scrollView;
40 | @property (nonatomic, strong) UIImageView *currentImageView;
41 | @property (nonatomic, strong) UIImageView *nextImageView;
42 | @property (nonatomic, strong) UIView *bottomContainer;
43 | @property (nonatomic, strong) UIPageControl *pageControl;
44 | @property (nonatomic, strong) UILabel *descLabel;
45 |
46 | @property (nonatomic, assign) NSInteger currentIndex;
47 | @property (nonatomic, assign) NSInteger nextIndex;
48 |
49 | @property (nonatomic, strong) NSTimer *autoPagingTimer;
50 |
51 | @end
52 |
53 | @implementation SRCarouselView
54 |
55 | #pragma mark - Overriding
56 |
57 | - (void)dealloc {
58 | [self stopAutoPagingTimer];
59 | }
60 |
61 | #pragma mark - Lazy Load
62 |
63 | - (SRCarouselImageManager *)imageManager {
64 | if (!_imageManager) {
65 | __weak typeof(self) weakSelf = self;
66 | _imageManager = [[SRCarouselImageManager alloc] init];
67 | _imageManager.downloadImageSuccess = ^(UIImage *image, NSInteger imageIndex) {
68 | weakSelf.images[imageIndex] = image;
69 | if (weakSelf.currentIndex == imageIndex) {
70 | weakSelf.currentImageView.image = image;
71 | }
72 | };
73 | _imageManager.downloadImageFailure = ^(NSError *error, NSString *imageURLString) {
74 | NSLog(@"downloadImageFailure error: %@", error);
75 | };
76 | }
77 | return _imageManager;
78 | }
79 |
80 | - (UILabel *)descLabel {
81 | if (!_descLabel) {
82 | _descLabel = [[UILabel alloc] init];
83 | _descLabel.textColor = [UIColor whiteColor];
84 | _descLabel.font = [UIFont systemFontOfSize:15];
85 | _descLabel.textAlignment = NSTextAlignmentLeft;
86 | [self addSubview:_descLabel];
87 | }
88 | return _descLabel;
89 | }
90 |
91 | #pragma mark - Init Methods
92 |
93 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary {
94 | return [self sr_carouselViewWithImageArrary:imageArrary describeArray:nil placeholderImage:nil delegate:nil];
95 | }
96 |
97 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray {
98 | return [self sr_carouselViewWithImageArrary:imageArrary describeArray:describeArray placeholderImage:nil delegate:nil];
99 | }
100 |
101 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary describeArray:(NSArray *)describeArray placeholderImage:(UIImage *)placeholderImage {
102 | return [self sr_carouselViewWithImageArrary:imageArrary describeArray:describeArray placeholderImage:placeholderImage delegate:nil];
103 | }
104 |
105 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary
106 | describeArray:(NSArray *)describeArray
107 | placeholderImage:(UIImage *)placeholderImage
108 | delegate:(id)delegate
109 | {
110 | return [[self alloc] initWithImageArrary:imageArrary
111 | describeArray:describeArray
112 | placeholderImage:placeholderImage
113 | delegate:delegate];
114 | }
115 |
116 | - (instancetype)initWithImageArrary:(NSArray *)imageArrary
117 | describeArray:(NSArray *)describeArray
118 | placeholderImage:(UIImage *)placeholderImage
119 | delegate:(id)delegate
120 | {
121 | if (self = [super init]) {
122 | _imageArray = imageArrary;
123 | _describeArray = describeArray;
124 | _placeholderImage = placeholderImage;
125 |
126 | _delegate = delegate;
127 |
128 | _images = [NSMutableArray array];
129 |
130 | _currentIndex = 0;
131 | _nextIndex = 0;
132 |
133 | [self setup];
134 | [self startAutoPagingTimer];
135 | }
136 | return self;
137 | }
138 |
139 | + (instancetype)sr_carouselViewWithImageArrary:(NSArray *)imageArrary
140 | describeArray:(NSArray *)describeArray
141 | placeholderImage:(UIImage *)placeholderImage
142 | block:(SRDidTapCarouselViewAtIndexBlock)block
143 | {
144 | return [[self alloc] initWithImageArrary:imageArrary
145 | describeArray:describeArray
146 | placeholderImage:placeholderImage
147 | block:block];
148 | }
149 |
150 | - (instancetype)initWithImageArrary:(NSArray *)imageArrary
151 | describeArray:(NSArray *)describeArray
152 | placeholderImage:(UIImage *)placeholderImage
153 | block:(SRDidTapCarouselViewAtIndexBlock)block
154 | {
155 | if (self = [super init]) {
156 | _imageArray = imageArrary;
157 | _describeArray = describeArray;
158 | _placeholderImage = placeholderImage;
159 |
160 | _block = block;
161 |
162 | _images = [NSMutableArray array];
163 |
164 | _currentIndex = 0;
165 | _nextIndex = 0;
166 |
167 | [self setup];
168 | [self startAutoPagingTimer];
169 | }
170 | return self;
171 | }
172 |
173 | #pragma mark - Setup UI
174 |
175 | - (void)setup {
176 | if (_imageArray.count == 0) {
177 | return;
178 | }
179 | [self setupSubviews];
180 | [self setupImages];
181 | [self setupImageDescribes];
182 | }
183 |
184 | - (void)setupSubviews {
185 | _scrollView = [[UIScrollView alloc] init];
186 | _scrollView.delegate = self;
187 | _scrollView.pagingEnabled = YES;
188 | _scrollView.bounces = NO;
189 | _scrollView.showsHorizontalScrollIndicator = NO;
190 | _scrollView.showsVerticalScrollIndicator = NO;
191 | [self addSubview:_scrollView];
192 |
193 | _pageControl = [[UIPageControl alloc] init];
194 | _pageControl.hidesForSinglePage = YES;
195 | _pageControl.userInteractionEnabled = NO;
196 | _pageControl.numberOfPages = _imageArray.count;
197 | _pageControl.currentPage = 0;
198 | [self addSubview:_pageControl];
199 |
200 | _currentImageView = [[UIImageView alloc] init];
201 | _currentImageView.contentMode = UIViewContentModeScaleAspectFill;
202 | _currentImageView.layer.masksToBounds = YES;
203 | [_scrollView addSubview:_currentImageView];
204 |
205 | _nextImageView = [[UIImageView alloc] init];
206 | _nextImageView.contentMode = UIViewContentModeScaleAspectFill;
207 | _nextImageView.layer.masksToBounds = YES;
208 | [_scrollView addSubview:_nextImageView];
209 |
210 | _currentImageView.userInteractionEnabled = YES;
211 | [_currentImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapCurrentImageView)]];
212 | }
213 |
214 | - (void)setupImages {
215 | for (int i = 0; i < _imageArray.count; i++) {
216 | if ([_imageArray[i] isKindOfClass:[UIImage class]]) { // local image
217 | [self.images addObject:_imageArray[i]];
218 | }
219 | if ([_imageArray[i] isKindOfClass:[NSString class]]) { // internet image
220 | if (_placeholderImage) { // hold placeholder image if setted
221 | [self.images addObject:_placeholderImage];
222 | } else { // use NSNull object replace if not setted
223 | [self.images addObject:[NSNull null]];
224 | }
225 | [self.imageManager downloadImage:self.imageArray[i] index:i]; // use SRCarouselImageManager to download image
226 | }
227 | }
228 |
229 | if ([self.images[0] isKindOfClass:[NSNull class]]) {
230 | _currentImageView.image = nil;
231 | } else { // show first image or placeholder image if exists
232 | _currentImageView.image = self.images[0];
233 | }
234 | }
235 |
236 | - (void)setupImageDescribes {
237 | if (_describeArray && _describeArray.count > 0) {
238 | if (_describeArray.count < self.images.count) {
239 | NSMutableArray *arrayM = [NSMutableArray arrayWithArray:_describeArray];
240 | for (NSInteger i = _describeArray.count; i < self.images.count; i++) {
241 | [arrayM addObject:@""];
242 | }
243 | _describeArray = arrayM;
244 | }
245 | _bottomContainer = [[UIView alloc] init];
246 | _bottomContainer.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
247 | [self addSubview:_bottomContainer];
248 | [_bottomContainer addSubview:self.descLabel];
249 |
250 | self.descLabel.text = _describeArray[0];
251 |
252 | [self bringSubviewToFront:_pageControl];
253 | }
254 | }
255 |
256 | - (void)layoutSubviews {
257 | [super layoutSubviews];
258 |
259 | _scrollView.frame = self.bounds;
260 | _scrollView.contentInset = UIEdgeInsetsZero;
261 |
262 | CGFloat width = _scrollView.frame.size.width;
263 | CGFloat height = _scrollView.frame.size.height;
264 |
265 | if (self.images.count > 1) {
266 | _scrollView.contentSize = CGSizeMake(width * 3, 0);
267 | _scrollView.contentOffset = CGPointMake(width, 0);
268 | _currentImageView.frame = CGRectMake(width, 0, width, height);
269 | } else {
270 | _scrollView.contentSize = CGSizeZero;
271 | _scrollView.contentOffset = CGPointMake(0, 0);
272 | _currentImageView.frame = CGRectMake(0, 0, width, height);
273 | }
274 |
275 | CGFloat bottomContainerHeight = 25;
276 | CGFloat pageControlDotWidth = 15;
277 | if (!_describeArray || _describeArray.count == 0) {
278 | _pageControl.frame = CGRectMake(width * 0.5 - _pageControl.numberOfPages * pageControlDotWidth * 0.5,
279 | height - bottomContainerHeight,
280 | _pageControl.numberOfPages * pageControlDotWidth,
281 | bottomContainerHeight);
282 | } else {
283 | _bottomContainer.frame = CGRectMake(0, height - bottomContainerHeight, width, bottomContainerHeight);
284 | _pageControl.frame = CGRectMake(width - _pageControl.numberOfPages * pageControlDotWidth - 5,
285 | height - bottomContainerHeight,
286 | _pageControl.numberOfPages * pageControlDotWidth,
287 | bottomContainerHeight);
288 | _descLabel.frame = CGRectMake(5, 0, width - 10, bottomContainerHeight);
289 | }
290 | }
291 |
292 | #pragma mark - Timer
293 |
294 | - (void)startAutoPagingTimer {
295 | if (self.images.count <= 1) {
296 | return;
297 | }
298 | if (_autoPagingTimer) {
299 | [self stopAutoPagingTimer];
300 | }
301 | _autoPagingTimer = [NSTimer timerWithTimeInterval:_autoPagingInterval == 0 ? 5.0 : _autoPagingInterval
302 | target:self
303 | selector:@selector(nextPage)
304 | userInfo:nil
305 | repeats:YES];
306 | [[NSRunLoop currentRunLoop] addTimer:_autoPagingTimer forMode:NSRunLoopCommonModes];
307 | }
308 |
309 | - (void)stopAutoPagingTimer {
310 | if (_autoPagingTimer) {
311 | [_autoPagingTimer invalidate];
312 | _autoPagingTimer = nil;
313 | }
314 | }
315 |
316 | #pragma mark - Actions
317 |
318 | - (void)nextPage {
319 | CGFloat width = _scrollView.frame.size.width;
320 | [_scrollView setContentOffset:CGPointMake(width * 2, 0) animated:YES];
321 | }
322 |
323 | - (void)didTapCurrentImageView {
324 | if (self.block) {
325 | self.block(self.currentIndex);
326 | }
327 | if ([self.delegate respondsToSelector:@selector(carouselViewDidTapCarouselViewAtIndex:)]) {
328 | [self.delegate carouselViewDidTapCarouselViewAtIndex:self.currentIndex];
329 | }
330 | }
331 |
332 | #pragma mark - UIScrollViewDelegate
333 |
334 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
335 | CGFloat width = _scrollView.frame.size.width;
336 | CGFloat height = _scrollView.frame.size.height;
337 |
338 | CGFloat offsetX = scrollView.contentOffset.x;
339 | if (offsetX == width) {
340 | return;
341 | }
342 |
343 | if (offsetX > width) {
344 | _nextImageView.frame = CGRectMake(CGRectGetMaxX(_currentImageView.frame), 0, width, height);
345 | _nextIndex = _currentIndex + 1;
346 | if (_nextIndex == self.images.count) {
347 | _nextIndex = 0;
348 | }
349 | }
350 |
351 | if (offsetX < width) {
352 | _nextImageView.frame = CGRectMake(0, 0, width, height);
353 | _nextIndex = _currentIndex - 1;
354 | if (_nextIndex < 0) {
355 | _nextIndex = self.images.count - 1;
356 | }
357 | }
358 |
359 | if ([self.images[_nextIndex] isKindOfClass:[NSNull class]]) {
360 | _nextImageView.image = nil;
361 | } else {
362 | _nextImageView.image = self.images[_nextIndex];
363 | }
364 | }
365 |
366 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
367 | [self stopAutoPagingTimer]; // stop timer when dragging scrollview manually
368 | }
369 |
370 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
371 | [self startAutoPagingTimer]; // start timer when stop dragging scrollview manually
372 | }
373 |
374 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
375 | [self updateContent]; // update content when paging finishes manually
376 | }
377 |
378 | - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
379 | [self updateContent]; // update content when paging finishes automatically
380 | }
381 |
382 | - (void)updateContent {
383 | CGFloat width = _scrollView.frame.size.width;
384 |
385 | if (_scrollView.contentOffset.x == width) { // if paging not finished do not update content
386 | return;
387 | }
388 |
389 | _currentIndex = _nextIndex;
390 | _currentImageView.image = _nextImageView.image;
391 | _pageControl.currentPage = _currentIndex;
392 | if (self.describeArray.count > 0) {
393 | self.descLabel.text = self.describeArray[_currentIndex];
394 | }
395 |
396 | [_scrollView setContentOffset:CGPointMake(width, 0) animated:NO];
397 | }
398 |
399 | #pragma mark - Public Methods
400 |
401 | - (void)setCurrentPageIndicatorTintColor:(UIColor *)currentPageIndicatorTintColor {
402 | if (_currentPageIndicatorTintColor != currentPageIndicatorTintColor) {
403 | _currentPageIndicatorTintColor = currentPageIndicatorTintColor;
404 | _pageControl.currentPageIndicatorTintColor = currentPageIndicatorTintColor;
405 | }
406 | }
407 |
408 | - (void)setPageIndicatorTintColor:(UIColor *)pageIndicatorTintColor {
409 | if (_pageIndicatorTintColor != pageIndicatorTintColor) {
410 | _pageIndicatorTintColor = pageIndicatorTintColor;
411 | _pageControl.pageIndicatorTintColor = pageIndicatorTintColor;
412 | }
413 | }
414 |
415 | - (void)setCurrentPageIndicatorImage:(UIImage *)currentPageIndicatorImage {
416 | if (_currentPageIndicatorImage != currentPageIndicatorImage) {
417 | _currentPageIndicatorImage = currentPageIndicatorImage;
418 | [_pageControl setValue:currentPageIndicatorImage forKey:@"currentPageImage"];
419 | }
420 | }
421 |
422 | - (void)setPageIndicatorImage:(UIImage *)pageIndicatorImage {
423 | if (_pageIndicatorImage != pageIndicatorImage) {
424 | _pageIndicatorImage = pageIndicatorImage;
425 | [_pageControl setValue:pageIndicatorImage forKey:@"pageImage"];
426 | }
427 | }
428 |
429 | - (void)setAutoPagingInterval:(NSTimeInterval)autoPagingInterval {
430 | if (_autoPagingInterval != autoPagingInterval) {
431 | _autoPagingInterval = autoPagingInterval;
432 | [self startAutoPagingTimer];
433 | }
434 | }
435 |
436 | + (void)clearCachedImages {
437 | [SRCarouselImageManager clearCachedImages];
438 | }
439 |
440 | @end
441 |
442 | #define SRImagesDirectory [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] \
443 | stringByAppendingPathComponent:NSStringFromClass([self class])]
444 |
445 | //#define SRImageName(URLString) [URLString lastPathComponent]
446 |
447 | //#define SRImagePath(URLString) [SRImagesDirectory stringByAppendingPathComponent:SRImageName(URLString)]
448 |
449 | @interface SRCarouselImageManager ()
450 |
451 | @property (nonatomic, strong) NSMutableDictionary *redownloadManager;
452 |
453 | @end
454 |
455 | @implementation SRCarouselImageManager
456 |
457 | + (void)load {
458 | NSString *imagesDirectory = SRImagesDirectory;
459 | BOOL isDirectory = NO;
460 | NSFileManager *fileManager = [NSFileManager defaultManager];
461 | BOOL isExists = [fileManager fileExistsAtPath:imagesDirectory isDirectory:&isDirectory];
462 | if (!isExists || !isDirectory) {
463 | [fileManager createDirectoryAtPath:imagesDirectory withIntermediateDirectories:YES attributes:nil error:nil];
464 | }
465 | }
466 |
467 | - (NSString *)imagePath:(NSString *)URLString {
468 | NSString *imageName = nil;
469 | NSString *query = [NSURL URLWithString:URLString].query;
470 | if (query) {
471 | // https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?x-oss-process=image/format,png
472 | // https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg?
473 | // https://yixunfiles-ali.yixun.arhieason.com/9535a537ad2538d53ec1a351deff3856_jpg.jpg
474 | // 9535a537ad2538d53ec1a351deff3856_jpg.jpg
475 | imageName = [URLString stringByReplacingOccurrencesOfString:query withString:@""];
476 | imageName = [imageName stringByReplacingOccurrencesOfString:@"?" withString:@""];
477 | }
478 | imageName = imageName.lastPathComponent;
479 | return [SRImagesDirectory stringByAppendingPathComponent:imageName];
480 | }
481 |
482 | - (NSMutableDictionary *)redownloadManager {
483 | if (!_redownloadManager) {
484 | _redownloadManager = [NSMutableDictionary dictionary];
485 | }
486 | return _redownloadManager;
487 | }
488 |
489 | - (instancetype)init {
490 | if (self = [super init]) {
491 | _repeatCountWhenDownloadFailed = 2;
492 | }
493 | return self;
494 | }
495 |
496 | - (UIImage *)imageFromSandbox:(NSString *)imageURLString {
497 | NSString *imagePath = [self imagePath:imageURLString];
498 | NSData *data = [NSData dataWithContentsOfFile:imagePath];
499 | if (data.length > 0 ) {
500 | return [UIImage imageWithData:data];
501 | } else {
502 | [[NSFileManager defaultManager] removeItemAtPath:imagePath error:NULL];
503 | }
504 | return nil;
505 | }
506 |
507 | - (void)downloadImage:(NSString *)imageURLString index:(NSInteger)imageIndex {
508 | UIImage *image = [self imageFromSandbox:imageURLString];
509 | if (image) {
510 | if (self.downloadImageSuccess) {
511 | self.downloadImageSuccess(image, imageIndex);
512 | }
513 | return;
514 | }
515 | [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:imageURLString]
516 | completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
517 | dispatch_async(dispatch_get_main_queue(), ^{
518 | if (error) {
519 | [self redownloadImage:imageURLString index:imageIndex error:error];
520 | return;
521 | }
522 | UIImage *image = [UIImage imageWithData:data];
523 | if (!image) {
524 | return;
525 | }
526 | if (self.downloadImageSuccess) {
527 | self.downloadImageSuccess(image, imageIndex);
528 | }
529 | if (![data writeToFile:[self imagePath:imageURLString] atomically:YES]) {
530 | NSLog(@"writeToFile Failed!");
531 | }
532 | });
533 | }] resume];
534 | }
535 |
536 | - (void)redownloadImage:(NSString *)imageURLString index:(NSInteger)imageIndex error:(NSError *)error {
537 | NSNumber *redownloadNumber = self.redownloadManager[imageURLString];
538 | NSInteger redownloadTimes = redownloadNumber ? redownloadNumber.integerValue : 0;
539 | if (self.repeatCountWhenDownloadFailed > redownloadTimes) {
540 | self.redownloadManager[imageURLString] = @(++redownloadTimes);
541 | [self downloadImage:imageURLString index:imageIndex];
542 | return;
543 | }
544 | if (self.downloadImageFailure) {
545 | self.downloadImageFailure(error, imageURLString);
546 | }
547 | }
548 |
549 | + (void)clearCachedImages {
550 | NSFileManager *fileManager = [NSFileManager defaultManager];
551 | NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:SRImagesDirectory error:nil];
552 | for (NSString *fileName in fileNames) {
553 | if (![fileManager removeItemAtPath:[SRImagesDirectory stringByAppendingPathComponent:fileName] error:nil]) {
554 | NSLog(@"removeItemAtPath Failed!");
555 | }
556 | }
557 | }
558 |
559 | @end
560 |
--------------------------------------------------------------------------------