UIKit 在 UICollectionView 中拖放交換 Cell 視圖的極簡實現

在這里插入圖片描述

概覽

UIKit 中的 UICollectionView 視圖是我們顯示多列集合數據的不二選擇,而豐富多彩的交互操作更是我們選擇 UICollectionView 視圖的另一個重要原因。

在這里插入圖片描述

如上圖所示:我們實現了在 UICollectionView 中拖放交換任意兩個 Cell 子視圖的功能,這是怎么做到的呢?

在本篇博文中,您將學到如下內容:

  • 概覽
  • 1. UICollectionView 拖放交互基本思路
  • 2. 構建 storyboard
  • 3. 準備數據源
  • 4. 遵守拖放代理
  • 5. 更快的響應交換操作
  • 總結

其實,完成這樣一種交換遠比小伙伴們想象的要簡單的多!

所以,還等什么呢?Let‘s find out!!!😉


1. UICollectionView 拖放交互基本思路

在 iOS(iPadOS/MacOS) 中,廣義的拖放操作涉及到跨越不同 App 間的范疇:比如,我們常常希望將微信中的圖片直接拖動到 QQ 的聊天界面中去。

不過,這里我們只想在 App 內部進行拖動,所以完成起來就要簡單許多。我們的數據元素不需要滿足 NSItemProvider 或 NSSecureCoding 等一些苛刻限制,只是單純的數據即可。

一般來說,為了完成拖放,我們需要在對應視圖上實現 UIDragInteractionDelegate 和 UIDropInteractionDelegate 協議指定的方法。

而對于 UICollectionView 視圖,其子 Cell 間的拖動我們還有更簡單的方式:遵守 UICollectionViewDragDelegate 和 UICollectionViewDropDelegate 協議即可。

在這里插入圖片描述

iOS(iPadOS/MacOS) 系統中關于拖放(Drag & Drop)更詳細全面的介紹,蘋果官網無疑是一個很好的選擇:

  • ? Drag and drop

2. 構建 storyboard

首先,新建一個 UIKit 項目,打開 Main 故事板(Main.storyboard)為視圖控制器添加一個 UICollectionView 子視圖:

在這里插入圖片描述

如上圖所示,我們隨后又在 UICollectionView 中添加了一個靜態的 UICollectionViewCell 控件:

在這里插入圖片描述

然后,調整 UICollectionViewCell 背景以及 Label 字體大小和顏色到滿意為止:

在這里插入圖片描述

最后,妥善設置好 UICollectionViewCell 的 ID:

在這里插入圖片描述

并將 UICollectionView 關聯到視圖控制器的 collectionView 屬性上:

在這里插入圖片描述

3. 準備數據源

現在界面布局已準備就緒,我們接下來需要創建數據模型:

struct Item {var id = UUID()var title: Stringstatic var preview: [Item] {[Item(title: "Apple"), Item(title: "Banana"),Item(title: "Cherry"), Item(title: "Date"),Item(title: "Dragon"), Item(title: "Sheep"),Item(title: "V-Malicious"), Item(title: "X-Code"),Item(title: "GreatWall"), Item(title: "TaiTan"),Item(title: "Milk"), Item(title: "🥸"),]}
}

是滴,我們只需要一個簡單的結構類型就可以了!

然后,讓我們的 ViewController 遵守 UICollectionViewDataSource 協議,并實現相關方法:

class ViewController: UIViewController, UICollectionViewDataSource {@IBOutlet weak var collectionView: UICollectionView!var items = Item.previewfunc collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {items.count}func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cellcell.layer.cornerRadius = 15.0cell.label.text = items[indexPath.row].titlereturn cell}override func viewDidLoad() {super.viewDidLoad()collectionView.dataSource = self}
}

注意,我們并沒有讓 ViewController 遵守 UICollectionViewDelegate 協議,因為它和這里的拖動操作基本上沒有半毛線關系。

4. 遵守拖放代理

上面說過,為了讓 UICollectionView 中的 Cell 子視圖支持拖放,我們需要先讓視圖控制器遵守 UICollectionViewDragDelegate 和 UICollectionViewDropDelegate 協議:

extension ViewController: UICollectionViewDragDelegate {func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {// 保存源 Item 索引srcIndex = indexPathlet itemProvider = NSItemProvider(object: "Item" as NSString)return [UIDragItem(itemProvider: itemProvider)]}
}extension ViewController: UICollectionViewDropDelegate {func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {// 保存目的 Item 索引self.destIndex = destinationIndexPathreturn .init(operation: .move)}func collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession) {        guard let srcIndex, let destIndex else { return }swapItems(from: srcIndex, to: destIndex)self.srcIndex = nilself.destIndex = nil}
}

對于上面的代碼,需要說明的是:

  • collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) 在拖動開始時被調用,我們可以趁機保存源 Item 的信息;
  • collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) 在拖動中被多次調用,其中傳入的 destinationIndexPath 參數表示目的 Cell 的索引(如果下方有的話),我們可以借此保存目的 Item 的信息;
  • collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession) 在結束放置時被調用,我們此時可以實現交換對應 Cell 的操作;

