天天看點

50天iOS挑戰(Swift) - 第6天:可拖動重排的CollectionView50天iOS挑戰(Swift) - 第6天:可拖動重排的CollectionView

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

50天iOS挑戰(Swift) - 第6天:可拖動重排的CollectionView50天iOS挑戰(Swift) - 第6天:可拖動重排的CollectionView

過程

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。多謝~