swift:無限圖片輪播器
圖片輪播器用處很廣,什么廣告投放呀,新聞頭條滾動之類的,都是使用它。出于學習的目的用swift基于UICollectionView開發了一個無限循環的圖片輪播器。附上Github源碼以及素材地址CyclePictureView。下面是演示
適合人群
用swift寫了一個無限圖片輪播器,適合初學者。為了方便大家一步步自己開發,已經把開發過程中的每一個階段都用了一個工程,當然你也可以使用git回滾。而且在功能基本完成后,花了很大的心思對代碼進行重構,讓代碼更加符合swift的編程模式。

編碼
1.完成本地圖片的顯示
- 創建CyclePictureView.swift這是我們圖片輪播器主要的view,在這一階段,我僅僅只是利用UIColleciotnView的流水布局效果達成了一個簡單的效果。
- 創建CyclePictureCell.swift。這個是用于UIColleciotnView的cell,它代表顯示的每一張圖片
- 創建LocalPictureController.swift,用于測試
這一階段十分簡單,大家參照源碼1.完成本地圖片的顯示工程應該不會有問題
2.完成網絡圖片的顯示
- 添加了網絡圖片加載的功能,這里我沒有自己重寫圖片加載過程直接調用第三方框架Kingfisher,這個框架和oc的SDWebImage十分類似。其實事后我才發現Kingfisher跟SDWebImage的差距還是有很大。如果想使用其他框架列如SDWebImage只需將CyclePictureCell中設置圖片的kf_setImageWithURL改成sd_setImageWithURL。
- 創建WebPictureController.swift,用于對網絡數據的測試,我提供了一個Image.plist文件,里面是一些網絡圖片的url和圖片對應的描述。
這一階段如果是有經驗的開發者就對我實現這兩種圖片加載的方式感到不爽了,因為這里是可以對代碼進行重構的。當然,對于初學者的我們,一定要以實現功能為主如果在項目一開始就考慮代碼這樣寫不好,那樣寫不好。那么你永遠可能也邁不出項目第一步,會嚴重拖緩開發進度。所以我們先實現功能,對于代碼的重構,一個階段一個階段的來,或者說你啥時候實在忍不了了,那就整理代碼吧。
3.完成文字顯示,并暴露一些接口
- 添加了圖片的描述文字,并暴露了一些接口。因為我們開發這個圖片輪播器是想以后自己能夠用到,而往往對于不同規格的圖片輪播器,我們對于文字的大小什么的可能會不一樣。所以這些屬性必須提供給用戶,讓使用者能夠自己定制。
這里需要注意一點,父控件對于子控件的布局操作最好是放在layoutSubviews,因為只有在layoutSubviews這個方式里面才能保證子控件在你希望的地方顯示。這個方法在控件最終顯示前會調用一次進行最后的布局,當然也不僅僅是這時候才調用,附上layoutSubviews調用時機.
1、init初始化不會觸發layoutSubviews
2、addSubview會觸發layoutSubviews
3、設置view的Frame會觸發layoutSubviews,當然前提是frame的值設置前后發生了變化
4、滾動一個UIScrollView會觸發layoutSubviews
5、旋轉Screen會觸發父UIView上的layoutSubviews事件
6、改變一個UIView大小的時候也會觸發父UIView上的layoutSubviews事件
舉個例子,就拿本項目來說,CyclePictureCell的子控件有用于顯示圖片的imageVIew和用于顯示文字的UILabel,那么我對這兩個控件的大小以及位置的設置,最好放在CyclePictureCell的layoutSubviews方法。ps:layoutSubviews一定得調用super.layoutSubviews()
4.完成無限滾動
- 完成了無限滾動,其思路說來就是讓我們顯示的cell"變多"。
假如我們有5張圖片,UICollectionView將會準備用5個cell進行顯示(當然因為循環利用的機制在,所以創建出的cell并不一定是五個)。那么當我們顯示到第五張圖片的時候,如果要繼續顯示那么,應該顯示第一張圖片。這時候如果強制的用UICollciontVIew的方法強制拉回第一個cell,會有一個非常難看的向左滑動的動畫,而不是繼續向右滾動的動畫。
為了實現無限滾動,我只能把cell的數量變多,在五張圖片下,我讓cell有500個或者更多,但是cell顯示的圖片卻還是只有我們設置的那五張。并且在第一次顯示的時候,讓顯示是cell是最中間的一張。這樣,就照成了無限循環的假象。而且由于循環利用的機制的存在,是不用擔心效率問題的。
這里定時器的問題也需要額外注意一下。反正要又這種意識,只要用到了定時器就要擔心循環引用的問題。我的習慣是在開發的過程中,把所有用到的控制器也好,自定義的view也好。我總會加上它的析構方法deinit。
5.添加PageControl
- 這里我添加了UIPageControlPageControl控件。同樣值得注意的是它的frame應該在ayoutSubviews里面設置。
到達這一步,其實大概功能也都差不多了。而且代碼量也有300多行了。是可以考慮對代碼進行整理了(其實是我實在不能忍了)。
6整理代碼,重構圖片存取方式
將UICollectionView的數據源和代理全部放進了擴展里面。這其實也是沒辦法的事情,我總覺得讓一個View充當數據源和代理是不符合思維邏輯的,但又找不到其他解決方式,只要放進擴展。這樣讓CyclePictureView這個類的本體顯得沒那么臃腫
創建AuxiliaryModels.swift文件。加載怎樣的數據,以及怎樣的加載數據應該不是我們的CyclePictureView關心的事情。所以CyclePictureView的職責只要把圖片的數據源(url、名字)存儲起來,并在cell需要的時候給他就好,至于怎么樣顯示加載,那是cell的事情。所以我利用ImageBox來存儲圖片的數據源。這里不得不感嘆swift中枚舉的強大。然后在下一步中,將感受到swift2.0中協議的強大。
7.添加pageControl對齊模式,并且將無限滾動功能變成協議
首先我是先對pageControl的功能進行了一次增強,讓它的位置能夠被用戶所選擇。于是我立即定義了一個枚舉PageControlAliment來表示能夠支持的對齊方式。剩下的操作無疑是在layoutSubviews中根據不同的枚舉計算不同的位置。
PageControlAlimentProtocol:本來完成對齊模式應該分開一個工程再寫的,請原諒這里我靈感來的太突然,停不下來了。我的思路是這樣的:我是給pageControl添加了一個功能,然而這個功能對于CyclePictureView來說可以有也可以沒有。而且這個功能也不僅僅能夠用于CyclePictureView這個類,似乎是所有的擁有有UIPageControl這個控件的類(視圖),都可以有這個設置UIPageControl位置的功能。于是我想到了使用協議。我要為這個功能設計一個協議,叫做PageControlAlimentProtocol,一個對齊協議。任何一個View,你只要想方便的調整自己擁有UIPageControl,只要遵守這個協議,協議便能夠幫你調節控件位置。于是協議我定義成這樣。
protocol PageControlAlimentProtocol: class{
var pageControlAliment: PageControlAliment {get set}
func AdjustPageControlPlace(pageControl: UIPageControl)
}
一個屬性,便是開始定義的位置類型的枚舉,要使用本協議,必須得提供這個屬性。還有協議一個方法,是調節位置的協議方法。這里我在協議的擴展中給了它一個默認的實現。用戶在任何需要改變位置的代碼出直接調用即可。
- EndlessCycleProtocol:這個協議的思路和上面那個協議幾乎一樣。不過這個協議抽取出來感覺有點牽強。不過代碼確實是簡潔了。這里希望大牛們能夠指點
8.進一步改進代碼,并添加對storyboard的支持
- 首先是增加了CyclePictureView的代理協議CyclePictureViewDelegate,其實就是一個簡單的點擊事件的傳遞。
- 改進了一些代碼的邏輯。
- 增加了對storyboard的支持,這里折騰了挺久,在從storyboard中加載的時候,不知道為什么會有莫名的偏移,找了好久才找到原因。
總結
在編碼過程中,我盡量多的使用swift的特性,代碼寫到第五步感覺和以前oc的思維都差不多。于是我強迫自己對代碼進行修改。結果雖然是寫出來了不一樣的代碼,但是不知道有沒有過度重構,希望大家能夠指出不足之處。當然這個CyclePictureView圖片輪播器是可以拿到任何項目中去使用的。如果您發現任何BUG,或者有更好的建議或者意見,歡迎您的指出。郵箱:wxl19950606@163.com.感謝您的支持。