#import @class XRCarouselView;typedef void(^ClickBlock)(NSInteger index);//pageControl的顯示位置typedef enum {? ? PositionNone,? ? ? ? ? //默認(rèn)值 == PositionBottomCenter? ? PositionHide,? ? ? ? ? //隱藏? ? PositionTopCenter,? ? ? //中上? ? PositionBottomLeft,? ? //左下? ? PositionBottomCenter,? //中下? ? PositionBottomRight? ? //右下} PageControlPosition;@protocol XRCarouselViewDelegate/** *? 該方法用來處理圖片的點(diǎn)擊翅雏,會(huì)返回圖片在數(shù)組中的索引 *? 代理與block二選一即可撒穷,若兩者都實(shí)現(xiàn)煤痕,block的優(yōu)先級(jí)高 * *? @param carouselView 控件本身 *? @param index? ? ? ? 圖片索引 */- (void)carouselView:(XRCarouselView *)carouselView didClickImage:(NSInteger)index;@end/** *? 說明:要想正常使用虽惭,圖片數(shù)組imageArray必須設(shè)置 *? 控件的frame必須設(shè)置橡类,xib\sb創(chuàng)建的可不設(shè)置 *? 其他屬性都有默認(rèn)值,可不設(shè)置 */@interface XRCarouselView : UIView/* 這里沒有提供修改占位圖片的接口芽唇,如果需要修改顾画,可直接到.m文件中 修改占位圖片名稱為你想要顯示圖片的名稱,或者將你想要顯示圖片的 名稱修改為placeholder匆笤,因?yàn)闆]實(shí)際意義研侣,所以就不提供接口了 */#pragma mark 屬性/** *? 設(shè)置分頁(yè)控件位置,默認(rèn)為PositionBottomCenter */@property (nonatomic, assign) PageControlPosition pagePosition;/** *? 輪播的圖片數(shù)組炮捧,可以是圖片庶诡,也可以是網(wǎng)絡(luò)路徑 */@property (nonatomic, strong) NSArray *imageArray;/** *? 圖片描述的字符串?dāng)?shù)組,應(yīng)與圖片順序?qū)?yīng) * *? 圖片描述控件默認(rèn)是隱藏的 *? 設(shè)置該屬性后咆课,會(huì)取消隱藏末誓,顯示在圖片底部 */@property (nonatomic, strong) NSArray *describeArray;/** *? 圖片描述控件的背景顏色,默認(rèn)為黑色半透明 */@property (nonatomic, strong) UIColor *desLabelBgColor;/** *? 圖片描述控件的字體书蚪,默認(rèn)為13號(hào)字體 */@property (nonatomic, strong) UIFont *desLabelFont;/** *? 圖片描述控件的文字顏色喇澡,默認(rèn)為白色 */@property (nonatomic, strong) UIColor *desLabelColor;/** *? 每一頁(yè)停留時(shí)間,默認(rèn)為5s殊校,最少1s *? 當(dāng)設(shè)置的值小于1s時(shí)晴玖,則為默認(rèn)值 */@property (nonatomic, assign) NSTimeInterval time;/** *? 點(diǎn)擊圖片后要執(zhí)行的操作,會(huì)返回圖片在數(shù)組中的索引 */@property (nonatomic, copy) ClickBlock imageClickBlock;/** *? 代理,用來處理圖片的點(diǎn)擊 */@property (nonatomic, weak) iddelegate;
#pragma mark 構(gòu)造方法
/**
*? 構(gòu)造方法
*
*? @param imageArray 圖片數(shù)組
*? @param describeArray 圖片描述數(shù)組
*
*/
- (instancetype)initWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray;
+ (instancetype)carouselViewWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray;
- (instancetype)initWithImageArray:(NSArray *)imageArray describeArray:(NSArray *)describeArray;
+ (instancetype)carouselViewWithImageArray:(NSArray *)imageArray describeArray:(NSArray *)describeArray;
#pragma mark 方法
/**
*? 開啟定時(shí)器
*? 默認(rèn)已開啟呕屎,調(diào)用該方法會(huì)重新開啟
*/
- (void)startTimer;
/**
*? 停止定時(shí)器
*? 停止后让簿,如果手動(dòng)滾動(dòng)圖片,定時(shí)器會(huì)重新開啟
*/
- (void)stopTimer;
/**
*? 設(shè)置分頁(yè)控件指示器的圖片
*? 兩個(gè)圖片都不能為空秀睛,否則設(shè)置無效
*? 不設(shè)置則為系統(tǒng)默認(rèn)
*
*? @param pageImage? ? 其他頁(yè)碼的圖片
*? @param currentImage 當(dāng)前頁(yè)碼的圖片
*/
- (void)setPageImage:(UIImage *)pageImage andCurrentImage:(UIImage *)currentImage;
/**
*? 設(shè)置分頁(yè)控件指示器的顏色
*? 不設(shè)置則為系統(tǒng)默認(rèn)
*
*? @param color? ? 其他頁(yè)碼的顏色
*? @param currentColor 當(dāng)前頁(yè)碼的顏色
*/
- (void)setPageColor:(UIColor *)color andCurrentPageColor:(UIColor *)currentColor;
/**
*? 清除沙盒中的圖片緩存
*/
- (void)clearDiskCache;
@end
#import "XRCarouselView.h"#define DEFAULTTIME 3#define Margin 10typedef enum{? ? DirecNone,? ? DirecLeft,? ? DirecRight} Direction;@interface XRCarouselView()//輪播的圖片數(shù)組
@property (nonatomic, strong) NSMutableArray *images;
//下載的圖片字典
@property (nonatomic, strong) NSMutableDictionary *imageDic;
//下載圖片的操作
@property (nonatomic, strong) NSMutableDictionary *operationDic;
//滾動(dòng)方向
@property (nonatomic, assign) Direction direction;
//圖片描述控件尔当,默認(rèn)在底部
@property (nonatomic, strong) UILabel *describeLabel;
//分頁(yè)控件
@property (nonatomic, strong) UIPageControl *pageControl;
//顯示的imageView
@property (nonatomic, strong) UIImageView *currImageView;
//輔助滾動(dòng)的imageView
@property (nonatomic, strong) UIImageView *otherImageView;
//當(dāng)前顯示圖片的索引
@property (nonatomic, assign) NSInteger currIndex;
//將要顯示圖片的索引
@property (nonatomic, assign) NSInteger nextIndex;
//滾動(dòng)視圖
@property (nonatomic, strong) UIScrollView *scrollView;
//pageControl圖片大小
@property (nonatomic, assign) CGSize pageImageSize;
//定時(shí)器
@property (nonatomic, strong) NSTimer *timer;
//任務(wù)隊(duì)列
@property (nonatomic, strong) NSOperationQueue *queue;
@end
@implementation XRCarouselView
#pragma mark- 初始化方法
//創(chuàng)建用來緩存圖片的文件夾
+ (void)initialize {
NSString *cache = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"XRCarousel"];
BOOL isDir = NO;
BOOL isExists = [[NSFileManager defaultManager] fileExistsAtPath:cache isDirectory:&isDir];
if (!isExists || !isDir) {
[[NSFileManager defaultManager] createDirectoryAtPath:cache withIntermediateDirectories:YES attributes:nil error:nil];
}
}
#pragma mark- frame相關(guān)
- (CGFloat)height {
return self.scrollView.frame.size.height;
}
- (CGFloat)width {
return self.scrollView.frame.size.width;
}
#pragma mark- 懶加載
- (NSMutableDictionary *)imageDic{
if (!_imageDic) {
_imageDic = [NSMutableDictionary dictionary];
}
return _imageDic;
}
- (NSMutableDictionary *)operationDic{
if (!_operationDic) {
_operationDic = [NSMutableDictionary dictionary];
}
return? _operationDic;
}
- (NSOperationQueue *)queue {
if (!_queue) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}
- (UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] init];
_scrollView.pagingEnabled = YES;
_scrollView.bounces = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.delegate = self;
_currImageView = [[UIImageView alloc] init];
_currImageView.userInteractionEnabled = YES;
[_currImageView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageClick)]];
[_scrollView addSubview:_currImageView];
_otherImageView = [[UIImageView alloc] init];
[_scrollView addSubview:_otherImageView];
}
return _scrollView;
}
- (UILabel *)describeLabel {
if (!_describeLabel) {
_describeLabel = [[UILabel alloc] init];
_describeLabel.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
_describeLabel.textColor = [UIColor whiteColor];
_describeLabel.textAlignment = NSTextAlignmentCenter;
_describeLabel.font = [UIFont systemFontOfSize:13];
_describeLabel.hidden = YES;
}
return _describeLabel;
}
- (UIPageControl *)pageControl {
if (!_pageControl) {
_pageControl = [[UIPageControl alloc] init];
_pageControl.hidesForSinglePage = YES;
_pageControl.userInteractionEnabled = NO;
_pageControl.pageIndicatorTintColor = [UIColor colorWithRed:255/255.0 green:255/255.0 blue:255/255.0 alpha:0.5];
_pageControl.currentPageIndicatorTintColor = [UIColor redColor];
}
return _pageControl;
}
#pragma mark- 構(gòu)造方法
- (instancetype)initWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray {
if (self = [super initWithFrame:frame]) {
self.imageArray = imageArray;
}
return self;
}
+ (instancetype)carouselViewWithFrame:(CGRect)frame imageArray:(NSArray *)imageArray {
return [[self alloc] initWithFrame:frame imageArray:imageArray];
}
- (instancetype)initWithImageArray:(NSArray *)imageArray describeArray:(NSArray *)describeArray {
if (self = [self initWithFrame:CGRectZero imageArray:imageArray]) {
self.describeArray = describeArray;
}
return self;
}
+ (instancetype)carouselViewWithImageArray:(NSArray *)imageArray describeArray:(NSArray *)describeArray {
return [[self alloc] initWithImageArray:imageArray describeArray:describeArray];
}
#pragma mark- --------設(shè)置相關(guān)方法--------
#pragma mark 設(shè)置控件的frame,并添加子控件
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self addSubview:self.scrollView];
[self addSubview:self.describeLabel];
[self addSubview:self.pageControl];
}
#pragma mark 設(shè)置滾動(dòng)方向
- (void)setDirection:(Direction)direction {
if (_direction == direction) return;
_direction = direction;
if (direction == DirecNone) return;
if (direction == DirecRight) {
self.otherImageView.frame = CGRectMake(0, 0, self.width, self.height);
self.nextIndex = self.currIndex - 1;
if (self.nextIndex < 0) self.nextIndex = _images.count - 1;
} else if (direction == DirecLeft){
self.otherImageView.frame = CGRectMake(CGRectGetMaxX(_currImageView.frame), 0, self.width, self.height);
self.nextIndex = (self.currIndex + 1) % _images.count;
}
self.otherImageView.image = self.images[self.nextIndex];
}
#pragma mark 設(shè)置圖片數(shù)組
- (void)setImageArray:(NSArray *)imageArray{
if (!imageArray.count) return;
_imageArray = imageArray;
_images = [NSMutableArray array];
for (int i = 0; i < imageArray.count; i++) {
if ([imageArray[i] isKindOfClass:[UIImage class]]) {
[_images addObject:imageArray[i]];
} else if ([imageArray[i] isKindOfClass:[NSString class]]){
[_images addObject:[UIImage imageNamed:@"placeholder"]];
[self downloadImages:i];
}
}
self.currImageView.image = _images.firstObject;
self.pageControl.numberOfPages = _images.count;
}
#pragma mark 設(shè)置描述數(shù)組
- (void)setDescribeArray:(NSArray *)describeArray{
_describeArray = describeArray;
//如果描述的個(gè)數(shù)與圖片個(gè)數(shù)不一致琅催,則補(bǔ)空字符串
if (describeArray && describeArray.count > 0) {
if (describeArray.count < _images.count) {
NSMutableArray *describes = [NSMutableArray arrayWithArray:describeArray];
for (NSInteger i = describeArray.count; i < _images.count; i++) {
[describes addObject:@""];
}
_describeArray = describes;
}
self.describeLabel.hidden = NO;
_describeLabel.text = _describeArray.firstObject;
}
}
#pragma mark 設(shè)置scrollView的contentSize
- (void)setScrollViewContentSize {
if (_images.count > 1) {
self.scrollView.contentSize = CGSizeMake(self.width * 3, 0);
self.scrollView.contentOffset = CGPointMake(self.width, 0);
self.currImageView.frame = CGRectMake(self.width, 0, self.width, self.height);
[self startTimer];
} else {
self.scrollView.contentSize = CGSizeZero;
self.scrollView.contentOffset = CGPointZero;
self.currImageView.frame = CGRectMake(0, 0, self.width, self.height);
}
}
#pragma mark 設(shè)置圖片描述控件
//設(shè)置背景顏色
- (void)setDesLabelBgColor:(UIColor *)desLabelBgColor {
_desLabelBgColor = desLabelBgColor;
self.describeLabel.backgroundColor = desLabelBgColor;
}
//設(shè)置字體
- (void)setDesLabelFont:(UIFont *)desLabelFont {
_desLabelFont = desLabelFont;
self.describeLabel.font = desLabelFont;
}
//設(shè)置文字顏色
- (void)setDesLabelColor:(UIColor *)desLabelColor {
_desLabelColor = desLabelColor;
self.describeLabel.textColor = desLabelColor;
}
#pragma mark 設(shè)置pageControl的指示器圖片
- (void)setPageImage:(UIImage *)pageImage andCurrentImage:(UIImage *)currentImage {
if (!pageImage || !currentImage) return;
self.pageImageSize = pageImage.size;
[self.pageControl setValue:currentImage forKey:@"_currentPageImage"];
[self.pageControl setValue:pageImage forKey:@"_pageImage"];
}
#pragma mark 設(shè)置pageControl的指示器顏色
- (void)setPageColor:(UIColor *)color andCurrentPageColor:(UIColor *)currentColor {
_pageControl.pageIndicatorTintColor = color;
//? 設(shè)置當(dāng)前頁(yè)碼指示器的顏色
_pageControl.currentPageIndicatorTintColor = currentColor;
}
#pragma mark 設(shè)置pageControl的位置
- (void)setPageControlPosition {
if (_pagePosition == PositionHide) {
_pageControl.hidden = YES;
return;
}
CGSize size;
if (_pageImageSize.width == 0) {//沒有設(shè)置圖片
size = [_pageControl sizeForNumberOfPages:_pageControl.numberOfPages];
size.height = 20;
} else {//設(shè)置圖片了
size = CGSizeMake(_pageImageSize.width * (_pageControl.numberOfPages * 2 - 1), _pageImageSize.height);
}
_pageControl.frame = CGRectMake(0, 0, size.width, size.height);
if (_pagePosition == PositionNone || _pagePosition == PositionBottomCenter)
_pageControl.center = CGPointMake(self.width * 0.5, self.height - (_describeLabel.hidden? 10 : 30));
else if (_pagePosition == PositionTopCenter)
_pageControl.center = CGPointMake(self.width * 0.5, size.height * 0.5);
else if (_pagePosition == PositionBottomLeft)
_pageControl.frame = CGRectMake(Margin, self.height - (_describeLabel.hidden? size.height : size.height + 20), size.width, size.height);
else
_pageControl.frame = CGRectMake(self.width - Margin - size.width, self.height - (_describeLabel.hidden? size.height : size.height + 20), size.width, size.height);
}
#pragma mark 設(shè)置定時(shí)器時(shí)間
- (void)setTime:(NSTimeInterval)time {
_time = time;
[self startTimer];
}
#pragma mark- --------定時(shí)器相關(guān)方法--------
- (void)startTimer {
//如果只有一張圖片居凶,則直接返回,不開啟定時(shí)器
if (_images.count <= 1) return;
//如果定時(shí)器已開啟藤抡,先停止再重新開啟
if (self.timer) [self stopTimer];
self.timer = [NSTimer timerWithTimeInterval:_time < 1? DEFAULTTIME : _time target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)stopTimer {
[self.timer invalidate];
self.timer = nil;
}
- (void)nextPage {
[self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
}
#pragma mark- -----------其它-----------
#pragma mark 布局子控件
- (void)layoutSubviews {
[super layoutSubviews];
//有導(dǎo)航控制器時(shí)侠碧,會(huì)默認(rèn)在scrollview上方添加64的內(nèi)邊距,這里強(qiáng)制設(shè)置為0
_scrollView.contentInset = UIEdgeInsetsZero;
_scrollView.frame = self.bounds;
_describeLabel.frame = CGRectMake(0, self.height - 20, self.width, 20);
[self setPageControlPosition];
[self setScrollViewContentSize];
}
#pragma mark 圖片點(diǎn)擊事件
- (void)imageClick {
if (self.imageClickBlock) {
self.imageClickBlock(self.currIndex);
} else if ([_delegate respondsToSelector:@selector(carouselView:didClickImage:)]){
[_delegate carouselView:self didClickImage:self.currIndex];
}
}
#pragma mark 下載網(wǎng)絡(luò)圖片
- (void)downloadImages:(int)index {
NSString *key = _imageArray[index];
//從內(nèi)存緩存中取圖片
UIImage *image = [self.imageDic objectForKey:key];
if (image) {
_images[index] = image;
return;
}
//從沙盒緩存中取圖片
NSString *cache = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"XRCarousel"];
NSString *path = [cache stringByAppendingPathComponent:[key lastPathComponent]];
NSData *data = [NSData dataWithContentsOfFile:path];
if (data) {
image = [UIImage imageWithData:data];
_images[index] = image;
[self.imageDic setObject:image forKey:key];
return;
}
//下載圖片
NSBlockOperation *download = [self.operationDic objectForKey:key];
if (download) return;
//創(chuàng)建一個(gè)操作
download = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:key];
NSData *data = [NSData dataWithContentsOfURL:url];
if (!data) return;
UIImage *image = [UIImage imageWithData:data];
//取到的data有可能不是圖片
if (image) {
[self.imageDic setObject:image forKey:key];
self.images[index] = image;
//如果下載的圖片為當(dāng)前要顯示的圖片缠黍,直接到主線程給imageView賦值弄兜,否則要等到下一輪才會(huì)顯示
if (_currIndex == index) [_currImageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
[data writeToFile:path atomically:YES];
}
[self.operationDic removeObjectForKey:key];
}];
[self.queue addOperation:download];
[self.operationDic setObject:download forKey:key];
}
#pragma mark 清除沙盒中的圖片緩存
- (void)clearDiskCache {
NSString *cache = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"XRCarousel"];
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:cache error:NULL];
for (NSString *fileName in contents) {
[[NSFileManager defaultManager] removeItemAtPath:[cache stringByAppendingPathComponent:fileName] error:nil];
}
}
#pragma mark 當(dāng)圖片滾動(dòng)過半時(shí)就修改當(dāng)前頁(yè)碼
- (void)changeCurrentPageWithOffset:(CGFloat)offsetX {
if (offsetX < self.width * 0.5) {
NSInteger index = self.currIndex - 1;
if (index < 0) index = self.images.count - 1;
_pageControl.currentPage = index;
} else if (offsetX > self.width * 1.5){
_pageControl.currentPage = (self.currIndex + 1) % self.images.count;
} else {
_pageControl.currentPage = self.currIndex;
}
}
#pragma mark- --------UIScrollViewDelegate--------
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetX = scrollView.contentOffset.x;
self.direction = offsetX > self.width? DirecLeft : offsetX < self.width? DirecRight : DirecNone;
[self changeCurrentPageWithOffset:offsetX];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self stopTimer];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
[self startTimer];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self pauseScroll];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self pauseScroll];
}
- (void)pauseScroll {
//等于1表示沒滾動(dòng)
if (self.scrollView.contentOffset.x / self.width == 1) return;
self.currIndex = self.nextIndex;
self.pageControl.currentPage = self.currIndex;
self.currImageView.frame = CGRectMake(self.width, 0, self.width, self.height);
self.describeLabel.text = self.describeArray[self.currIndex];
self.currImageView.image = self.otherImageView.image;
self.scrollView.contentOffset = CGPointMake(self.width, 0);
}
@end