生活随笔
收集整理的這篇文章主要介紹了
RxSwift之UI控件UICollectionView扩展的使用
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一、基本用法
① 單個分區(qū)的集合視圖
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.sectionInset
= UIEdgeInsets.init(top
: 0, left: 5, bottom
: 0, right: 5)
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.view
.addSubview(self.collectionView
!)
let items
= Observable.just(["Swift","PHP","Ruby","Java","C++",])
items
.bind(to
: collectionView
.rx
.items
) { (collectionView
, row
, element
) inlet indexPath
= IndexPath(row
: row
, section
: 0)let cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(row):\(element)"return cell
}.disposed(by
: disposeBag
)
② 單元格選中事件響應(yīng)
- 如下所示,當(dāng)點(diǎn)擊某個單元格時將其索引位置,以及對應(yīng)的標(biāo)題打印出來:
選中項的indexPath為:
[0, 0]
選中項的標(biāo)題為:
Swift
- 如果業(yè)務(wù)代碼直接放在響應(yīng)方法內(nèi)部,可以如下實現(xiàn):
collectionView
.rx
.itemSelected
.subscribe(onNext
: { indexPath
inprint("選中項的indexPath為:\(indexPath)")
}).disposed(by
: disposeBag
)
collectionView
.rx
.modelSelected(String.self).subscribe(onNext
: { item
inprint("選中項的標(biāo)題為:\(item)")
}).disposed(by
: disposeBag
)
- 也可以在響應(yīng)中調(diào)用外部的方法實現(xiàn):
collectionView
.rx
.itemSelected
.subscribe(onNext
: { [weak self] indexPath
inprint("選中項的indexPath為:\(indexPath)")
}).disposed(by
: disposeBag
)
collectionView
.rx
.modelSelected(String.self).subscribe(onNext
: {[weak self] item
inprint("選中項的標(biāo)題為:\(item)")
}).disposed(by
: disposeBag
)
- 如果想要同時獲取選中項的索引以及內(nèi)容,可以如下實現(xiàn):
Observable.zip(collectionView
.rx
.itemSelected
, collectionView
.rx
.modelSelected(String.self)).bind
{ [weak self] indexPath
, item
inprint("選中項的indexPath為:\(indexPath)")print("選中項的標(biāo)題為:\(item)")}.disposed(by
: disposeBag
)
③ 單元格取消選中事件響應(yīng)
被取消選中項的indexPath為:
[0, 4]
被取消選中項的的標(biāo)題為:C
++
collectionView
.rx
.itemDeselected
.subscribe(onNext
: { [weak self] indexPath
inprint("被取消選中項的indexPath為:\(indexPath)")
}).disposed(by
: disposeBag
)
collectionView
.rx
.modelDeselected(String.self).subscribe(onNext
: {[weak self] item
inprint("被取消選中項的的標(biāo)題為:\(item)")
}).disposed(by
: disposeBag
)
Observable.zip(collectionView
.rx
.itemDeselected
, collectionView
.rx
.modelDeselected(String.self)).bind
{ [weak self] indexPath
, item
inprint("被取消選中項的indexPath為:\(indexPath)")print("被取消選中項的的標(biāo)題為:\(item)")}.disposed(by
: disposeBag
)
④ 單元格高亮完成后的事件響應(yīng)
高亮單元格的indexPath為:
[0, 3]
collectionView
.rx
.itemHighlighted
.subscribe(onNext
: { indexPath
inprint("高亮單元格的indexPath為:\(indexPath)")
}).disposed(by
: disposeBag
)
⑤ 高亮轉(zhuǎn)成非高亮完成的事件響應(yīng)
失去高亮的單元格的indexPath為:
[0, 3]
collectionView
.rx
.itemUnhighlighted
.subscribe(onNext
: { indexPath
inprint("失去高亮的單元格的indexPath為:\(indexPath)")
}).disposed(by
: disposeBag
)
⑥ 單元格將要顯示出來的事件響應(yīng)
將要顯示單元格indexPath為:
[0, 0]
將要顯示單元格cell為:
<RxSwift.MyCollectionViewCell: 0x7fe0f1112490; baseClass
= UICollectionViewCell; frame
= (5 0; 126 70); layer
= <CALayer: 0x6000006f1a40>>將要顯示單元格indexPath為:
[0, 1]
將要顯示單元格cell為:
<RxSwift.MyCollectionViewCell: 0x7fe0f0706be0; baseClass
= UICollectionViewCell; frame
= (144 0; 126 70); layer
= <CALayer: 0x6000006f6360>>將要顯示單元格indexPath為:
[0, 2]
將要顯示單元格cell為:
<RxSwift.MyCollectionViewCell: 0x7fe0f1304420; baseClass
= UICollectionViewCell; frame
= (283 0; 126 70); layer
= <CALayer: 0x6000006bd900>>將要顯示單元格indexPath為:
[0, 3]
將要顯示單元格cell為:
<RxSwift.MyCollectionViewCell: 0x7fe0f1304e40; baseClass
= UICollectionViewCell; frame
= (5 80; 126 70); layer
= <CALayer: 0x6000006bd960>>將要顯示單元格indexPath為:
[0, 4]
將要顯示單元格cell為:
<RxSwift.MyCollectionViewCell: 0x7fe0f1113c50; baseClass
= UICollectionViewCell; frame
= (144 80; 126 70); layer
= <CALayer: 0x6000006f18a0>>
collectionView
.rx
.willDisplayCell
.subscribe(onNext
: { cell
, indexPath
inprint("將要顯示單元格indexPath為:\(indexPath)")print("將要顯示單元格cell為:\(cell)\n")
}).disposed(by
: disposeBag
)
⑦ 分區(qū)頭部或尾部將要顯示出來的事件響應(yīng)
collectionView
.rx
.willDisplaySupplementaryView
.subscribe(onNext
: { view
, kind
, indexPath
inprint("將要顯示分區(qū)indexPath為:\(indexPath)")print("將要顯示的是頭部還是尾部:\(kind)")print("將要顯示頭部或尾部視圖:\(view)\n")
}).disposed(by
: disposeBag
)
二、RxDataSources
① 單分區(qū)的 CollectionView
- 主視圖控制器里的功能實現(xiàn)有如下兩種寫法:
-
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.sectionInset
= UIEdgeInsets.init(top
: 0, left: 5, bottom
: 0, right: 5)
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.view
.addSubview(self.collectionView
!)
let items
= Observable.just([SectionModel(model
: "", items
: ["Swift","PHP","Python","Java","javascript","C#"])])
let dataSource
= RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
}
)
items
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.sectionInset
= UIEdgeInsets.init(top
: 0, left: 5, bottom
: 0, right: 5)
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.view
.addSubview(self.collectionView
!)
let sections
= Observable.just([MySection(header
: "", items
: ["Swift","PHP","Python","Java","javascript","C#"])])
let dataSource
= RxCollectionViewSectionedReloadDataSource<MySection>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
}
)
sections
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
struct MySection {var header
: Stringvar items
: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity
: String {return header
}init(original
: MySection, items
: [Item]) {self = original
self.items
= items
}
}
- 注意:RxDataSources 是以 section 來做為數(shù)據(jù)結(jié)構(gòu)的,因此不管 collectionView 是單分區(qū)還是多分區(qū),在使用 RxDataSources 的過程中,都需要返回一個 section 的數(shù)組。
② 多分區(qū)的 CollectionView
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
flowLayout
.headerReferenceSize
= CGSize(width
: self.view
.frame
.width
, height
: 40)
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.collectionView
.register(MySectionHeader.self,forSupplementaryViewOfKind
: UICollectionView.elementKindSectionHeader
,withReuseIdentifier
: "Section")
self.view
.addSubview(self.collectionView
!)
let items
= Observable.just([SectionModel(model
: "腳本語言", items
: ["Python","javascript","PHP",]),SectionModel(model
: "高級語言", items
: ["Swift","C++","Java","C#"])])
let dataSource
= RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
},configureSupplementaryView
: {(ds
,cv
, kind
, ip
) inlet section
= cv
.dequeueReusableSupplementaryView(ofKind
: kind
,withReuseIdentifier
: "Section", for: ip
) as! MySectionHeadersection
.label
.text
= "\(ds[ip.section].model)"return section
})
items
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
flowLayout
.headerReferenceSize
= CGSize(width
: self.view
.frame
.width
, height
: 40)
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.collectionView
.register(MySectionHeader.self,forSupplementaryViewOfKind
: UICollectionView.elementKindSectionHeader
,withReuseIdentifier
: "Section")
self.view
.addSubview(self.collectionView
!)
let sections
= Observable.just([MySection(header
: "腳本語言", items
: ["Python","javascript","PHP",]),MySection(header
: "高級語言", items
: ["Swift","C++","Java","C#"])])
let dataSource
= RxCollectionViewSectionedReloadDataSource<MySection>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
},configureSupplementaryView
: {(ds
,cv
, kind
, ip
) inlet section
= cv
.dequeueReusableSupplementaryView(ofKind
: kind
,withReuseIdentifier
: "Section", for: ip
) as! MySectionHeadersection
.label
.text
= "\(ds[ip.section].header)"return section
})
sections
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
struct MySection {var header
: Stringvar items
: [Item]
}extension MySection : AnimatableSectionModelType {typealias Item = Stringvar identity
: String {return header
}init(original
: MySection, items
: [Item]) {self = original
self.items
= items
}
}
三、刷新集合數(shù)據(jù)
① 刷新數(shù)據(jù)
- 如下所示:
-
- 界面初始化完畢后,collectionView 默認(rèn)會加載一些隨機(jī)數(shù)據(jù);
-
- 點(diǎn)擊右上角的刷新按鈕,collectionView 會重新加載并顯示一批新數(shù)據(jù);
-
- 為方便演示,每次獲取數(shù)據(jù)不是真的去發(fā)起網(wǎng)絡(luò)請求,而是在本地生成后延遲 2 秒返回,模擬這種異步請求的情況。
let flowLayout
= UICollectionViewFlowLayout()
flowLayout
.itemSize
= CGSize(width
: (SCREEN_WIDTH-50)/3.0, height
: 70)
flowLayout
.headerReferenceSize
= CGSize(width
: self.view
.frame
.width
, height
: 40)
self.collectionView
= UICollectionView(frame
: CGRect.init(x
: 0, y
: 88, width
: SCREEN_WIDTH, height
: SCREEN_HEIGHT - 88),collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.view
.addSubview(self.collectionView
!)
let randomResult
= self.refreshButton
.rx
.tap
.asObservable().startWith(()).flatMapLatest(getRandomResult
).share(replay
: 1)
let dataSource
= RxCollectionViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
}
)
randomResult
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
func getRandomResult() -> Observable<[SectionModel<String, Int>]> {print("正在請求數(shù)據(jù)......")let items
= (0 ..< 5).map {_ inInt(arc4random_uniform(100000))}let observable
= Observable.just([SectionModel(model
: "S", items
: items
)])return observable
.delay(2, scheduler
: MainScheduler.instance
)
}
② 防止集合視圖多次刷新
- flatMapLatest 的作用是:當(dāng)在短時間內(nèi)(上一個請求還沒回來)連續(xù)點(diǎn)擊多次“刷新”按鈕,雖然仍會發(fā)起多次請求,但 collectionView 只會接收并顯示最后一次請求,避免集合視圖出現(xiàn)連續(xù)刷新的現(xiàn)象:
let randomResult
= refreshButton
.rx
.tap
.asObservable().startWith(()) .flatMapLatest(getRandomResult
).share(replay
: 1)
- 也可以對源頭進(jìn)行限制下,即通過 throttle 設(shè)置個閥值(比如 1 秒),如果在 1 秒內(nèi)有多次點(diǎn)擊則只取最后一次,那么自然也就只發(fā)送一次數(shù)據(jù)請求:
let randomResult
= refreshButton
.rx
.tap
.asObservable().throttle(1, scheduler
: MainScheduler.instance
) .startWith(()) .flatMapLatest(getRandomResult
).share(replay
: 1)
③ 停止數(shù)據(jù)請求
- 在實際項目中我們可能會需要對一個未完成的網(wǎng)絡(luò)請求進(jìn)行中斷操作,比如切換頁面或者分類時,如果上一次的請求還未完成就要將其取消掉。
- 該功能簡單說就是通過 takeUntil 操作符實現(xiàn),當(dāng) takeUntil 中的 Observable 發(fā)送一個值時,便會結(jié)束對應(yīng)的 Observable:
let randomResult
= refreshButton
.rx
.tap
.asObservable().startWith(()) .flatMapLatest
{self.getRandomResult().takeUntil(self.cancelButton
.rx
.tap
)}.share(replay
: 1)
四、樣式修改
- 不管屏幕尺寸如何,collectionView 每行總是固定顯示 4 個單元格,即單元格的寬度隨屏幕尺寸的變化而變化,而單元格的高度為寬度的 1.5 倍:
let flowLayout
= UICollectionViewFlowLayout()
self.collectionView
= UICollectionView(frame
: self.view
.frame
,collectionViewLayout
: flowLayout
)
self.collectionView
.backgroundColor
= UIColor.white
self.collectionView
.register(MyCollectionViewCell.self,forCellWithReuseIdentifier
: "Cell")
self.view
.addSubview(self.collectionView
!)
let items
= Observable.just([SectionModel(model
: "", items
: ["Swift","PHP","Python","Java","C++","C#"])])
let dataSource
= RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>>(configureCell
: { (dataSource
, collectionView
, indexPath
, element
) inlet cell
= collectionView
.dequeueReusableCell(withReuseIdentifier
: "Cell",for: indexPath
) as! MyCollectionViewCellcell
.label
.text
= "\(element)"return cell
}
)
items
.bind(to
: collectionView
.rx
.items(dataSource
: dataSource
)).disposed(by
: disposeBag
)
collectionView
.rx
.setDelegate(self).disposed(by
: disposeBag
)
func collectionView(_ collectionView
: UICollectionView,layout collectionViewLayout
: UICollectionViewLayout,sizeForItemAt indexPath
: IndexPath) -> CGSize {let width
= collectionView
.bounds
.width
let cellWidth
= (width
- 30) / 4 return CGSize(width
: cellWidth
, height
: cellWidth
* 1.5)
}
總結(jié)
以上是生活随笔為你收集整理的RxSwift之UI控件UICollectionView扩展的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。