iOS-無限循環輪播器(注釋詳細到沒有之一)

Bg:

1)有一段時間沒有寫文章了,最近事兒比較多,今天有人在技術群里面問我使用UIScrollview實現無限循環輪播的思想(3個UIImageView實現),我當時給了他一篇博客,不過好像這位朋友看的不是很懂,所以我寫了一個小Demo打算寫這篇文章去講解下,幫助有需要的朋友們,所以我盡量把能寫的注釋都寫詳細了,把思想寫全面了,讓大家看一遍基本就明白了,里面關于如何好的封裝一個控件的細節這里就不實現了,以講解實現為主哦
2)另外這種循環利用的思想也是面試可能會問到的,說不定還是加分項哦
3)另外這個文章說的是3個UIImageView,其實2個imageview完全可以實現(點擊這里看2個imageview實現),實時判斷向左向右,然后改變imageview的frame的x/y即可,當然還可以完全使用collectionview去實現,這個也比較流行(點擊這里看collectionview實現)

  • 先看下效果圖
kobe.gif
  • 這個效果圖也沒什么特別,大家都看到過無數次了,包括這個無線輪播,大家想必也都了解過,所以這次我們不實現什么特別的效果,主要是通過這個小功能,給有需要的朋友講解下無線輪播思想

使用3個imageview實現無線輪播的大致原理

  • 將3個imageview添加到scrollview上面,scrollview的contensize是3個imageview的寬度,設置scrollview一開始初始的偏移量為一個imageview寬度,因為里面有3個UIImageView,所以scrollview默認顯示的就是中間的那個imageview,并且關鍵就是讓屏幕顯示的始終就是中間的這個imagview
  • 使用3個imageview來回更換圖片,并在每一次更換圖片后立即再設置scrollview偏移量還為一個imagview的寬度,也就是讓scrollview滾動后再滾回原來默認的位置,這樣就可以達到始終顯示中間那個imageview的效果
  • 看到過其他博客里面有這樣描述過這個原理
ps:例如要使用三個UIImageView循環顯示5張圖片
1)由于中間的imageview是顯示在屏幕上的,它需要在啟動時默認顯示第1張圖片,那么左邊的imagview
自然就需要顯示最后一張圖片,右邊的imagview自然要顯示第二張圖片了.所以一開始肯定默認放圖片5、
圖片1、圖片2,當前顯示中間的UIImageView,也就是圖片1
2)如果用戶手指向左滑動,那么就會顯示圖片2,當圖片2顯示完整后迅速重新設置左中右三個UIImageView
的內容為圖片1、圖片2、圖片3,然后馬上設置contentOffset再次為一開始默認的一個imageview寬度,
讓它滾回默認一開始的位置,以此來達到一直顯示的是中間的UIImageView的效果,此刻中間那個imagview
顯示的也就是圖片2
3)繼續向左滑動看到圖片3,當圖片3滾動完成迅速重新設置3個UIImageView的內容為圖片2、圖片3、圖片
4,然后通過設置contentOffset依然顯示中間的那個UIImageView,此刻也就是圖片3
5)當然,向右滑動原理完全一樣,如此操作就給用戶一種循環的錯覺,而且圖片多的話不占用過多內存
  • 為此我做了一個動態圖,以此來動態描述下這個原理
scroll.gif

部分代碼實現:

  • 大致原理就是上述那些,文字比較多,但是是核心思想,可以借助代碼再去理解一下,代碼里面注釋也是非常詳細的
- (void)creatUI
{
    //初始化scrollview
    _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.scrollView.contentSize = CGSizeMake(view_WIDTH * 3, view_HEIGHT);
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.showsVerticalScrollIndicator = NO;
    self.scrollView.pagingEnabled = YES;
    self.scrollView.bounces = NO;
    //設置scrollview一開始的偏移量為一個寬度,因為里面有3個UIImageView,所以scrollview默認顯示的就是中間的那個imageview
    self.scrollView.contentOffset = CGPointMake(view_WIDTH, 0);
    self.scrollView.delegate = self;
    [self addSubview:self.scrollView];



    //初始化imageview
    _imageViews = [NSMutableArray array];
    //創建三個imageView作為循環復用的載體,圖片將循環加載在這三個imageView上面
    for (NSInteger i = 0; i < 3; i++) {
        UIImageView *imageView = [[UIImageView alloc] init];
        imageView.frame = CGRectMake(view_WIDTH * i, 0, view_WIDTH,view_HEIGHT);
        //(self.dataArray.count - 1 + i)%self.dataArray.count也可以達到讓一開始3個imageview分別顯示最后一張<-->第一張<-->第二張圖片,但是讓大家理解起來會有一定難度,所以采用下面最簡單的方法直接設置
        //imageView.tag = (self.dataArray.count - 1 + i)%self.dataArray.count;

        //3個imageview一開始需要的圖片分別對應圖片數組的圖片索引應該是imageview[0].index-->images.count-1,imageview[1].index-->0,imageview[2].index-->1
        NSInteger index = 0;
        if (i == 0) index = _imagesArray.count - 1;
        if (i == 1) index = 0;
        if (i == 2) index = 1;

        //把index賦值給imageview的tag值,這樣方便在后面方法中通過imageview的tag值直接拿到index,我們就可以輕松從圖片數組獲取對應的圖片然后顯示到imageview上面,有媒介的作用
        imageView.tag = index;
        imageView.userInteractionEnabled = YES;

       //這里給imageview添加了一個單擊手勢,通過block回調處理了imageview點擊的監聽事件
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageViewClicked:)];
        [imageView addGestureRecognizer:tap];

        //設置imageView上的image圖片,2個方法使用哪一個設置都可以,關于2個方法的選擇可以看下面詳細注釋
        [self setImageWithImageView:imageView];
        [self setImageView:imageView atIndex:index];
        //將imageView加入數組中,方便隨后取用
        [_imageViews addObject:imageView];
        [self.scrollView addSubview:imageView];

    }
    //初始化pageControl,最后添加,這樣它會顯示在最前面,不會被遮擋
    self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.scrollView.frame) - 30, view_WIDTH, 30)];
    self.pageControl.numberOfPages = _imagesArray.count;
    self.pageControl.currentPage = 0;
    [self addSubview:self.pageControl];
}


