SwiftUI 集合視圖(Grid)拖放交換 Cell 的極簡實現

在這里插入圖片描述

概覽

自從 SwiftUI 橫空出世那天起,小伙伴們都感受到了它驚人的簡單與便捷。而在本課中,我們將會用一個小“栗子”更直觀的讓大家體驗到它無與倫比簡潔的描述性特質:

在這里插入圖片描述

如上圖所示,我們在 SwiftUI 中實現了 Grid 中拖放交換 Cell 的功能,它是如何做到又快又好的呢?

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

  • 概覽
  • 1. UIKit 中類似實現的思路
  • 2. SwiftUI 的世界:超乎尋常的簡單!
  • 3. 設置導出類型標識符
  • 4. 創建數據模型
  • 5. 強大的 Drag&Drop 視圖修改器
  • 6. 調整拖放的視覺效果
  • 總結

相信學完本課后,小伙伴們對 SwiftUI 中 Grid 視圖以及拖放行為的內功修為都能夠愈發精進!

那還等什么呢?Let‘s go!!!😉


1. UIKit 中類似實現的思路

在探索 SwiftUI 的解決方案之前,我們先來看看 UIKit 中完成類似實現要做些神馬。

首先,SwiftUI 集合視圖 Grid 在 UIKit 中的“對應物”是 UICollectionView。為了使 UICollectionView 履行拖放的責任和義務,我們需要讓其視圖控制器遵守 UICollectionViewDragDelegate 和 UICollectionViewDropDelegate 協議。

接著,選擇實現上面兩個協議中的若干方法。一般的,我們需要:

  • 在拖動開始時獲取源 Item:通過 collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) 方法來實現;
  • 在拖動進行中實時更新目標 Item:通過 collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) 方法來實現;
  • 在拖動完成后交換源和目標 Item:通過 collectionView(_ collectionView: UICollectionView, dragSessionDidEnd session: UIDragSession) 方法來實現;

最后,為了完成交換操作我們需要更改 UICollectionView 的數據源,并且親自動手處理界面的更新。

在 UIKit 在 UICollectionView 中拖放交換 Cell 視圖的極簡實現 這篇博文中,我們詳細討論了如何在 UIKit 的 UICollectionView 視圖中實現 Cell 拖放交換,感興趣的小伙伴們可以猛戳進一步觀賞。

2. SwiftUI 的世界:超乎尋常的簡單!

看到 UIKit 中這么“一大坨”實現概要,小伙伴們是否有點欲哭無淚的趕腳。

現在,歡迎大家來到 SwiftUI 的世界!

在 SwiftUI 中實現與 UIKit 中同樣的功能,不能說輕而易舉,也只能是毫不費力!

總的來說 SwiftUI 的簡單性體現在如下幾個方面:

  1. 由于 SwiftUI 描述性的特質,我們可以徹底丟棄故事板用簡潔的代碼去構建 Grid 自身布局和 Cell 的界面;
  2. 現成的 SwiftUI 原生拖放視圖修改器,讓簡潔更進一步;
  3. 由狀態驅動的數據源在改變時能夠“變化自如”,各種動畫效果應用起來更是得心應手;
  4. 可以非常方便的更改拖動中 Cell 的外觀;

看到這里,小伙伴們是否有些怦然心動了呢??

心動不如行動,下面就且看我們如何用 SwiftUI 來簡化 UIKit 中“笨拙”的實現!

3. 設置導出類型標識符

首先,新建一個 SwiftUI 項目,進入 Xcode 項目中 TARGET 的 info 選項窗口,展開底部的 Exported Type Identifiers 面板,在其中新建一個 Identifier 為 com.hopy.panda.com.ITEM 的導出類型:

在這里插入圖片描述

大家可以自由選擇上面 Identifier 對應的字符串標識,并沒有特定要求。

在這里,新建一個導出類型的目的是防止 App 在運行時出現所需類型未導出的警告。

實際上,如果只是要實現單個 App 中的拖放,也可以對此“不聞不問”。

4. 創建數據模型

接著,我們創建 Item 數據模型:

import UniformTypeIdentifiersextension UTType {static var item: UTType = .init(exportedAs: "com.hopy.panda.com.ITEM")
}struct Item: Identifiable, Hashable, Transferable, Codable{var id = UUID()var title: Stringstatic var transferRepresentation: some TransferRepresentation {CodableRepresentation(contentType: .item)}static 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: "🥸"),]}()
}

