這是利用人的視覺錯覺來實現無限輪播,UICollectionView 有很好的重用機制,這只是部分核心代碼,後期還要繼續完善和代碼重構。數組
#import <UIKit/UIKit.h> #import "ADPageControlView.h" @interface ADCarouselView : UIView /**圖片資源數組*/ @property (nonatomic, strong) NSArray *imgs; /**標題數組*/ @property (nonatomic, strong) NSArray *titles; /**是否無限循環輪播*/ @property (nonatomic, assign) BOOL loop; /**自動輪播時間間隔,默認爲0,0表示不開啓自動輪播*/ @property (nonatomic, assign) NSTimeInterval automaticallyScrollDuration; /**圖片的展開方式*/ @property (nonatomic, assign) UIViewContentMode imageContentMode; /**佔位圖片*/ @property (copy, nonatomic) NSString *placeholderImageName; + (instancetype)carouselViewWithFrame:(CGRect)frame; /**分頁控制(相關屬性自行設置,不設置使用默認屬性)*/ @property (strong, nonatomic) ADPageControlView *pageControlView; /**標題*/ @property (nonatomic, strong) UILabel *titleLabel; @end
#import "ADCarouselView.h" #define kADCarouselViewLeftMargin 10 #define kPageControlViewDefaultW 80 #define kPageControlViewDefaultH 44 #define kTitleLabelToTitleLabelMargin 10 #define kTitleLabelDefaultH kPageControlViewDefaultH #define kPageControlViewDefaultFrame CGRectMake([UIScreen mainScreen].bounds.size.width - kPageControlViewDefaultW - kADCarouselViewLeftMargin, self.bounds.size.height - kPageControlViewDefaultH, kPageControlViewDefaultW, kPageControlViewDefaultH) #define kTitleLabelDefaultFrame CGRectMake(kADCarouselViewLeftMargin, self.bounds.size.height - kTitleLabelDefaultH, [UIScreen mainScreen].bounds.size.width - kPageControlViewDefaultW - kADCarouselViewLeftMargin - kADCarouselViewLeftMargin - kTitleLabelToTitleLabelMargin, kTitleLabelDefaultH) #define kTitleLabelDefaultTextColor [UIColor whiteColor] #define kTitleLabelDefaultFont [UIFont systemFontOfSize:14] @class ADCarouselViewCell; #pragma mark - ADCarouselViewCell(輪播圖子控件) @interface ADCarouselViewCell : UICollectionViewCell /**圖片名稱*/ @property (copy, nonatomic) NSString *imgName; @end @interface ADCarouselViewCell() /**圖片*/ @property (weak, nonatomic) UIImageView *imgView; @end @implementation ADCarouselViewCell - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setUpCarouselViewCell]; } return self; } - (void)setUpCarouselViewCell { UIImageView *imgView = [[UIImageView alloc] init]; imgView.contentMode = UIViewContentModeCenter; self.imgView = imgView; [self.contentView addSubview:self.imgView]; self.backgroundColor = [UIColor whiteColor]; } - (void)setImgName:(NSString *)imgName { _imgName = imgName; self.imgView.image = [UIImage imageNamed:_imgName]; } - (void)layoutSubviews { [super layoutSubviews]; self.imgView.frame = self.bounds; } @end #pragma mark - ADCarouselView(輪播圖控件) @interface ADCarouselView()<UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout> /**輪播控件*/ @property (weak, nonatomic) UICollectionView *carouselView; /**佈局*/ @property (nonatomic, strong) UICollectionViewFlowLayout *layout; /**輪播圖片數組*/ @property (nonatomic, strong) NSMutableArray *carouselImages; /**自動輪播定時器*/ @property (nonatomic, strong) NSTimer *timer; /**當前滾動的位置*/ @property (nonatomic, assign) NSInteger currentIndex; /**上次滾動的位置*/ @property (nonatomic, assign) NSInteger lastIndex; @end @implementation ADCarouselView + (instancetype)carouselViewWithFrame:(CGRect)frame { ADCarouselView *carouselView = [[self alloc] initWithFrame:frame]; return carouselView; } - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { //一、添加collectionview //1.1設置collectionview佈局 UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; self.layout = layout; layout.minimumLineSpacing = 0; layout.minimumInteritemSpacing = 0; //設置滾動方向 layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; //1.2初始化collectionview UICollectionView *carouselView = [[UICollectionView alloc] initWithFrame:frame collectionViewLayout:layout]; carouselView.showsHorizontalScrollIndicator = NO; carouselView.pagingEnabled = YES; carouselView.delegate = self; carouselView.dataSource = self; //二、註冊cell類型 [carouselView registerClass:[ADCarouselViewCell class] forCellWithReuseIdentifier:@"carouselViewCell"]; self.carouselView = carouselView; //三、添加爲子控件 [self addSubview:carouselView]; //四、設置自動滾動時間間隔 self.loop = NO; self.automaticallyScrollDuration = 0; //添加標題和分頁 self.titleLabel.frame = kTitleLabelDefaultFrame; self.pageControlView.frame = kPageControlViewDefaultFrame; self.titleLabel.textColor = kTitleLabelDefaultTextColor; self.titleLabel.font = kTitleLabelDefaultFont; } return self; } #pragma mark 自動滾動時間設置 - (void)setAutomaticallyScrollDuration:(NSTimeInterval)automaticallyScrollDuration { _automaticallyScrollDuration = automaticallyScrollDuration; if (_automaticallyScrollDuration > 0) { [self.timer invalidate]; self.timer = nil; NSTimer *timer = [NSTimer timerWithTimeInterval:self.automaticallyScrollDuration target:self selector:@selector(startScrollAutomtically) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; self.timer = timer; } else { [self.timer invalidate]; } } #pragma mark 構造新的圖片數組 - (NSMutableArray *)carouselImages { if (!_carouselImages) { _carouselImages = [NSMutableArray arrayWithArray:self.imgs]; if (self.loop && self.imgs.count > 0) { [_carouselImages insertObject:[self.imgs lastObject] atIndex:0]; [_carouselImages addObject:self.imgs[0]]; } } return _carouselImages; } #pragma mark 自動滾動 - (void)startScrollAutomtically { NSInteger currentIndex = self.currentIndex + 1; currentIndex = (currentIndex == self.carouselImages.count) ? 1 : currentIndex; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:currentIndex inSection:0]; BOOL isNeedAnim = self.automaticallyScrollDuration <= 0.3 ? NO : YES; [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:isNeedAnim]; } - (void)layoutSubviews { [super layoutSubviews]; self.carouselView.frame = self.bounds; //默認滾動到第一張圖片 if (self.loop && self.carouselView.contentOffset.x == 0) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:0]; [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:NO]; self.currentIndex = 1; } } #pragma mark 代理方法 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { ADCarouselViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"carouselViewCell" forIndexPath:indexPath]; cell.imgView.contentMode = self.imageContentMode; cell.imgName = self.carouselImages[indexPath.row]; return cell; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.carouselImages.count; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat width = self.frame.size.width; NSInteger index = (scrollView.contentOffset.x + width * 0.5) / width; if (self.loop) { //當滾動到最後一張圖片時,繼續滾向後動跳到第一張 if (index == self.imgs.count + 1) { self.currentIndex = 1; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.currentIndex inSection:0]; [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:NO]; return; } //當滾動到第一張圖片時,繼續向前滾動跳到最後一張 if (scrollView.contentOffset.x < width * 0.5) { self.currentIndex = self.imgs.count; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.currentIndex inSection:0]; [self.carouselView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; return; } } //避免屢次調用currentIndex的setter方法 if (self.currentIndex != self.lastIndex) { self.currentIndex = index; } self.lastIndex = index; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { //關閉自動滾動 [self.timer invalidate]; } - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { if (self.automaticallyScrollDuration > 0) { [self.timer invalidate]; self.timer = nil; NSTimer *timer = [NSTimer timerWithTimeInterval:self.automaticallyScrollDuration target:self selector:@selector(startScrollAutomtically) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; self.timer = timer; } } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return self.frame.size; } - (void)setCurrentIndex:(NSInteger)currentIndex { _currentIndex = currentIndex; if (_currentIndex < self.imgs.count + 1) { // NSLog(@"%zd",currentIndex); NSInteger index = _currentIndex > 0 ? _currentIndex - 1 : 0; self.pageControlView.currentPage = index; self.titleLabel.hidden = !self.titles.count; if (self.titles.count > index) { self.titleLabel.text = self.titles[index]; } return; } } - (void)setImgs:(NSArray *)imgs { _imgs = imgs; self.pageControlView.hidden = !_imgs.count; self.pageControlView.numberOfPages = _imgs.count; } - (ADPageControlView *)pageControlView { if (!_pageControlView) { _pageControlView = [ADPageControlView pageControlViewWithFrame:CGRectZero]; [self addSubview:_pageControlView]; } return _pageControlView; } - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [[UILabel alloc] init]; [self addSubview:_titleLabel]; } return _titleLabel; } @end
PageControl控件:oop
#import <UIKit/UIKit.h> @interface ADPageControlView : UIView /**總頁數*/ @property(assign,nonatomic) NSInteger numberOfPages; /**當前頁*/ @property(assign,nonatomic) NSInteger currentPage; /**全部分頁dot的背景*/ @property (nonatomic, strong) UIImage *allPageDotImage; /**當前dot的背景*/ @property (nonatomic, strong) UIImage *currentPageDotImage; /**全部分頁dot的背景顏色*/ @property (nonatomic, strong) UIColor *allPageDotBackgroundColor; /**當前dot的背景顏色*/ @property (nonatomic, strong) UIColor *currentPageDotColor; /**dot的圓角,默認是dot點高的一半*/ @property (nonatomic, assign) CGFloat dotCorner; + (instancetype)pageControlViewWithFrame:(CGRect)frame; /** dotsSize:點的大小 dotsMargin:點之間的間距 */ + (instancetype)pageControlViewWithFrame:(CGRect)frame dotsSize:(CGSize)dotsSize dotsMargin:(CGFloat)dotsMargin; @end
#import "ADPageControlView.h" #define ADPageControlViewDotViewDefaultWH 10 #define ADPageControlViewDotViewDefaultColor [UIColor grayColor] #define ADPageControlViewCurrentDotViewColor [UIColor whiteColor] @interface ADPageControlView() /**小圓點*/ @property (nonatomic, strong) NSMutableArray *dots; /**小圓點大小*/ @property (nonatomic, assign) CGSize dotsSize; /**小圓點之間的間距*/ @property (nonatomic, assign) CGFloat dotsMargin; /**是否徹底自定義*/ @property (nonatomic, assign) BOOL is_custom; @end @implementation ADPageControlView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.numberOfPages = 0; self.currentPage = 0; } return self; } + (instancetype)pageControlViewWithFrame:(CGRect)frame { return [self pageControlViewWithFrame:frame dotsSize:CGSizeZero dotsMargin:0]; } + (instancetype)pageControlViewWithFrame:(CGRect)frame dotsSize:(CGSize)dotsSize dotsMargin:(CGFloat)dotsMargin { ADPageControlView *pageControlView = [[ADPageControlView alloc] initWithFrame:frame]; pageControlView.dotsSize = dotsSize; pageControlView.dotsMargin = dotsMargin; pageControlView.is_custom = YES; return pageControlView; } - (void)layoutSubviews { [super layoutSubviews]; CGFloat dotViewW = 0; CGFloat dotViewMargin = 0; CGFloat dotViewY = 0; CGFloat dotViewH = 0; CGFloat dotViewMarginLeft = 0; dotViewW = self.dotsSize.width > 0 ? self.dotsSize.width : ADPageControlViewDotViewDefaultWH; dotViewH = self.dotsSize.height > 0 ? self.dotsSize.height : ADPageControlViewDotViewDefaultWH; dotViewMargin = self.dotsMargin > 0 ? self.dotsMargin : (self.bounds.size.width - self.numberOfPages * dotViewW) / (self.numberOfPages - 1); dotViewY = (self.bounds.size.height - dotViewH) * 0.5; dotViewMarginLeft = (self.bounds.size.width - self.numberOfPages * dotViewW - (self.numberOfPages - 1) * dotViewMargin) * 0.5; [self.dots enumerateObjectsUsingBlock:^(UIImageView *dotView, NSUInteger idx, BOOL * _Nonnull stop) { CGFloat dotViewX = dotViewMarginLeft + idx * (dotViewW + dotViewMargin); dotView.frame = CGRectMake(dotViewX, dotViewY, dotViewW, dotViewH); CGFloat cornerRadius = self.dotCorner > 0 ? self.dotCorner : dotViewH * 0.5; dotView.layer.cornerRadius = cornerRadius; }]; } - (void)setNumberOfPages:(NSInteger)numberOfPages { _numberOfPages = numberOfPages; [self.dots enumerateObjectsUsingBlock:^(UIView *dotView, NSUInteger idx, BOOL * _Nonnull stop) { [dotView removeFromSuperview]; }]; [self.dots removeAllObjects]; for (NSInteger i = 0; i < _numberOfPages; i++) { UIImageView *dotView = [[UIImageView alloc] init]; dotView.backgroundColor = self.allPageDotBackgroundColor ? self.allPageDotBackgroundColor : ADPageControlViewDotViewDefaultColor; dotView.image = self.allPageDotImage; [self.dots addObject:dotView]; [self addSubview:dotView]; } } - (void)setCurrentPage:(NSInteger)currentPage { _currentPage = currentPage; [self.dots enumerateObjectsUsingBlock:^(UIImageView *dotView, NSUInteger idx, BOOL * _Nonnull stop) { dotView.backgroundColor = self.allPageDotBackgroundColor ? self.allPageDotBackgroundColor : ADPageControlViewDotViewDefaultColor; dotView.image = self.allPageDotImage; if (idx == _currentPage) { dotView.backgroundColor = ADPageControlViewCurrentDotViewColor; dotView.image = self.currentPageDotImage; } }]; } - (NSMutableArray *)dots { if (!_dots) { _dots = [NSMutableArray array]; } return _dots; } @end