輪播圖幾乎是每一個 App 都會有的功能,而在 UIKit
中又找不到能夠直接實現輪播圖的視圖控件。因此我們需要通過組合視圖控件的方法去實現輪播。
首先分析輪播圖的結構組成,從靜止上看,輪播圖就是在一個橫向的滾動視圖上添加了幾張圖片,然后在滾動視圖的底部添加一個頁碼控制器來監聽滾動視圖顯示的是第幾張圖片,接下來的循環滾動,只需要添加一個定時器來讓滾動視圖有序地進行輪回滾動。
考慮到視圖的復用性和點擊觸發事件,我們使用 UICollectionView
作為主控件,設置翻頁的橫向滾動,在 UICollectionViewCell
上添加和 UICollectionView
一樣大小的 UIImageView
。為了讓封裝之后的輪播圖類方便在視圖控制器上的使用,將這個類繼承于 UIView
,UICollectionView
和頁碼控制器 UIPageControl
的布局將在類的初始化實現。為了能夠讓輪播圖的點擊事件得到響應,在初始化的時候,傳入一個可以處理用戶點擊了第幾張輪播圖片的 block
。
不多說了,直接上代碼。
接口頭文件 CJMRotationPictureController.h
:
/**
選中圖片完成處理
@param nPicture 選中的第幾張圖片
*/
typedef void(^ selectCompletion)(NSInteger nPicture);
/**
輪播圖
*/
@interface CJMRotationPictureController : UIView
/**
初始化方法
@param frame 布局
@param complete 選擇處理
@return 實例化對象
*/
- (instancetype)initWithFrame:(CGRect)frame selectComplete:(selectCompletion)complete;
/**
更新輪播圖,支持 UIImage 數組 或 圖片地址 NSString 數組
@param arrPicture 圖片數據
*/
- (void)updateDataWithPictureArray:(NSArray *)arrPicture;
@end
實現文件 CJMRotationPictureController.m
:
// 主視圖的長寬
#define kViewWidth self.frame.size.width
#define kViewHeight self.frame.size.height
static NSString *gPictureID = @"PictureID";
@interface CJMRotationPictureController () <UICollectionViewDelegate, UICollectionViewDataSource>
/**
主集合圖
*/
@property (nonatomic, weak) UICollectionView *collectMain;
/**
頁碼控制器
*/
@property (nonatomic, weak) UIPageControl *pageShow;
/**
數據源 UIImage 數組 或 圖片地址 NSString 數組
*/
@property (nonatomic, strong) NSArray *arrDataSource;
/**
滾動計時器
*/
@property (nonatomic, strong) NSTimer *timerScroll;
/**
事件處理
*/
@property (nonatomic, copy) selectCompletion blockComplete;
@end
@implementation CJMRotationPictureController
/**
初始化方法
@param frame 布局
@param complete 選擇處理
@return 實例化對象
*/
- (instancetype)initWithFrame:(CGRect)frame selectComplete:(selectCompletion)complete {
self = [super initWithFrame:frame];
if (self) {
[self initLayout];
self.blockComplete = complete;
}
return self;
}
- (void)initLayout {
[self setBackgroundColor:[UIColor whiteColor]];
// 主集合圖
// 自適應布局
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setMinimumLineSpacing:0];
[flowLayout setMinimumInteritemSpacing:0];
// 水平方向滾動
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setItemSize:self.frame.size];
UICollectionView *collectTemp = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, kViewWidth, kViewHeight) collectionViewLayout:flowLayout];
[collectTemp setBackgroundColor:[UIColor whiteColor]];
// 隱藏水平滾動條
collectTemp.showsHorizontalScrollIndicator = NO;
// 翻頁效果
collectTemp.pagingEnabled = YES;
collectTemp.delegate = self;
collectTemp.dataSource = self;
[self addSubview:collectTemp];
self.collectMain = collectTemp;
// 注冊cell
[self.collectMain registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:gPictureID];
// 頁碼控制器
UIPageControl *pageTemp = [[UIPageControl alloc] initWithFrame:CGRectMake(kViewWidth * 0.8, kViewHeight - 30, kViewWidth/5, 30)];
// 只有一頁是隱藏
[pageTemp setHidesForSinglePage:YES];
// 普通顏色
[pageTemp setPageIndicatorTintColor:[UIColor colorWithWhite:1.0 alpha:0.5]];
// 當前頁顏色
[pageTemp setCurrentPageIndicatorTintColor:[UIColor colorWithWhite:1.0 alpha:0.8]];
[self addSubview:pageTemp];
self.pageShow = pageTemp;
self.arrDataSource = [NSArray array];
}
#pragma mark - UICollectionViewDataSourse 收集數據源處理
// 區域數
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
// 單元數
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.arrDataSource count];
}
// cell
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:gPictureID forIndexPath:indexPath];
// 圖片
UIImageView *imgvPicture = (UIImageView *)[cell viewWithTag:10];
if (imgvPicture == nil) {
// 添加子控件
imgvPicture = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kViewWidth, kViewHeight)];
[imgvPicture setTag:10];
[imgvPicture setClipsToBounds:YES];
[imgvPicture setContentMode:UIViewContentModeScaleAspectFill];
[cell addSubview:imgvPicture];
}
// 數組對象的類型
if ([self.arrDataSource[indexPath.row] isKindOfClass:[UIImage class]]) {
// UIImage
[imgvPicture setImage:self.arrDataSource[indexPath.row]];
} else if ([self.arrDataSource[indexPath.row] isKindOfClass:[NSString class]]) {
// 圖片地址的 NSString
NSURL *urlImage = [NSURL URLWithString:self.arrDataSource[indexPath.row]];
NSData *dataImage = [NSData dataWithContentsOfURL:urlImage];
if (dataImage) {
[imgvPicture setImage:[UIImage imageWithData:dataImage]];
}
}
return cell;
}
// 選中項處理
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
// 通過委托傳遞選中序號
self.blockComplete(indexPath.row);
}
#pragma mark - 對外接口
/**
更新輪播圖,支持 UIImage 數組 或 圖片地址 NSString 數組
@param arrPicture 圖片數據
*/
- (void)updateDataWithPictureArray:(NSArray *)arrPicture {
self.arrDataSource = arrPicture;
// 頁視圖布局
CGFloat fwidth = 13 * [self.arrDataSource count];
[self.pageShow setFrame:CGRectMake(kViewWidth - 14 - fwidth, kViewHeight - 30, fwidth, 30)];
// 總頁數
[self.pageShow setNumberOfPages:[self.arrDataSource count]];
// 當前頁
[self.pageShow setCurrentPage:0];
// 更新集合視圖
[self.collectMain reloadData];
// 信息不少于2條是,啟動計時器,否則停止計時器
if ([self.arrDataSource count] > 2) {
// 啟動定時器
[self fireScollerTimer];
} else {
// 關閉定時器
if (self.timerScroll) {
[self.timerScroll invalidate];
}
}
}
/**
啟動定時器
*/
- (void)fireScollerTimer {
if (self.timerScroll == nil) {
self.timerScroll = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timerActionWithTimer:) userInfo:nil repeats:YES];
}
[[NSRunLoop currentRunLoop] addTimer:self.timerScroll forMode:NSRunLoopCommonModes];
}
/**
定時器事件
@param timer 定時器
*/
- (void)timerActionWithTimer:(NSTimer* )timer {
// 通過改變滾動的滾動位置來達到切換的效果
// 獲取當前的所處的滾動位置
float offset_x = self.collectMain.contentOffset.x;
// 每次滾動一條
offset_x += kViewWidth;
// 滾屏寬度
CGFloat scrollWidth = ([self.arrDataSource count] - 1) * kViewWidth;
// 最后一條之后,回到第一條
if (offset_x > scrollWidth) {
// 最后的偏移量
[self.collectMain setContentOffset:CGPointMake(0, 0) animated:NO];
} else {
[self.collectMain setContentOffset:CGPointMake(offset_x, 0) animated:YES];
}
}
/**
更新頁碼
*/
- (void)updataPageNumber {
float offset_x = self.collectMain.contentOffset.x;
[self.pageShow setCurrentPage:offset_x/kViewWidth];
}
#pragma mark - UIScrollViewDelegate 滾動監聽
// 滾動視圖開始被拖動
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
// 定時器休眠
[self.timerScroll setFireDate:[NSDate distantFuture]];
}
// 滾動視圖靜止
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// 定時器 2 秒后重啟
[self.timerScroll setFireDate:[NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]]];
}
// 滾動后--更新所滑到的頁碼
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// 更新頁碼
[self updataPageNumber];
}
@end
在視圖控制器上的調用:
CJMRotationPictureController *rotation = [[CJMRotationPictureController alloc] initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenWidth/2) selectComplete:^(NSInteger nPicture) {
NSLog(@"點擊了第%ld圖片", nPicture);
}];
[self.view addSubview:rotation];
// 載入圖片
[rotation updateDataWithPictureArray:@[[UIImage imageNamed:@"desk1"], [UIImage imageNamed:@"test2"], [UIImage imageNamed:@"desk1"], [UIImage imageNamed:@"test2"]]];
結果截圖:
輪播圖
GitHub 上的代碼
使用 CocoaPods
集成:
pod 'CJMRotationPicture'