在上面的代碼中,我們做了這樣幾件事:

  1. 導入 UniformTypeIdentifiers 框架;
  2. 為我們的 Item 擴展 UTType 類型;
  3. 讓 Item 類型遵守 Transferable 和 Codable 協議;

5. 強大的 Drag&Drop 視圖修改器

在數據模型就緒之后,我們可以來打造 App 界面了。

首先是最簡單的 Item 導航目標視圖 DetailView:

struct DetailView: View {let item: Itemvar body: some View {Text(item.title).font(.system(size: 55, weight: .bold, design: .rounded))}
}

然后,是我們期待已久的主視圖 ContentView:

struct ContentView: View {@State var items = Item.previewprivate let cols = [GridItem(.flexible()), GridItem(.flexible())]@ViewBuilder func itemView(item: Item) -> some View {ZStack {Rectangle().frame(width: 170, height: 170).foregroundStyle(.pink.gradient)Text(item.title).font(.title2.weight(.bold)).foregroundStyle(.white)}.clipShape(RoundedRectangle(cornerRadius: 11))}var body: some View {NavigationStack {ScrollView(showsIndicators: false) {LazyVGrid(columns: cols) {ForEach(items) { item inNavigationLink(value: item) {itemView(item: item)}}}}.padding(.horizontal).edgesIgnoringSafeArea(.bottom)}}
}

運行效果如下圖所示:

在這里插入圖片描述

接著,我們來實現核心拖放功能。所幸的是,SwiftUI 早已為我們打點好了一切!

在 SwiftUI 中,對于拖動功能我們有 draggable(_:preview:) 修改器方法:

在這里插入圖片描述

而對于放置功能,同樣有 dropDestination(for:action:isTargeted:) 修改器為我們排憂解難:

在這里插入圖片描述

有了上述兩者的合璧,我們即可“利劍出鞘,無堅不摧”!

下面,我們先為 ContentView 中添加拖放交互所需的狀態:

@State var draggingItem: Item?
@State var draggingOverItem: Item?

現在,在 Grid 中的每個 Cell 上附著我們的拖放視圖修改器:

itemView(item: item).draggable(item) {itemView(item: item).onAppear {draggingItem = item}}.dropDestination(for: Item.self, action: { _, _ inguard let srcItem = draggingItem, let destItem = draggingOverItem else { return false }let srcIdx = items.firstIndex(of: srcItem)!let destIdx = items.firstIndex(of: destItem)!withAnimation(.snappy) {items.swapAt(srcIdx, destIdx)}draggingItem = nildraggingOverItem = nilreturn true}, isTargeted: { entered inguard entered, item != draggingItem else { return }draggingOverItem = item})

上面代碼的功能很簡單:我們在拖動那一剎那獲取源 Item,在拖動中即時更新目標 Item,最后在拖動結束時交換它們。

在這里插入圖片描述

My God!怎能如此簡單,竟引無數禿頭碼農門競折腰、齊掉發!


注意,目前拖放功能在 Xcode (15.2)預覽中執行起來有 Bug,大家可以在模擬器或真機中測試上述代碼。


6. 調整拖放的視覺效果

雖然我們已經實現了博文開頭的預定目標,不過我們還可以百尺竿頭更進一步。

利用 SwiftUI 的簡潔性,我們希望當用戶拖動 Item 時應該體現出有所不同的視覺效果:Grid 中對應的 Cell 能夠略微縮小、變淡;

我們照例還是先在 ContentView 中增加一個用來表示當前拖動是否包含對應目標 Item 的 wasEntered 狀態,并新建一個 needApplyDragingEffect() 方法來檢查是否要添加額外的視覺效果:

@State var wasEntered = falseprivate func needApplyDragingEffect(_ item: Item) -> Bool {draggingOverItem == item && wasEntered
}

接著,我們將 ContentView 中的 body 代碼修改為如下形式:

var body: some View {NavigationStack {ScrollView(showsIndicators: false) {LazyVGrid(columns: cols) {ForEach(items) { item inNavigationLink(value: item) {itemView(item: item).opacity(needApplyDragingEffect(item) ? 0.5 : 1.0).scaleEffect(x: needApplyDragingEffect(item) ? 0.9 : 1.0, y: needApplyDragingEffect(item) ? 0.9 : 1.0).draggable(item) {...}.dropDestination(for: Item.self, action: { _, _ inguard let srcItem = draggingItem, let destItem = draggingOverItem else { return false }let srcIdx = items.firstIndex(of: srcItem)!let destIdx = items.firstIndex(of: destItem)!withAnimation(.snappy) {items.swapAt(srcIdx, destIdx)}draggingItem = nildraggingOverItem = nilwasEntered = falsereturn true}, isTargeted: { entered inwithAnimation(.bouncy) {wasEntered = entereddraggingOverItem = item}})}}}}}
}

