iOS-無限迴圈輪播器(註釋詳細到沒有之一)
阿新 • • 發佈:2019-02-01
Bg:
1)有一段時間沒有寫文章了,最近事兒比較多,今天有人在技術群裡面問我使用UIScrollview實現無限迴圈輪播的思想(3個UIImageView實現),我當時給了他一篇部落格,不過好像這位朋友看的不是很懂,所以我寫了一個小Demo打算寫這篇文章去講解下,幫助有需要的朋友們,所以我儘量把能寫的註釋都寫詳細了,把思想寫全面了,讓大家看一遍基本就明白了,裡面關於如何好的封裝一個控制元件的細節這裡就不實現了,以講解實現為主哦
2)另外這種迴圈利用的思想也是面試可能會問到的,說不定還是加分項哦
3)另外這個文章說的是3個UIImageView,其實2個imageview完全可以實現(點選這裡看2個imageview實現
- 先看下效果圖
kobe.gif
- 這個效果圖也沒什麼特別,大家都看到過無數次了,包括這個無線輪播,大家想必也都瞭解過,所以這次我們不實現什麼特別的效果,主要是通過這個小功能,給有需要的朋友講解下無線輪播思想
使用3個imageview實現無線輪播的大致原理
- 將3個imageview新增到scrollview上面,scrollview的
contensize
是3個imageview的寬度
,設定scrollview一開始初始的偏移量為一個imageview寬度
- 使用3個
imageview
來回更換圖片,並在每一次更換圖片後立即再設定scrollview偏移量還為一個imagview的寬度,也就是讓scrollview滾動後再滾回原來預設的位置,這樣就可以達到始終顯示中間那個imageview的效果 - 看到過其他部落格裡面有這樣描述過這個原理
ps:例如要使用三個UIImageView迴圈顯示5張圖片 1)由於中間的imageview是顯示在螢幕上的,它需要在啟動時預設顯示第1張圖片,那麼左邊的imagview 自然就需要顯示最後一張圖片,右邊的imagview自然要顯示第二張圖片了.所以一開始肯定預設放圖片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
方法更新圖片以及分頁控制元件的狀態了,這裡就不再描述了,詳細的建議可以看程式碼完整參考理解,另外這些是個人理解,不足之處歡迎指正,感謝支援 - 程式碼可以點選這裡檢視