大概的實(shí)現(xiàn)思路有這么幾種:
1 .UIScrollView
+UIImageView
1.1. 使用比 圖片數(shù) 多 2 個(gè)的UIImageView
1.2 使用三個(gè)UIImageView
實(shí)現(xiàn)
1.3 使用 兩個(gè)UIImageView
實(shí)現(xiàn)
2.UICollectionView
(當(dāng)然UICollectionView
也是繼承自UIScrollView
)
2.1 使用UICollectionView
的復(fù)用特性,復(fù)制 多份 圖片作為數(shù)據(jù)源,從中間開始顯示
2.2 在 第一張圖片前加 上最后一張圖片,在最后一張圖片前加上第一張圖片
注意:
1.
記得設(shè)置scrollView.isPagingEnabled = true
2.
控制UIScrollView
回滾的時(shí)候 不要使用動(dòng)畫,否則會(huì)調(diào)用代理方法
1.1 使用比 圖片數(shù) 多 2 個(gè)的 UIImageView
實(shí)現(xiàn)
思路
: 創(chuàng)建 比圖片數(shù)多 2個(gè)UIImageView
,最一個(gè)ImageView
顯示第一張圖片,第一個(gè)ImageView
顯示最后一張圖片,從第二個(gè)到倒數(shù)第二個(gè)ImageView
依次顯示第一張到最后一張圖片 當(dāng)滾動(dòng)到最后一張圖片, 即 :原始 圖片順序?yàn)?1,2,3,4,5,6
,則八個(gè)ImageView
顯示圖片的順序?yàn)?code>6,1,2,3,4,5,6,1,依次把ImageView
添加到UIScrollView
上,最開始顯現(xiàn)第二個(gè)ImageView
,當(dāng)滾動(dòng)到第一個(gè) ImageView
的時(shí)候,控制UIScrollView
滾動(dòng)到倒數(shù)第二個(gè) ImageView
,滾動(dòng)到最后一個(gè) ImageView
的時(shí)候,控制UIScrollView
滾到第二個(gè)ImageView
,只有 中間的 是正式用來顯示的.兩頭的用來實(shí)現(xiàn)一個(gè)視差效果
核心代碼如下
private lazy var mainScrollView : UIScrollView = {
let scrollView = UIScrollView(frame: bounds)
let scrollVWidth = bounds.width * CGFloat(images.count + 2)
scrollView.contentSize = CGSize(width: scrollVWidth, height: bounds.height)
scrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
scrollView.delegate = self
scrollView.bounces = true
scrollView.isPagingEnabled = true
for (index, imageV) in imageVs.enumerated() {
imageV.frame = CGRect(x: bounds.width * CGFloat(index), y: 0, width: bounds.width, height: bounds.height)
scrollView.addSubview(imageV)
}
return scrollView
}()
// images 為根據(jù)傳 過來的圖片再在頭尾分別加上最后一張和第一張圖片創(chuàng)建的新數(shù)組
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x >= (bounds.width * CGFloat(images.count - 1)) {
mainScrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
}
if scrollView.contentOffset.x <= 0 {
mainScrollView.setContentOffset(CGPoint(x: bounds.width * CGFloat(images.count - 2), y: 0), animated: false)
}
}
1.2 使用三個(gè)UIImageView
實(shí)現(xiàn)
思路: 方法1.1
的進(jìn)化版,方法1.1
在有大量圖片的情況下,需要?jiǎng)?chuàng)建大量的ImageView
明顯不合適,所以我們 把中間的一個(gè)ImageView
片來代替 1.1
中的 大量中間圖片,即只有中間一張圖片是用來正式顯示圖片的,每次滾動(dòng)結(jié)束都需要滾回到 中間圖片位置,兩邊的ImageView
用來實(shí)現(xiàn)視差效果
核心代碼如下
// 這里為每個(gè) ImageView 添加一個(gè) tag ,用來標(biāo)識(shí)當(dāng)前顯示的圖片和后面計(jì)算將要顯示的圖片
private lazy var leftImageV : UIImageView = {
let imageV = UIImageView(frame: bounds)
imageV.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)
imageV.tag = images.count - 1
imageV.image = images[images.count - 1]
return imageV
}()
private lazy var MidleImageV : UIImageView = {
let imageV = UIImageView(frame: bounds)
imageV.frame = CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height)
imageV.tag = 0
imageV.image = images[0]
return imageV
}()
private lazy var RightImageV : UIImageView = {
let imageV = UIImageView(frame: bounds)
imageV.frame = CGRect(x: bounds.width * 2, y: 0, width: bounds.width, height: bounds.height)
imageV.tag = 1
imageV.image = images[1]
return imageV
}()
private lazy var imageVs : Array<UIImageView> = {
return [leftImageV,RightImageV,MidleImageV];
}()
private lazy var mainScrollView : UIScrollView = {
let scrollView = UIScrollView(frame: bounds)
let scrollVWidth = images.count >= 3 ? bounds.width * 3 : bounds.width * CGFloat(images.count)
scrollView.contentSize = CGSize(width: scrollVWidth, height: bounds.height)
scrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
scrollView.delegate = self
scrollView.bounces = true
scrollView.isPagingEnabled = true
scrollView.addSubview(leftImageV)
scrollView.addSubview(MidleImageV)
scrollView.addSubview(RightImageV)
return scrollView
}()
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
//判斷 滾動(dòng)方向,右滾下標(biāo) +1 ,左滾下標(biāo) -1
var direction : Int = 0
if scrollView.contentOffset.x > bounds.width {
direction = 1
}else if scrollView.contentOffset.x < bounds.width{
direction = -1
}
let imageVs = [leftImageV,MidleImageV,RightImageV]
// 根據(jù)圖片tag 和滾動(dòng)方向 計(jì)算 要顯示的 圖片,更新 tag 值
for imageV in imageVs {
var index = imageV.tag + direction
if index == images.count {
index = 0
}else if index == -1{
index = images.count - 1
}
imageV.image = images[index]
imageV.tag = index
}
mainScrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
}
1.3 使用 兩個(gè) UIImageView
實(shí)現(xiàn)
思路:
UIScrollView
的 contentSize
依然 為 三倍 屏幕寬度,創(chuàng)建兩個(gè) ImageView
添加到 UIScrollView
上, 沒有滾動(dòng)時(shí),兩個(gè)imageView
重疊,上面的ImageView
顯示當(dāng)前圖片,發(fā)生滾動(dòng)的時(shí)候,判斷滾動(dòng)方向,把底下的ImageView
移到 左邊或者右邊,滾動(dòng)結(jié)束,UIScrollView
滾動(dòng) 回 中間位置
核心代碼如下:
private var isChange : Bool = true
let images : Array<UIImage>
lazy var bottomImageView : UIImageView = {
let imageView = UIImageView(image: UIImage(named: "2.png"))
imageView.frame = CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height)
imageView.tag = 0
return imageView
}()
lazy var topImageView : UIImageView = {
let imageView = UIImageView(image: UIImage(named: "1.png"))
imageView.frame = CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height)
imageView.tag = 0
return imageView
}()
lazy var scrollView : UIScrollView = {
let scroView = UIScrollView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height))
scroView.contentSize = CGSize(width: bounds.width * 3, height: bounds.height)
scroView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
scroView.isPagingEnabled = true
scroView.bounces = false
scroView.delegate = self
scroView.addSubview(bottomImageView)
scroView.addSubview(topImageView)
return scroView
}()
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
imageOffset = scrollView.contentOffset.x
}
// 每次滾動(dòng)的時(shí)候 判斷 滾動(dòng)方向,調(diào)整底下的 imageview 的位置和顯示的圖片
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if isChange{
if scrollView.contentOffset.x < bounds.width {
imageOffset = scrollView.contentOffset.x
var tag = bottomImageView.tag - 1
if tag < 0{
tag = images.count - 1
}
bottomImageView.image = images[tag]
bottomImageView.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)
bottomImageView.tag = tag
}else if scrollView.contentOffset.x > bounds.width {
imageOffset = scrollView.contentOffset.x
var tag = bottomImageView.tag + 1
if tag == images.count{
tag = 0
}
bottomImageView.image = images[tag]
bottomImageView.frame = CGRect(x: bounds.width * 2, y: 0, width: bounds.width, height: bounds.height)
bottomImageView.tag = tag
}
}
isChange = false
}
//滾動(dòng)結(jié)束, scrollView 回滾到中間位置,更新 上面的 ImageView 的圖片
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
bottomImageView.frame = CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height)
topImageView.frame = CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height)
print(bottomImageView.tag)
topImageView.image = images[bottomImageView.tag]
scrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
isChange = true
}
這里 因?yàn)?
scrollViewDidScroll
代理滾動(dòng)過程中會(huì)一直調(diào)用,而我這里只需要判斷 一下滾動(dòng)方向然后調(diào)整 底下的ImageView
, 所以用的 一個(gè)BOOL
值來限制執(zhí)行,當(dāng)時(shí)沒想到一個(gè)比較好的方案,所以實(shí)現(xiàn)看起來比較low
2.1 使用UICollectionView
的復(fù)用特性,復(fù)制 多份 圖片作為數(shù)據(jù)源,從中間開始顯示
思路:
復(fù)制 多份 需要顯示的圖片 作為UICollectionView
數(shù)據(jù)源,開始的時(shí)候 就把scrollView
滾動(dòng)到中間那份 數(shù)據(jù)的開頭位置,因?yàn)閺?fù)制的多份數(shù)據(jù),所以滾動(dòng)的時(shí)候就會(huì)無限輪播的效果,這里就不貼具體代碼,大家可以自行 嘗試下
2.2 在 第一張圖片前加 上最后一張圖片,在最后一張圖片前加上第一張圖片
思路:
和1.1
方法類似,只是使用 UICollectionView
的話因?yàn)?code>UICollectionView自身的復(fù)用機(jī)制,所以沒有 1.1
方法 創(chuàng)建大量 ImageView
的性能問題,同樣的,在首尾 分別加上最后和第一張圖片,當(dāng) 滾動(dòng)到這兩個(gè)位置的時(shí)候,控制 scrollView
滾動(dòng)到中間顯示區(qū)的對(duì)應(yīng)位置
核心代碼如下:
let images : Array<UIImage>
//創(chuàng)建 UICollectionView
lazy var collectionV : UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0
layout.itemSize = CGSize(width: bounds.width, height: bounds.height)
layout.scrollDirection = .horizontal
let collectionV = UICollectionView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height), collectionViewLayout: layout)
collectionV.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
collectionV.dataSource = self
collectionV.delegate = self
collectionV.isPagingEnabled = true
return collectionV
}()
//根據(jù)傳進(jìn)來的 圖片數(shù)組構(gòu)建 數(shù)據(jù)源數(shù)組
init(frame: CGRect,images :Array<UIImage> ) {
var tempImages : Array<UIImage> = [images[images.count - 1]]
for image in images {
tempImages.append(image)
}
tempImages.append(images[0])
self.images = tempImages
super.init(frame: frame)
addSubview(collectionV)
collectionV.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
}
extension XlXBannerCollectionV :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.imageV = UIImageView(image: images[indexPath.row])
return cell
}
// 滾動(dòng)到 首尾的時(shí)候控制回滾
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if scrollView.contentOffset.x >= (bounds.width * CGFloat(images.count - 1)) {
collectionV.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
}
if scrollView.contentOffset.x <= 0 {
collectionV.setContentOffset(CGPoint(x: bounds.width * CGFloat(images.count - 2), y: 0), animated: false)
}
}
}
無限輪播 整體 上就是 中間部分 是用來正式顯示的,首尾的是 在滾動(dòng)的時(shí)候讓用戶有種 后面還有內(nèi)容的錯(cuò)覺,當(dāng)滾動(dòng)停止,立馬回滾到中間顯示區(qū)的對(duì)應(yīng)位置,來達(dá)到無限輪播的視覺效果
自動(dòng)輪播的話在
scrollView
代理方法里面合適的位置開啟或暫停定時(shí)器來實(shí)現(xiàn),代碼中剔除了其他干擾只保留了核心部分,因?yàn)楣δ鼙容^簡(jiǎn)單,所以也沒注意代碼衛(wèi)生,實(shí)際開發(fā)中,各位小伙伴們還是要多多注意下代碼習(xí)慣,抽取封裝,配置工具類什么的來實(shí)現(xiàn)的漂亮一點(diǎn)
最后:寫完了才想起來兩張圖片的情況下沒做處理,可能還是會(huì)有些問題吧,道理都是一樣的,實(shí)際開發(fā)中多多注意下