Swift 仿頭條頻道管理(UICollectionView颤殴,拖拽排序)

前言

初步實(shí)現(xiàn)了一個(gè)仿今日頭條的頻道管理溶耘,能夠進(jìn)行拖拽排序,效果圖如下

image

分析

主要使用UICollectionView實(shí)現(xiàn)服鹅,利用其原生的API實(shí)現(xiàn)拖拽效果凳兵。

核心分為以下步驟:

  • 得到獲取焦點(diǎn)的Cell

  • 處理移動(dòng)中的事件

  • 移動(dòng)結(jié)束后,處理放下Cell問題

創(chuàng)建UICollectionView之前先創(chuàng)建個(gè)UICollectionViewFlowLayout企软,我們定義一行最多顯示4個(gè)Cell庐扫,單個(gè)Cell之前的間距是10,高度為40


let width: CGFloat = (self.view.frame.width -  5 * 10) / 4

let height: CGFloat = 40

定義UICollectionViewFlowLayout


let flowLayout = UICollectionViewFlowLayout()

//滾動(dòng)方向

flowLayout.scrollDirection = .vertical

//網(wǎng)格中各行項(xiàng)目之間使用的最小間距

flowLayout.minimumLineSpacing = 10

//在同一行中的項(xiàng)目之間使用的最小間距

flowLayout.minimumInteritemSpacing = 10

//用于單元格的默認(rèn)大小

flowLayout.itemSize = CGSize.init(width: width, height: height)

//用于標(biāo)題的默認(rèn)大小

flowLayout.headerReferenceSize = CGSize.init(width: self.view.frame.width, height: 50)

headerReferenceSize 用于定義頭部的大姓躺凇(IndexPath.section對(duì)應(yīng)的部分)

創(chuàng)建UICollectionView形庭,UICollectionView需要register兩個(gè),一個(gè)是普通視圖的Cell藻治,一個(gè)是頭部對(duì)應(yīng)的Cell


myCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)

