輪播圖是App中常用的展示界面,主要用到的控件有UIScrollView和UIPageControl邀杏。難點(diǎn)有兩處,一個(gè)是定時(shí)器自動(dòng)輪播,一個(gè)是手動(dòng)操作后不影響定器自動(dòng)輪播
一芬沉、要達(dá)到的效果
二、邏輯分析
首先阁猜,輪播圖需要輪播幾張圖片丸逸,就要往scrollView上放幾張對(duì)應(yīng)的圖片,這里定為3張剃袍。圖片大小寬度和ScrollView的frame一樣黄刚,便于翻頁(yè)。
然后民效,當(dāng)圖片從頭輪播時(shí)憔维,到第三張,此時(shí)圖片應(yīng)該跳到第一張才能實(shí)現(xiàn)循環(huán)畏邢,但是要保證流暢的話业扒,就不能直接設(shè)置scrollView的contentOffset為(0,0)。解決辦法是舒萎,在scrollView的最后再加一張ImageView程储,上面放第一張圖片,這樣的話當(dāng)輪播到第四張ImageView(視覺(jué)效果是第一張圖片)時(shí)逆甜,再設(shè)置scrollView的contentOffset為(0,0)虱肄,這樣視覺(jué)效果就流暢了
第三,因?yàn)樾枰鶅蓚€(gè)方向輪播交煞,所以也要在scrollView的開(kāi)頭加一個(gè)ImageView,上面放第三張圖片咏窿,并設(shè)置當(dāng)輪播到這里時(shí)設(shè)置scrollView的contentOffset為倒數(shù)第二張imageView的位置。
綜上:要實(shí)現(xiàn)三張圖片的輪播素征,ScrollView的contentSize的寬度為5張圖片寬度集嵌,第一張ImageView放第三張圖片,最后一張ImageView放第一張圖片御毅,其他的按順序放圖片根欧。以上能實(shí)現(xiàn)的是手指滑動(dòng)的輪播,還有自動(dòng)輪播和手指滑動(dòng)后自動(dòng)輪播的稍復(fù)雜功能端蛆,但是核心原理還是上面的原理凤粗。直接上代碼,更合味
三今豆、代碼分析
添加UIScrollView嫌拣、NStimer和UIPageControl控件
@property (nonatomic)UIScrollView *scrollView;
@property(nonatomic,strong)UIPageControl * pageController;//頁(yè)面控制器
@property(nonatomic,strong)NSTimer * timer;//計(jì)時(shí)器
定義幾個(gè)宏
#define imageCount 3//圖片的張數(shù)
//當(dāng)前設(shè)備的屏幕寬度
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
//當(dāng)前設(shè)備的屏幕高度
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
初始化控件柔袁,并將圖片添加上去
//初始化定時(shí)器
self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
//添加scrollView
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 250)];
self.scrollView.contentSize = CGSizeMake((imageCount + 2)*kScreenWidth, 0);
self.scrollView.pagingEnabled = YES;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.bounces = NO;
self.scrollView.scrollEnabled = YES;
self.scrollView.contentOffset = CGPointMake(1*kScreenWidth, 0);
self.scrollView.delegate = self;
[self.view addSubview:self.scrollView];
//添加圖片
for (int i = 0; i < imageCount+2; i++) {
if (i == 0) {
UIImageView * imageV = [[UIImageView alloc] initWithFrame:CGRectMake(i*kScreenWidth, 0, kScreenWidth, 250)];
[self.scrollView addSubview:imageV];
imageV.image = [UIImage imageNamed:@"13.jpg"];
} else if(i == imageCount + 1){
UIImageView * imageV = [[UIImageView alloc] initWithFrame:CGRectMake(i*kScreenWidth, 0, kScreenWidth, 250)];
[self.scrollView addSubview:imageV];
imageV.image = [UIImage imageNamed:@"11.jpg"];
}else{
UIImageView * imageV = [[UIImageView alloc] initWithFrame:CGRectMake(i*kScreenWidth, 0, kScreenWidth, 250)];
[self.scrollView addSubview:imageV];
imageV.image = [UIImage imageNamed:[NSString stringWithFormat:@"1%d.jpg",i]];
}
}
//在scrollView上添加page
self.pageController = [[UIPageControl alloc] init];
[self.view addSubview:self.pageController];
self.pageController.frame = CGRectMake(kScreenWidth/2 - 50, 250 - 20, 100, 25);
self.pageController.numberOfPages = imageCount;
self.pageController.pageIndicatorTintColor = [UIColor whiteColor];
self.pageController.currentPageIndicatorTintColor = [UIColor cyanColor];
self.pageController.currentPage = 0;
滑動(dòng)ScrollView時(shí),要設(shè)置頁(yè)碼控件PageControl的改變,還要設(shè)置在最后一張和第一張ImageView時(shí)要做跳轉(zhuǎn)异逐,這些都可以在代理方法中去實(shí)現(xiàn)捶索。遵循代理UIScrollViewDelegate
在只要ScrollView滑動(dòng)時(shí)就會(huì)走的代理方法中設(shè)置PageControl的改變
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//當(dāng)scrollView滑動(dòng)時(shí),設(shè)置page
CGFloat scroll = scrollView.contentOffset.x/kScreenWidth;
NSInteger number = (NSInteger)scroll;//偏移了幾個(gè)屏幕寬的距離
if (number == imageCount+1||(number == imageCount&&scroll - number > kScreenWidth/2)) {//如果偏移為最大值或?qū)⒁阶畲笾? self.pageController.currentPage = 0;
}else if (number == 0||(number == 0&&scroll-number < kScreenWidth/2)){//如果偏移為最小值或者將要到最小值
self.pageController.currentPage = imageCount- 1;
}else{
if (scroll - number>kScreenWidth/2) {
self.pageController.currentPage = (number-1)+1;
}
if (scroll - number<=kScreenWidth/2) {
self.pageController.currentPage = (number-1);
}
}
}
在滑動(dòng)結(jié)束減速時(shí)的代理方法中設(shè)置偏移
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
if (self.scrollView.contentOffset.x == (imageCount + 1)*kScreenWidth) {//手動(dòng)滑到最后一張ImageView
self.scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
}else if (self.scrollView.contentOffset.x == 0*kScreenWidth) {//手動(dòng)滑到第一張ImageView
self.scrollView.contentOffset = CGPointMake(imageCount*kScreenWidth, 0);
}
}
以上實(shí)現(xiàn)的是可手動(dòng)輪播灰瞻,下面是自動(dòng)輪播的實(shí)現(xiàn)
自動(dòng)輪播的實(shí)現(xiàn)主要是在定時(shí)器的定時(shí)事件中實(shí)現(xiàn)功能腥例。先創(chuàng)建一個(gè)全局變量,控制顯示的圖片角標(biāo)酝润,并循環(huán)使用
static NSInteger pageNumber = 0;//用于記錄計(jì)時(shí)器計(jì)時(shí)循環(huán)
然后實(shí)現(xiàn)定時(shí)器的定時(shí)事件方法
-(void)timerAction{
if(pageNumber == imageCount){//手動(dòng)滑滑到最大偏移燎竖,即顯示的是視覺(jué)上第一張圖
pageNumber = 0;
//跳到第一張圖
self.scrollView.contentOffset = CGPointMake(kScreenWidth,0);
//然后滑到視覺(jué)上第二張圖片
[UIView animateWithDuration:0.5 animations:^{
self.scrollView.contentOffset = CGPointMake((pageNumber+2)*kScreenWidth,0);
}];
}else if(pageNumber == 0){
//滑到視覺(jué)上第二張圖
[UIView animateWithDuration:0.5 animations:^{
self.scrollView.contentOffset = CGPointMake((pageNumber+2)*kScreenWidth,0);
}];
}else {
[UIView animateWithDuration:0.5 animations:^{
self.scrollView.contentOffset = CGPointMake((pageNumber+2)*kScreenWidth,0);
}];
}
pageNumber++;
}
對(duì)于上面的方法中pageNumber的理解:pageNumber=0時(shí),當(dāng)前顯示第一張圖袍祖,但是將要顯示第二張圖底瓣;等于1時(shí)當(dāng)前顯示第二張圖,將要顯示第三張圖蕉陋,以此類推。當(dāng)pageNumber=3拨扶,這時(shí)顯示的是最后一張ImageView,也就是第一張圖凳鬓,所以將pageNumber設(shè)置為0,因?yàn)槭且h(huán)顯示患民,所以將Scrollview的偏移設(shè)置為(kScreenWidth,0)缩举,即第一張圖,此時(shí)將要顯示的是第二張圖匹颤,如此便完成了循環(huán)自動(dòng)輪播
手動(dòng)輪播和自動(dòng)輪播都已實(shí)現(xiàn)仅孩,現(xiàn)在要實(shí)現(xiàn)的是自動(dòng)+手動(dòng)輪播。
思路:當(dāng)ScrollView正要自動(dòng)輪播到下一張圖時(shí)印蓖,我剛好進(jìn)行了手動(dòng)操作辽慕,這樣的話ScrollView到底是按自動(dòng)播放到下一張還是按手動(dòng)滑到下一張?感覺(jué)有沖突赦肃。所以這里的處理方式是溅蛉,只要進(jìn)行了手動(dòng)操作,就將自動(dòng)播放停止他宛,手動(dòng)操作完畢船侧,在開(kāi)啟自動(dòng)播放,這樣就不沖突了
先寫(xiě)兩個(gè)方法厅各,開(kāi)啟定時(shí)器的方法和結(jié)束定時(shí)器的方法
-(void)beginAction{//開(kāi)啟定時(shí)器
//如果計(jì)時(shí)器已開(kāi)啟 先停止
if (self.timer) [self stopAction];
self.timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
-(void)stopAction{//結(jié)束定時(shí)器
[self.timer invalidate];
self.timer = nil;
}
首先镜撩,在ScrollView開(kāi)始被拖拽時(shí)停止定時(shí)器,實(shí)現(xiàn)ScrollView開(kāi)始拖拽的代理方法
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
//結(jié)束計(jì)時(shí)
[self stopAction];
}
在拖拽完畢的時(shí)候開(kāi)啟定時(shí)器队塘,可以在ScrollView的減速結(jié)束的代理方法添加開(kāi)啟定時(shí)器的代碼(這個(gè)代理方法上面寫(xiě)過(guò)了袁梗,直接添加一行代碼即可)卫旱。
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
if (self.scrollView.contentOffset.x == (imageCount + 1)*kScreenWidth) {//手動(dòng)滑到最后一張ImageView
self.scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
}else if (self.scrollView.contentOffset.x == 0*kScreenWidth) {//手動(dòng)滑到第一張ImageView
self.scrollView.contentOffset = CGPointMake(imageCount*kScreenWidth, 0);
}
//啟動(dòng)定時(shí)器
[self beginAction];
}
手指滑動(dòng)結(jié)束后,開(kāi)始自動(dòng)播放围段,此時(shí)是定時(shí)器的定時(shí)事件方法(timerAction)起作用顾翼,該播放哪一張圖由pageNumber等于幾決定,由于定時(shí)器停止時(shí)pageNumber的值奈泪,無(wú)法正確指示顯示哪一張圖了适贸,所以再手動(dòng)滑動(dòng)時(shí),應(yīng)該同步改變pageNumber的值涝桅。在ScrollView的減速結(jié)束代理方法中加入代碼
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
if (self.scrollView.contentOffset.x == (imageCount + 1)*kScreenWidth) {//手動(dòng)滑到最后一張ImageView
self.scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
}else if (self.scrollView.contentOffset.x == 0*kScreenWidth) {//手動(dòng)滑到第一張ImageView
self.scrollView.contentOffset = CGPointMake(imageCount*kScreenWidth, 0);
}
//啟動(dòng)定時(shí)器
[self beginAction];
//拖拽結(jié)束后給記錄輪播到第幾張的變量賦值
NSInteger number = (NSInteger)self.scrollView.contentOffset.x/kScreenWidth;
if (number == imageCount+1){//在最后一張(向右才會(huì)到這里)
pageNumber = 0;
}else if(number == 0){//在scrollView的第一張(向左才會(huì)到這里)
pageNumber = imageCount -1;
}else{
pageNumber = number - 1;
}
}
至此拜姿,大功告成
四、demo下載
五冯遂、補(bǔ)充
今天去面試蕊肥,面試官說(shuō),在沒(méi)有失焦的情況下蛤肌,一直滑動(dòng)壁却,滑動(dòng)到頭,就會(huì)滑不動(dòng)了裸准。這種情況怎么處理展东?當(dāng)時(shí)沒(méi)有回答上來(lái),也沒(méi)考慮過(guò)炒俱,回家玩了玩以前的DEMO盐肃,確實(shí)沒(méi)有考慮這種情況。但是經(jīng)過(guò)反復(fù)驗(yàn)證权悟,找到了一種解決辦法砸王。也許不是最簡(jiǎn)單的。先呈上峦阁,請(qǐng)品味谦铃。
思路:只要scrollView在動(dòng),就會(huì)走scrollViewDidScroll代理方法拇派,那只要捕捉到偏移量最大和最小的瞬間荷辕,并在此瞬間將scrollView的偏移量設(shè)置到對(duì)應(yīng)的位置就可以了。我發(fā)現(xiàn)一直拖拽的狀態(tài)是走了scrollViewWillBeginDragging代理方法件豌,但沒(méi)有走scrollViewDidEndDragging代理方法疮方,一旦走了scrollViewDidEndDragging代理方法,拖拽就結(jié)束茧彤。所以骡显,我可以設(shè)置一個(gè)全局變量,記錄拖拽的狀態(tài)。有了拖拽的狀態(tài)惫谤,我就可以在scrollViewDidScroll的方法中捕捉偏移量最大值和最小值壁顶。具體看下面實(shí)現(xiàn)。
首先添加一個(gè)屬性溜歪,布爾值類型若专,用來(lái)記錄是否在拖拽狀態(tài)
@property(nonatomic,assign)BOOL isDraging;//是否拖拽
當(dāng)然,應(yīng)該給一個(gè)初始值蝴猪,這是我的習(xí)慣
self.isDraging = NO;
然后給這個(gè)屬性進(jìn)行賦值
在scrollView的開(kāi)始拖拽的代理方法中调衰,修改isDraging屬性的值為YES,表示已經(jīng)進(jìn)入拖拽狀態(tài)
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
//結(jié)束計(jì)時(shí)
[self stopAction];
self.isDraging = YES;
}
在scrollView結(jié)束拖拽的代理方法中,修改isDraging屬性的值為NO,表示已經(jīng)結(jié)束拖拽狀態(tài)
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
self.isDraging = NO;
}
然后在代理方法:scrollViewDidScroll中加入兩個(gè)判斷自阱,即在拖拽狀態(tài)下嚎莉,滑動(dòng)到最大值和滑動(dòng)到最小值時(shí),跳轉(zhuǎn)到第一張圖片位置(此位置偏移量為一張圖片的寬度)和最后一張圖片位置(此位置偏移量為3張圖片寬度偏移量位置)
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (self.isDraging&&self.scrollView.contentOffset.x == 0) {
self.scrollView.contentOffset = CGPointMake(imageCount*kScreenWidth, 0);//跳到了倒數(shù)第二張ImageView(即最后一張圖)
}
if (self.isDraging&&self.scrollView.contentOffset.x == (imageCount + 1)*kScreenWidth) {
self.scrollView.contentOffset = CGPointMake(kScreenWidth,0);//偏移量為第一張圖
}
...
}
這樣問(wèn)題就解決了沛豌。歡迎指點(diǎn)趋箩!