在上面代碼中,當拖動著的視圖凌駕于任意 Cell 的上空時,我們為對應的 Cell 添加了視覺特效:

在這里插入圖片描述

至此,我們用 SwiftUI 簡潔的代碼邏輯完成了 UIKit 中相同的功能,我們還更進一步為拖放添加了些許取悅用戶的視覺效果,棒棒噠!💯

總結

在本篇博文中,我們討論了在 SwiftUI 中如何為集合視圖(Grid)添加拖放交換其 Cell 的功能,小伙伴們可以從代碼中真正體會到 SwiftUI 的簡潔之美!

感謝觀賞,再會!😎

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

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

相關文章

基于SpringBoot + Layui的社區物業管理系統

項目介紹 社區物業管理系統是基于java編程語言,springboot框架,idea工具,mysql數據庫進行開發,本系統分為業主和管理員兩個角色,業主可以登陸系統,查看車位費用信息,查看物業費用信息&#xff0…

2個wordpress優化SEO主題模板

SEO優化wordpress主題 簡潔的SEO優化wordpress主題,效果好不好,結果會告訴你,適合SEO公司使用的主題。 https://www.jianzhanpress.com/?p2804 SEO優化海外WordPress主題 簡潔的SEO優化海外服務商WordPress主題,為中國制造202…

C# byte[]、struct、intptr、byte[]和byte*等的相互轉換

struct、byte[]互相轉換 //struct轉換為byte[] public static byte[] StructToBytes(object structObj) {int size Marshal.SizeOf(structObj);IntPtr buffer Marshal.AllocHGlobal(size);try{Marshal.StructureToPtr(structObj, buffer, false);byte[] bytes new byte[siz…

HTTP REST 方式調用WebService接口(wsdl)

一、WebService接口正常使用SOAP協議調用,測試時常采用SoapUI軟件調用,具體如下: 二、由于目前主流web服務逐漸轉換為RESTful的形式,且SOAP協議的實現也是基于HTTP協議,故存在通過HTTP調用WebService接口的可能 2.1 …

Flink雙流(join)

一、介紹 Join大體分類只有兩種:Window Join和Interval Join Window Join有可以根據Window的類型細分出3種:Tumbling(滾動) Window Join、Sliding(滑動) Window Join、Session(會話) Widnow Join。 🌸Window 類型的join都是利用window的機制…

【OpenFeign常用配置】

OpenFeign常用配置 快速入門:1、引入依賴2、啟用OpenFeign 實踐1、引入依賴2、開啟連接池功能3、模塊劃分4、日志5、重試 快速入門: OpenFeign是一個聲明式的http客戶端,是spring cloud在eureka公司開源的feign基礎上改造而來。其作用及時基于…

C++ template-2

第 5 章 基礎技巧 5.1 typename 關鍵字 關鍵字typename在C標準化過程中被引入進來&#xff0c;用來澄清模板內部的一個標識符代表的 是某種類型&#xff0c;而不是數據成員。考慮下面這個例子&#xff1a; template<typename T> class MyClass { public:void foo() {t…

【代碼隨想錄算法訓練營Day09】28.實現 strStr(); 459.重復的子字符串

文章目錄 Day 9 第四章 字符串part0228. 實現 strStr() &#xff08;本題可以跳過&#xff09;KMP 思路KMP 代碼 459.重復的子字符串 &#xff08;本題可以跳過&#xff09;字符串總結雙指針回顧 Day 9 第四章 字符串part02 今日任務 28.實現 strStr(); 459.重復的子字符串; 字…

題目:C++快速找到未知長度單鏈表的中間節點。普通方法和高級方法2種解題思路解析。

在數據結構的面試中&#xff0c;經常會出現這樣的問題&#xff1a;如何快速找到未知長度單鏈表的中間節點&#xff1f;通常&#xff0c;面試官會期待你提供兩種解法&#xff1a;一種是最基本的普通方法&#xff0c;另一種是更高效的 advanced 方法。本文將詳細介紹這兩種方法。…

Nginx -2

接著上文寫 5.4.7 驗證模塊 需要輸入用戶名和密碼 模塊名稱&#xff1a;ngx_http_auth_basic_module 訪問控制基于模塊 ngx_http_auth_basic_module 實現&#xff0c;可以通過匹配客戶端資源進行限制 語法&#xff1a; Syntax: auth_basic string | off; Default: auth_ba…