myCollectionView.register(UINib.init(nibName: "EditCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: EditCollectionViewCell.forCellReuseIdentifier)

myCollectionView.register(UINib(nibName: "HeaderCollectionReusableView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: HeaderCollectionReusableView.forCellReuseIdentifier)

EditCollectionViewCell 為頻道的Cell(如:“關(guān)注碘勉、特產(chǎn)、健康桩卵、房產(chǎn)等”)验靡,HeaderCollectionReusableView 為頭部的Cell (如:“我的頻道、頻道推薦”)

需要實(shí)現(xiàn)的代理


myCollectionView.delegate = self

myCollectionView.dataSource = self

myCollectionView.dragDelegate = self

myCollectionView.dropDelegate = self

delegate 管理集合視圖中項(xiàng)目顯示和選擇

dataSource 提供數(shù)據(jù)源

dragDelegate 管理拖曳交互

dropDelegate 管理丟棄交互

重點(diǎn)就是dragDelegate和dropDelegate

提供的數(shù)據(jù)源如下


func initData() {

        itemHeaders = ["我的頻道","頻道推薦"]

        itemNames = [0: [String](["關(guān)注","推薦","視頻","熱點(diǎn)","北京","新時(shí)代","圖片","頭條號(hào)","娛樂","問答","體育","科技","懂車帝","財(cái)經(jīng)","軍事","國際"]),1: [String](["健康","冬奧","特產(chǎn)","房產(chǎn)","小說","時(shí)尚","歷史","育兒","直播","搞笑","數(shù)碼","美食","養(yǎng)生","電影","手機(jī)","旅游","寵物","情感"])]

    }

開啟UICollectionView響應(yīng)拖拽操作


myCollectionView.dragInteractionEnabled = true

以上是基本的操作流程雏节,下面重點(diǎn)講下胜嗓,上邊提到的三個(gè)步驟


得到獲取焦點(diǎn)的Cell

需要實(shí)現(xiàn)UICollectionViewDragDelegate代理,并提供UIDragItem


func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {

        guard indexPath.section != 1 else {

            return []

        }

        let item = self.itemNames[indexPath.section]![indexPath.row]

        let itemProvider = NSItemProvider(object: item as NSString)

        let dragItem = UIDragItem(itemProvider: itemProvider)

        dragItem.localObject = item

        dragingIndexPath = indexPath

        return [dragItem]

    }

由于我們規(guī)定钩乍,“我的頻道”可以拖拽辞州,“頻道推薦”不可以,所以 indexPath.section 為1 的部分寥粹,返回一個(gè)空數(shù)組变过,表示不響應(yīng)事件。

通過section和row 獲取到對(duì)象的值涝涤,并創(chuàng)建NSItemProvider返回媚狰。

NSItemProvider:拖放活動(dòng)期間在進(jìn)程之間共享的數(shù)據(jù)或文件,初始化的object要是NSObject, NSCopying的子類阔拳,如:NSItemProvider(object: item as NSString)崭孤,是把String作為共享數(shù)據(jù)了。

處理移動(dòng)中的事件

這個(gè)我們需要實(shí)現(xiàn)UICollectionViewDropDelegate代理糊肠,并提供UICollectionViewDropProposal


func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {

        guard dragingIndexPath?.section == destinationIndexPath?.section else {

            return UICollectionViewDropProposal(operation: .forbidden)

        }

        if session.localDragSession != nil {

            if collectionView.hasActiveDrag {

                return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)

            } else {

                return UICollectionViewDropProposal(operation: .copy, intent: .insertAtDestinationIndexPath)

            }

        } else {

            return UICollectionViewDropProposal(operation: .forbidden)

        }

    }

由于“我的頻道”和“頻道推薦”是禁止互相滑動(dòng)的辨宠,所以,拖拽的起始dragingIndexPath和目標(biāo)destinationIndexPath的section不一樣货裹,就表示跨區(qū)了嗤形,設(shè)置其為forbidden。

移動(dòng)結(jié)束后弧圆,處理放下Cell問題

這是最后一步派殷,需要實(shí)現(xiàn)UICollectionViewDropDelegate代理还最,通過coordinator我們可以獲取到操作類型,是move還是copy毡惜。


func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {

        guard let destinationIndexPath = coordinator.destinationIndexPath else {

            return

        }

        switch coordinator.proposal.operation {

        case .move:

            break

        case .copy:

            break

        default:

            return

        }

    }

move 操作后拓轻,需要把之前的位置刪除掉,在新的位置進(jìn)行插入


self.itemNames[destinationIndexPath.section]!.remove(at: sourceIndexPath.row)

self.itemNames[destinationIndexPath.section]!.insert(item.dragItem.localObject as! String, at: destinationIndexPath.row)

collectionView.deleteItems(at: [sourceIndexPath])

collectionView.insertItems(at: [destinationIndexPath])

要先對(duì)數(shù)據(jù)源進(jìn)行移除和添加操作经伙,然后在視圖進(jìn)行更新扶叉,否則會(huì)崩潰

copy 操作后,需要在新的位置進(jìn)行插入


let indexPath = IndexPath(row: destinationIndexPath.row, section: destinationIndexPath.section)

self.itemNames[destinationIndexPath.section]!.insert(item.dragItem.localObject as! String, at: indexPath.row)

collectionView.insertItems(at: indexPath)

以上就是全部的分析流程帕膜,還有一種實(shí)現(xiàn)思路是:在collectionView上添加一個(gè)自定義的View枣氧,覆蓋在Cell之上,獲取手勢(shì)事件后垮刹,根據(jù)手勢(shì)滑動(dòng)达吞,動(dòng)態(tài)更改自定義的View的位置,同樣可以實(shí)現(xiàn)以上效果荒典。

Git: https://github.com/LSnumber1/CustomUICollection

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酪劫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寺董,更是在濱河造成了極大的恐慌覆糟,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遮咖,死亡現(xiàn)場(chǎng)離奇詭異滩字,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)御吞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門麦箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陶珠,你說我怎么就攤上這事挟裂。” “怎么了背率?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)嫩与。 經(jīng)常有香客問我寝姿,道長(zhǎng),這世上最難降的妖魔是什么划滋? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任饵筑,我火速辦了婚禮,結(jié)果婚禮上处坪,老公的妹妹穿的比我還像新娘根资。我一直安慰自己架专,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布玄帕。 她就那樣靜靜地躺著部脚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裤纹。 梳的紋絲不亂的頭發(fā)上委刘,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音鹰椒,去河邊找鬼锡移。 笑死,一個(gè)胖子當(dāng)著我的面吹牛漆际,可吹牛的內(nèi)容都是我干的淆珊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼奸汇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼施符!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茫蛹,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤操刀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后婴洼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骨坑,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年柬采,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了欢唾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粉捻,死狀恐怖礁遣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肩刃,我是刑警寧澤祟霍,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站盈包,受9級(jí)特大地震影響沸呐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜呢燥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一崭添、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叛氨,春花似錦呼渣、人聲如沸棘伴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽焊夸。三九已至,卻和暖如春缰犁,著一層夾襖步出監(jiān)牢的瞬間淳地,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工帅容, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颇象,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓并徘,卻偏偏與公主長(zhǎng)得像遣钳,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子麦乞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容