隨后,我們還需要在 ViewController 中添加所需的屬性和方法:

var srcIndex: IndexPath?
var destIndex: IndexPath?private func swapItems(from srcIndex: IndexPath, to destIndex: IndexPath) {items.swapAt(srcIndex.row, destIndex.row)// 將源和目的 Cell 的移動放到批處理中以產生流暢的動畫:collectionView.performBatchUpdates {collectionView.moveItem(at: srcIndex, to: destIndex)collectionView.moveItem(at: destIndex, to: srcIndex)}
}

最后,在視圖控制器加載時為其綁定對應的拖放代理,并開啟拖動交互:

override func viewDidLoad() {super.viewDidLoad()collectionView.dataSource = selfcollectionView.dropDelegate = selfcollectionView.dragDelegate = selfcollectionView.dragInteractionEnabled = true
}

現在,運行 App 看一下效果:

在這里插入圖片描述

不過,小伙伴們或許發現了,拖動放置后 Cell 交換會有略微延時,這是怎么回事呢?

5. 更快的響應交換操作

上面 Cell 拖動交換會慢一拍的原因為:我們是在 collectionView(_ collectionView: UICollectionView, dropSessionDidEnd session: UIDropSession) 方法中執行 swapItems() 來完成交換的。

而系統對該方法的回調觸發是比較“謹慎”的,它會在判斷用戶徹底抬起手指后才能得到運行機會。

如果希望用戶在目標 Cell 上抬起手指時能夠立即完成交換行為,我們可以將 swapItems() 方法放到 UICollectionViewDragDelegate 協議中的 collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) 的回調中去執行,因為該方法識別觸發的速度要快得多:

extension ViewController: UICollectionViewDragDelegate {func collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) {guard let srcIndex, let destIndex else { return }swapItems(from: srcIndex, to: destIndex)self.srcIndex = nilself.destIndex = nil}
}

再次運行 App 看一下:

在這里插入圖片描述

現在,我們 UICollectionView 中的拖放交換操作的速度又上了一個新臺階,還不快給自己一個大大的贊嗎?棒棒噠!💯

總結

在本篇博文中,我們討論了 UIKit 中 UICollectionView 視圖拖放操作的基本原理,并用最簡單的代碼實現了 UICollectionView 視圖中 Cell 的交換功能。

感謝觀賞,再會!😎

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/696806.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/696806.shtml
英文地址,請注明出處:http://en.pswp.cn/news/696806.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

js如何判斷一個對象中某一個屬性存在并且有值

