50天iOS挑戰(Swift) - 第6天:可拖動重排的CollectionView
50天,每天一個Swift語言的iOS練手項目,覆寫iOS開發的主要知識。貴在堅持,重在思考
文章清單:http://blog.csdn.net/b735098742/article/category/6978601
Github項目:https://github.com/Minecodecraft/50DaysOfSwift
簡介
本項目為實作CollectionView的拖動重排。iOS 9.0起系統級支援了重排,免除了計算重排相關資料的複雜過程,該項目即為此實踐。
主要知識點: Collection View, UICollectionViewDelegate,swift懶加載,枚舉(使用、rawValue)、Storyboard
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICdzFWRoRXdvN1LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90zZNBzYE9UNBRlT6NmaZZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN2gDMzEzM1ETNycDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
過程
1、 架構搭建
本項目結構很簡單,通過NavigationController管理一個TableViewController即可,根據需要壓入。(本項目練習純CollectionViewController以及ViewController下的CollectionView兩種方式)
對于Storyboard連線之類的設定不再贅述,關于涉及到的Cell和CollectionView的其他知識通過後續項目練習。
2、 完成segue
前面說了,本項目用TableViewController來跳轉,那麼首先來實作這個。
連接配接好跳轉的segue之後,接下來如何跳轉呢?如果在跳轉時頻繁枚舉segue,那稍微大的項目簡直爆炸,而且這樣的代碼基本和後期維護sayGoodBye了。
我們采用枚舉控制器的做法,實作一個segueIdentifier()->String的方法傳回控制器對應的segue名稱,然後利用
performSegue(withIdentifier identifier: String, sender: Any?)
來完成跳轉,跳轉的準備工作就在
prepare(for segue: UIStoryboardSegue, sender: Any?)
中執行。(如果這裡不懂請留言)
enum VC: String {
case ViewController
case CollectionViewController
func segueIdentifier() -> String {
switch self {
case .ViewController:
return "VCSegueIdentifier"
case .CollectionViewController:
return "CVCSegueIdentifier"
}
}
}
3、 性能第一,寫個懶加載閉包
表格的資料就用懶加載來實作吧,懶加載就是并不一開始就加載而是用到再加載,這對移動裝置并不寬裕的記憶體影響很大,Swift中對懶加載的處理很簡單,lazy搞定。
再說說閉包,這也是swift的一大特色,對應于OC的block但是更強。Swift中,方法是特殊的閉包,可見熟練掌握閉包可以把swift寫的更優雅。
懶加載基本格式就是
lazy var [valueName]:TypeName = { [description]; return [value] }()
也可以簡化成
lazy var [valueName]: TypeName {}
// 閉包方式懶加載指派
lazy var str:String = {
return "Hello Swift"
}()
// 簡化版閉包
lazy var str: String {
return "Hello Swift"
}
是以本文的控制器數組就采用這個方式完成:
lazy var vc: [VC] = {
var tmpArray: [VC] = [.ViewController, .CollectionViewController]
return tmpArray
}()
4、 先從簡單的開始——CollectionViewController
使用CollectionViewController時,系統自動幫我們實作了正常的手勢操作,我們隻需要重排就好啦
實作基礎方法:numberOfItemsInSection、cellForItemAt,過于基礎不再贅述
接下來就是重排的重要方法:moveItemAt
// 實作重排的關鍵
override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let tmpCell = cells.remove(at: sourceIndexPath.item)
cells.insert(tmpCell, at: destinationIndexPath.item)
}
很簡單有木有,就是在這個方法裡把資料源的資料進行修改即可,兩行代碼的作用就是把資料源數組交換了位置。
5、 進階——使用CollectionView
相對CollectionViewController,使用CollectionView多了一些步驟,但是應用的範圍也更靈活。使用者需要自行實作UICollectionViewDelegate以及UICollectionViewDataSource。
實作方式見代碼,同步驟4。
同時,使用者的手勢也需要自行設定,響應長按手勢的代碼如下:
func longPressAction(_ gesture: UILongPressGestureRecognizer) {
// 判斷長按的狀态
switch gesture.state {
case .began:
// 擷取選擇位置
// indexPathForItem擷取Item所處的indexPath
// indexPath是表格或清單推算出目前row和col的屬性
// ***GestureRecognizer的location方法傳回在所處view的一個CGPoint屬性
guard let selectedIndexPath = self.collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else { return }
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
case .changed:
// 移動了
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: collectionView))
case .ended:
// 結束移動
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}
一點小小的補充
1.很多人遇到了CollectionView頂部留白的問題,網上搜到的答案是使用
self.automaticallyAdjustsScrollViewInsets = false
,但是都沒有給出解釋。特地研究了下,這個是系統對于目前界面有TabBar、NavigationBar、StatuBar元素的,自動計算并提供的留白。是以解決方法兩種:①:使用者在設定大小時候已經考慮了這幾個Bar控件,那麼使用
self.automaticallyAdjustsScrollViewInsets = false
關閉系統的适配即可。 ②:使用者可以不考慮這幾個Bar的大小,直接把CollectionView的frame設定為目前view最大即可。
2.項目源碼位址 GitHub,歡迎大家前來支援,希望可以随手留個Star。多謝~