swift ScrollView之無限輪播(待調整)
輪播是最常看到的廣告方式,但是都沒有實際想過是怎麼辦到的,希望有機會生出想做的collectionView版本。
。我的輪播有幾個Point:
1.在有限的圖片中,一直播下去不會回頭,∴達到無限輪播錯覺。
2.使用timer讓做自行運作,不需手動輪播。
3.透過人力介入,可以改變播放方向。
4.人力介入撥動不到一整頁,也可以改變播放方向。
。原理:
要在原本的照片牆頭尾都另外加上假照片,3-123-1 的方式,這樣在滑到最後一張位置四(真3)時,可以繼續滑到位置五(假1),但我們在滑到位置五(假1)時轉成位置二的(真1)
∵兩張照片相同,故使用者視覺上不會發現。
如果兩張照片不同,就會有視覺差別:如下
— Demo不同照片:
— Demo相同照片:
。動手做:
1.設置ScrollView、PageControl
在Main.storyboard 準備ScrollView,其大小與螢幕四邊貼齊。
— ScrollView
內容物大小:
寬:ScrollView寬*照片數量(+假的兩張)、高:螢幕高
起始點:
(x:距離一個ScrollView寬開始、y:0) ∵從位置二跑
Delegate:
//因為等等利用UIScrollViewDelegate的內建方法scrollView.delegate = self
並貼上UIPageControl,其isUserInteractionEnabled = false
並設定PageControl.numberOfPages = 幾張照片
PageControl.currentPage = 0 設定當前為第一個點點
(也可以用Programmatically方式設定)
2.設置ImageView
Concept:將準備的照片名稱放進array,把他們做成UIImageView丟進ScrollView裡面
創建一組3 -123–1 。。N+2 個imageView
— 每張UIImageView大小
(x:該照片距離幾個螢幕寬、y:0、寬:螢幕寬、高:螢幕高)
頭尾的照片放自訂的,其他按照順序生成放置
— 加入scrollView
scrollView.addSubview(imageView)
(也可以在Main.storyboard手動設定,可利用stackView讓他們更整齊)
3. 滑動事件
Concept:利用UIScrollViewDelegate的方法scrollViewDidScroll( )偵測滑動事件。
此事件不管是timer觸發ScrollView移動還是手動滑動,皆會被偵測到
— 利用假照片來動手腳:
。如果滑到位置一(假3) 需轉成 位置四(真3),故為三張寬間距
if offsetX == 0 { let contentOffsetMinX = scrollViewWidth * CGFloat(imageNames.count) adScrollView.contentOffset = CGPoint(x: contentOffsetMinX, y: 0) //此時需記錄更改後的最後位置,待會使用
lastTimeOffsetX = contentOffsetMinX
}
/
。如果滑到位置五(假1) 需轉成 位置二(真1) ,故為一張寬間距
if offsetX == scrollViewWidth * CGFloat(imageNames.count + 1) { adScrollView.contentOffset = CGPoint(x: scrollViewWidth, y: 0) //此時需記錄更改後的最後位置,待會使用
lastTimeOffsetX = scrollViewWidth
}
如果 lastTimeOffset直接取用ScrollView.contentOffset,有可能來不及變動就取到,可能會是錯誤的位置。
/
。平時滑動時,計算頁數,變動PageControl的currentPage頁碼
//但因為位置一(假3)不列入頁數計算,故需要減一,否則頁數會有誤
let page = round(scrollView.contentOffset.x / scrollViewWidth) — 1adPageControl.currentPage = Int(page)
/
=以上動手腳都不能用setContentOffset(_:),因為自帶動畫效果,會被發現
4.Timer設置
Concept:
— timer生成前:
不外乎先判斷是否已經生成,當然被生成過就停掉它
確認當下沒有timer時,再生成新的timer
/
— timer生成後:
記得離開頁面時,停掉timer計時器
Timer 如同執行緒,若沒停止的話,是會一直在背景執行的,所以必須在離開畫面時去停止它作法:
生成Timer,並每幾秒觸發滑動func,repeats: true
觸發func autoScroll( )
宣告一個變數,用來儲存當前間隔距離
//預設viewDidAppear( )後,畫面往右邊行走,故我間距初始值為1(位置二)
var index = 1 //間距
每次timer呼叫時,
— 右邊:間距數都加1
— 左邊:間距數都減1
case .right: //當i跑到間距四個時,也就是第五張圖,從間距2開始跑 if index == imageNames.count + 1 { index = 2 } else { index += 1 }
— 右邊
平時每次+1 ,
終究會加到位置五(間距4格)=位置二,下一張為位置三(間距2格)
故間距又回到2開始累加
case .left: //當跑到位置0時,也就是要從間距2開始跑 if index == 0 { index = imageNames.count — 1 } else { index -= 1 }
— 左邊
平時每次-1,
終究會扣到位置一(間距0格)=位置四,下一張為屁股數來第二張,count-1(間距2格)
故間距又回到2開始累扣
adScrollView.setContentOffset(CGPoint(x: scrollViewWidth * CGFloat(index), y: 0), animated: true)//此時需記錄更改後的最後位置,待會使用
lastTimeOffsetX = scrollViewWidth * CGFloat(index)
此時將scrollView位置每次改變(x:螢幕寬*間距數),用動畫效果方式轉換位置比較好看
/
如果沒有用動畫的方式,會沒有滑動的效果
adScrollView.contentOffset = CGPoint(x: scrollViewWidth * CGFloat(spacing), y: 0)
5.當手動介入時
Concept:需要暫停timer,直到手離開畫面時,重新啟動timer,
手動介入若有方向變動,須記錄方向,讓timer知道下次往哪個方向啟動
手動介入觸發func 順序//in scrollViewWillBeginDragging//in scrollViewDidScroll//in scrollViewWillEndDragging 將開始停止拖拽的時候執行//in scrollViewDidEndDragging 停止拖拽的時候開始執行//in scrollViewDidEndDecelerating 减速停止的時候開始執行
故需要在
— scrollViewWillBeginDragging 時,直接終止timer(我發現沒有暫停的func,只能整個Stop掉)
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { //print(“\(scrollView.contentOffset.x)”) stopTimer()}
— 將在即將結束拖曳or結束拖曳時,將位置與上次最後位置相比,取得方向
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
//print(“\(scrollView.contentOffset.x)”)
guard let lastTimeOffsetX = lastTimeOffsetX else { return } let currentOffsetX = scrollView.contentOffset.x //若有改變方向,變動方向註記 if currentOffsetX < lastTimeOffsetX { direction = .left //往左跑 } else { direction = .right //往右跑 } }
— 在位移結束後,再啟動timer,並重計當下頁數,紀錄最後位置
timer若在位移結束前啟動,會發生跳頁的狀況,因為他已經偷偷先幫你數好了
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { //print(“\(scrollView.contentOffset.x)”)
let contentOffsetMinX = scrollView.contentOffset.x index = Int(contentOffsetMinX / screenWidth)
//重新計算頁數 lastTimeOffsetX = scrollView.contentOffset.x
//若最後真的有換頁,再重新assign進去 startTimer()}
。Demo:
可以幫我點個讚,或是分享一杯咖啡支持我。
。參考文件:
。彼得潘飛哪裡,我就飛哪裡~
(他說他想在文章裡面出現)
**因為gist 貼上來顯示不出來,我只好用截圖的方式貼了0.0