在JavaScript中,可以使用不同的方法來判斷一個對象中某個屬性是否存在并且有值。以下是幾種常見的方法: 1、使用hasOwnProperty()方法:該方法用于檢查對象是否具有指定的屬性。可以通過以下方式來判斷屬性是否存在并且有值: if (…

整理了去年的一些運維面試題一

Ingress的yaml文件需要包含哪些? CICD搭建流程? JAVA程序打包工具? 如何檢測Linux端口如何通信? k8s集群之間如何通信的? docker組成部分? 20位掩碼有多少主機IP? 在linux中四個T的硬盤使用什…

Zabbix 遠程監控主機

目錄 1、安裝 Zabbix 安裝客戶端 服務端測試通訊 Web頁面添加主機 2、監控 Nginx 自定義腳本監控 Nginx web配置臺 3、監控 MySQL 配置模版文件 配置Web界面 1、安裝 Zabbix node-12 作為zabbix的被監控端,提供mysql服務器,配置zabbix監控node…

jquery寫組件滑動人機驗證組件

jquery組件,雖然 jquery 語法古老,但是寫好了用起來真的很爽啊,本文用滑動人機驗證給大家做個詳細教程(直接復制代碼就可以用噢o(* ̄▽ ̄*)ブ) 第一步 先看下組件本身 component.js (function() {…

Nginx網絡服務三-----(三方模塊和內置變量)

1.驗證模塊 需要輸入用戶名和密碼 我們要用htpasswd這個命令,先安裝一下httpd 生成文件和用戶 修改文件 訪問頁面 為什么找不到頁面? 對應的路徑下,沒有這個文件 去創建文件 去虛擬機瀏覽器查看 有的頁面不想被別人看到,可以做…

【UI自動化】使用poco框架進行元素唯一定位

直接選擇: 1.poco(text買入).click() 2.poco("android.widget.ImageView").click()相對選擇、空間選擇: 3.poco(text/name).parent().child()[0].click()正則表達式: 4.listpoco(textMatches".*ETF")今天主要想記錄下…

centos 系統盤 放到 win pc 中的異常解決

有一塊 2.5 480g sata ssd,之前是筆記本電腦的centos系統盤,后來沒用了,打算掛到臺式機上當下載盤。臺式機pc的主板是華碩 h610m-a。 難點一: 因為臺式pc上已經掛了兩塊3.5 hdd,發現sata的電源線都在3.5hdd附近&#…

利用RBI(Remote Browser Isolation)技術訪問ChatGPT

系統組網圖 #mermaid-svg-Bza2puvd8MudMbqR {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Bza2puvd8MudMbqR .error-icon{fill:#552222;}#mermaid-svg-Bza2puvd8MudMbqR .error-text{fill:#552222;stroke:#552222;…

300分鐘吃透分布式緩存-10講:MC是怎么定位key的?

我們在進行 Mc 架構剖析時,除了學習 Mc 的系統架構、網絡模型、狀態機外,還對 Mc 的 slab 分配、Hashtable、LRU 有了簡單的了解。本節課,將進一步深入學習這些知識點。 接下來,進入 Memcached 進階的學習。會講解 Mc 是如何進行…

QT應用軟件【協議篇】周立功CAN接口卡代碼示例

文章目錄 USBCAN系列CAN接口卡規格參數資料下載QT引用周立功的庫安裝sdk代碼USBCAN系列CAN接口卡 USBCAN系列CAN接口卡兼容USB2.0全速規范,可支持1/2/4/8路CAN接口。采用該接口卡,PC機可通過USB連入CAN網絡,進行CAN總線數據采集和處理,主要具備以下幾大優勢特點: 支持車載…

正交匹配追蹤(Orthogonal Matching Pursuit, OMP)的MATLAB實現

壓縮感知(Compressed Sensing, CS)是一種利用稀疏信號的先驗知識,用遠少于奈奎斯特采樣定理要求的樣本數目恢復整個信號的技術。正交匹配追蹤(Orthogonal Matching Pursuit, OMP)是一種常見的貪婪算法(Gree…

【CF】團隊訓練賽2 J-Palindrome Reversion 題解

傳送門:Palindrome Reversion 標簽:字符串 題目大意 規定一個操作:選擇字符串中的一段區間[l,r]并使其翻轉。現在給出一個字符串s,你要判斷能否通過一次操作使其變為回文串。 輸入:一個字符串,其長度不超…

在蘋果電腦MAC上安裝Windows10(雙系統安裝的詳細圖文步驟教程)

在蘋果電腦MAC上安裝Windows10(雙系統安裝的詳細圖文步驟教程) 一、準備工作準備項1:U盤作為系統安裝盤準備項2:您需要安裝的系統鏡像 二、啟動轉換助理步驟1:找到啟動轉換助理步驟2:啟動轉換助理步驟3&…

波奇學Linux:進程通信管道

進程通信 管道:基于文件級別的單向通信 創建父子進程,使得進程的struct file*fd_array[]的文件描述符指向同一個struct file文件,這個文件是內存級文件。 父進程關寫端,子進程再關閉讀端。實現單向通信 子進程寫入,父進…

Java面向對象(三)

一、封裝: 一般意義的封裝:把一段重復代碼抽取成一個函數,稱為代碼的封裝(包裝)面向對象語言的封裝:將類的某些信息隱藏在類的內部(通過使用不同的訪問權限修飾符),不許…

C++ Primer 筆記(總結,摘要,概括)——第3章 字符串、向量和數組

目錄 3.1 命名空間的using聲明 3.2 標準庫類型string 3.2.1 定義和初始化string對象 3.2.2 string對象上的操作 3.2.3 處理string對象中的字符 3.3 標準庫類型vector 3.3.1 定義和初始化vector對象 3.3.2 向vector對象中添加元素 3.3.3 其他vector操作 3.4 迭代器介紹 3.4.…

如何使用rocketmq實現分布式事務?

什么是rocketmq事務消息 事務消息是 Apache RocketMQ 提供的一種高級消息類型,支持在分布式場景下保障消息生產和本地事務的最終一致性。 RocketMQ的分布式事務又稱為“半消息事務”。 事務消息處理流程 RocketMQ是靠半消息機制實現分布式事務 事務消息&#x…

Spring之AOP源碼解析(上)

Aop相關注解 EnableTransactionManagementEnableAspectJAutoProxyEnableAsync... 從注解切入來看看這些注解都干了什么 Import注解作用簡述 注入的類一般繼承ImportSelector或者ImportBeanDefinitionRegistrar接口 繼承ImportSelector接口:selectImports方法返回…

pandas/geopandas 筆記:判斷地點在不在路網上 不在路網的點和路網的距離

0 導入庫 import osimport pandas as pd pd.set_option(display.max_rows,5)import osmnx as oximport geopandas as gpd from shapely.geometry import Point 1 讀取數據 假設我們有 如下的數據: 1.1 新加坡室外基站位置數據 cell_stationpd.read_csv(outdoor…

TSINGSEE青犀AI智能分析網關V4初始配置與算法相關配置介紹

TSINGSEE青犀AI智能分析網關V4內置了近40種AI算法模型,支持對接入的視頻圖像進行人、車、物、行為等實時檢測分析,上報識別結果,并能進行語音告警播放。硬件管理平臺支持RTSP、GB28181協議、以及廠家私有協議接入,可兼容市面上常見…