威爾金森功分器基本原理學習筆記

威爾金森功分器基本原理 威爾金森功率分配器的功能是將輸入信號等分或不等分的分配到各個輸出端口&#xff0c;并保持相同輸出相位。環形器雖然有類似功能&#xff0c;但威爾金森功率分配器在應用上具有更寬的帶寬。微帶形功分器的電路結構如圖所示&#xff0c;其中&#xff0…

【OpenAI Sora】何時開放使用?付費課程已上線(sora什么時候開放使用 )

Sora何時開放使用 根據提供的信息&#xff0c;Sora目前還未對廣大用戶開放。OpenAI在2024年2月15日展示了Sora的視頻&#xff0c;但沒有設立等待名單或提供API訪問。Sora仍在開發中&#xff0c;正在接受安全測試&#xff0c;并且尚未向公眾開放使用。 付費課程已上線 根據最…

Vue圖片瀏覽組件v-viewer,支持旋轉、縮放、翻轉等操作

Vue圖片瀏覽組件v-viewer&#xff0c;支持旋轉、縮放、翻轉等操作 之前用過viewer.js&#xff0c;算是市場上用過最全面的圖片預覽。v-viewer&#xff0c;是基于viewer.js的一個圖片瀏覽的Vue組件&#xff0c;支持旋轉、縮放、翻轉等操作。 基本使用 安裝&#xff1a;npm安裝…

費舍爾FISHER金屬探測器探測儀維修F70

美國FISHER LABS費舍爾地下金屬探測器&#xff0c;金屬探測儀等維修&#xff08;考古探金銀銅探寶等儀器&#xff09;。 費舍爾F70視聽目標ID金屬探測器&#xff0c;Fisher 金屬探測器公司成立于1931年&#xff0c;在實驗條件很艱苦的情況下&#xff0c;研發出了地下金屬探測器…

【Python】實現一個類似于Glass2k的Windows窗口透明化軟件

一 背景說明 網上看到一款Windows下的窗口透明化工具Glass2k&#xff08;Glass2k官網&#xff09;&#xff0c;可以簡單地通過快捷鍵實現任意窗口的透明化&#xff0c;還挺方便的&#xff0c;想用Python自己實現一下類似的功能。 軟件已經開源到&#xff1a;窗口透明化小工具開…

【Leetcode】889. 根據前序和后序遍歷構造二叉樹

文章目錄 題目思路代碼結果 題目 題目鏈接 給定兩個整數數組&#xff0c;preorder 和 postorder &#xff0c;其中 preorder 是一個具有 無重復 值的二叉樹的前序遍歷&#xff0c;postorder 是同一棵樹的后序遍歷&#xff0c;重構并返回二叉樹。 如果存在多個答案&#xff0c;…

CSS基礎屬性

【三】基礎屬性 【1】高度和寬度 &#xff08;1&#xff09;參數 width&#xff08;寬度&#xff09;&#xff1a;用于設置元素的寬度。可以使用具體的數值&#xff08;如像素值&#xff09;或百分比來指定寬度。 height&#xff08;高度&#xff09;&#xff1a;用于設置元…

Kubernetes 卷存儲 NFS | nfs搭建配置 原理介紹 nfs作為存儲卷使用

目錄 1、NFS介紹2、NFS服務部署2.1安裝nfs服務 (服務端配置)2.2啟動NFS服務2.3 服務檢查2.4 客戶端配置 3、nfs作為存儲卷使用3.1 nfs作為volume3.2 nfs存儲的缺點3.3 nfs作為PersistentVolum 4、nfs作為動態存儲提供5、總結 1、NFS介紹 NFS&#xff08;Network File System&a…

4.pom文件介紹Maven常用命令

1.pom.xml文件介紹. 1.1project標簽和modelVersion標簽介紹. pom.xml文件是maven的核心文件&#xff0c;POM(Project Object Model&#xff0c;項目對象模型)定義了項目的基本信息&#xff0c;用于描述如何構建&#xff0c;聲明項目依賴;&#xff1b; 1.2依賴坐標介紹. 依賴的…

得物面試:Kafka消息0丟失,如何實現?

得物面試&#xff1a;Kafka消息0丟失&#xff0c;如何實現&#xff1f; 尼恩說在前面 在40歲老架構師 尼恩的讀者交流群(50)中&#xff0c;最近有小伙伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、希音、百度、網易、美團的面試資格&#xff0c;遇到很多很重要的面…