iOS 輪播圖

輪播圖是App中常用的展示界面,主要用到的控件有UIScrollView和UIPageControl邀杏。難點(diǎn)有兩處,一個(gè)是定時(shí)器自動(dòng)輪播,一個(gè)是手動(dòng)操作后不影響定器自動(dòng)輪播

一芬沉、要達(dá)到的效果

效果動(dòng)圖.gif

二、邏輯分析

首先阁猜,輪播圖需要輪播幾張圖片丸逸,就要往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下載

gitHub下載地址

五冯遂、補(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)趋箩!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市加派,隨后出現(xiàn)的幾起案子叫确,更是在濱河造成了極大的恐慌,老刑警劉巖哼丈,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件启妹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡醉旦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)桨啃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)车胡,“玉大人,你說(shuō)我怎么就攤上這事照瘾⌒偌” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵析命,是天一觀的道長(zhǎng)主卫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鹃愤,這世上最難降的妖魔是什么簇搅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮软吐,結(jié)果婚禮上瘩将,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好姿现,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布肠仪。 她就那樣靜靜地躺著,像睡著了一般备典。 火紅的嫁衣襯著肌膚如雪异旧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,562評(píng)論 1 305
  • 那天提佣,我揣著相機(jī)與錄音吮蛹,去河邊找鬼。 笑死镐依,一個(gè)胖子當(dāng)著我的面吹牛匹涮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播槐壳,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼然低,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了务唐?” 一聲冷哼從身側(cè)響起雳攘,我...
    開(kāi)封第一講書(shū)人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎枫笛,沒(méi)想到半個(gè)月后吨灭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刑巧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年喧兄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片啊楚。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吠冤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恭理,到底是詐尸還是另有隱情拯辙,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布颜价,位于F島的核電站涯保,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏周伦。R本人自食惡果不足惜夕春,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望横辆。 院中可真熱鬧撇他,春花似錦茄猫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至锌畸,卻和暖如春勇劣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背潭枣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工比默, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盆犁。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓命咐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谐岁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子醋奠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 提到app輪播圖,我們會(huì)想到好多種實(shí)現(xiàn)方法伊佃。這里我給大家介紹一下用三個(gè)UIImageView創(chuàng)建輪播圖的方法,這里...
    杯水救車薪閱讀 2,088評(píng)論 2 5
  • 首先要封裝一個(gè)類窜司,有了這個(gè)類,只要把類拖拽到你的工程航揉,媽媽就再也不用擔(dān)心你不會(huì)做輪播圖了塞祈! 創(chuàng)建這個(gè)類如下: 類名...
    MangoJ閱讀 3,598評(píng)論 5 6
  • 現(xiàn)在的App開(kāi)發(fā)中,輪播圖幾乎是一個(gè)不可避免的都會(huì)用到的帅涂。個(gè)人封裝過(guò)輪播圖议薪,也看過(guò)很多種不同的輪播圖,目前掌握的輪...
    雪中夜歸人閱讀 11,013評(píng)論 16 40
  • 作為一個(gè)iOS開(kāi)發(fā)者,我們經(jīng)常需要用到輪播圖,比如:我們首頁(yè)頂部的廣告輪播,商品詳情頁(yè)面的商品圖片輪播等,如果我們...
    cd5e2b81487d閱讀 2,685評(píng)論 1 24
  • 好久沒(méi)寫(xiě)簡(jiǎn)書(shū)了媳友。上個(gè)月還在放假的時(shí)候幾乎一天一篇日記吧笙蒙,傻露上學(xué)之后她也不經(jīng)常寫(xiě)了,我還說(shuō)呢咋不堅(jiān)持呢庆锦。好吧,我上...
    暖在手心的皮卡丘閱讀 232評(píng)論 6 2