/*
 這里給imageview設置圖片有2個選擇,第一可以使用這個方法,傳遞一個需要設置的imageview以及對應的index
 好處:一眼就可以讓大家明白這個方法內部的實現功能,易與閱讀和理解
 相對于下面那個方法的壞處:其實我們完全可以不傳遞index這個參數,我們完全可以把index賦值給imageview的tag,這樣我們只用傳遞一個imageview過來,就可以既拿到imageview,又可以通過imageview的tag拿到index
 總結:2個方法都可以,看大家喜歡哪一種,哪一種順手好理解就使用哪一種
 */
- (void)setImageView:(UIImageView *)imageView atIndex:(NSInteger)index
{
    //根據實時計算得出的index,從圖片數組里面取值,然后賦值給對應左中右3個imageview
    UIImage *image = (UIImage *)_imagesArray[index];
    imageView.image = image;

}
- (void)setImageWithImageView:(UIImageView *)imageView{

    //根據imageView的tag值給imageView設置image
    // UIImage *image = (UIImage *)self.dataArray[imageView.tag];
    // imageView.image = image;
}

//定時器調用的方法
- (void)nextPage
{
    //NSLog(@"定時器的%f",_scrollView.contentOffset.x);
    //定時器方法都是相當于向左滑動,偏移量是增大的,原本偏移量是一倍的寬度,定時器方法執行一次,偏移量就要增大一個寬度,這樣也就是setContentOffset:CGPointMake(VIEW_WIDTH * 2, 0),相當于設置偏移量是2倍寬度
    //執行了setContentOffset:方法,系統會自動調用scrollViewDidEndScrollingAnimation:方法,在這個方法里面再設置回偏移量等于一倍的寬度,同時更換各個imageview的圖片,那么還是相當于中間的那個imageview顯示在屏幕上
    [self.scrollView setContentOffset:CGPointMake(view_WIDTH * 2, 0) animated:YES];
}

#pragma mark - 更新圖片和分頁控件的當前頁
- (void)updateImageViewsAndPageControl {
    //先判斷出scrollview的操作行為是向左向右還是不動
    //定義一個flag,目前是讓scrollview向左向右滑動的時候索引對應的+1或者-1
    int flag = 0;
    if (self.scrollView.contentOffset.x > view_WIDTH)
    {//手指向左滑動
        flag = 1;
    }
    else if (self.scrollView.contentOffset.x == 0)//原本偏移量是一個寬度,現在==0了,那么就是手指向右滑動了
    {//手指向右滑動
        flag = -1;
    }
    else
    {//除了向左向右之外就是沒有移動,那么不需要任何操作,直接返回
        return;
    }

    //    NSInteger index = 0;
    //修改imageViews中的imageView的tag值,從而修改imageView上顯示的image,pageControl的頁碼
    for (UIImageView *imageView in _imageViews) {
        /*
         (1)當屏幕中間那個imageview顯示最后一張圖片時,右邊的ImageView,也即下一張圖片應該是顯示最開始的那一張圖片(第0張);

         (2)當屏幕中間顯示最開始的那一張圖片(第0張)時,左邊的ImageView,也即上一張圖片應該是最后一張圖片。
         */
        NSInteger index = imageView.tag + flag ;

        if (index < 0) {
            index = self.pageControl.numberOfPages - 1;
        } else if (index >= self.pageControl.numberOfPages) {
            index = 0;
        }

        imageView.tag = index;
        //更新每一頁上的image
        [self setImageWithImageView:imageView];
        [self setImageView:imageView atIndex:index];
    }
    //更新pageControl顯示的頁碼,也就是中間那個imageview的tag值
    self.pageControl.currentPage = [_imageViews[1] tag];

    //使用無動畫的效果快速切換,也就是把scrollview的偏移量還設置成一個imageview的寬度
    //這里是通過設置scrollview的偏移量讓其來回滑動,時刻更換imageview的圖片,每換一次,就立即讓scrollview以無動畫的方式再回到偏移量為一個imageview寬度的偏移量位置,即還是顯示的中間那個imageview,以此給用戶產生一種來回切換的錯覺,實質一直是在顯示中間那個imageview
    self.scrollView.contentOffset = CGPointMake(view_WIDTH, 0);
}

  • 其他的就是在scrollview的幾個代理方法里面要么開啟定時器,要么關閉定時器,要么就是調用updateImageViewsAndPageControl方法更新圖片以及分頁控件的狀態了,這里就不再描述了,詳細的建議可以看代碼完整參考理解,另外這些是個人理解,不足之處歡迎指正,感謝支持
  • 代碼可以點擊這里查看
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容