SwiftUI & PromiseKit:讓 Alert 樣式統一又可復用  打破彈出視窗的惡夢


開發 iOS 的過程中,常常會有彈出 Alert 讓使用者選擇的需求,又需要知道使用者選擇了哪一個,卻遇到各種彈出都要一直callback callback 嗎?每次選項都很難掌握,多個選項還要自行客製化,也很難復用,只能一個畫面刻一個?

在本篇教學文章中,我們會了解到幾個要點並實作:

  1. 建立統一入口 Alert 服務化,讓任何地方需要顯示與選擇時,都能輕易掌握使用呼叫。
  2. 客製化領域設計選項 (enum),不管是哪種商業邏輯都能很簡單地讓使用者做選擇,並且輕易掌握使用者選擇後的事件回應。
  3. Alert 服務自動讀取 enum 選項,並建置 UI Button 列表。
  4. 脫離傳統 UIAlertController callback 地獄,結合 PromiseKit 與 SwiftEntryKit 解偶系統服務與商業邏輯領域設計。

本篇文章將使用以下的工具、環境與第三方庫:

  1. Xcode 11
  2. macOS 10.15
  3. SwiftUI
  4. PromiseKit
  5. SwiftEntryKit
  6. 範例

建議大家參考範例程式碼消化服用呦!

前置工作

首先,讓我們建立一個展示用的 View,這次我們選擇使用 SwiftUI 來建立!
由於繪製畫面並不是我們這篇文章的重點,有興趣的讀者可以自行研究繪製 view 的程式碼。

開啟一個 SwiftUI 的初始專案,修改 ContentView 如下:

其中有需要的圖片資源都在範例程式碼中,請記得取呦!現在我們應該可以看到以下畫面了!

alert-1

嗯,非常不錯!這次我們稍微設計了一下展示畫面與動畫,讓整體而言不要那麼枯燥!

設計思路

寫程式碼前我們通常需要設計一番,並思考需要哪些功能,而決定要應用怎麼樣的設計模式。

首先,我們先來分析需求!

需求 1:我們想要有一個不耦合於任何組件的 Alert
需求 2:我們想要有一個服務專門提供接口,上層只要提供選項與數據,由這一層服務幫我們完成繪製畫面,並回傳使用者選擇的選項
需求 3:這個選項應該要可以隨意擴充,以及應對不同商業邏輯的選項組合
需求 4:我們想要回傳選項時,可以讓流程很好的執行,不會造成 callback hell

為此,我們集成了兩個很常用的第三方組件 PromiseKitSwiftEntryKit,來解決我們的需求。

實作服務層

首先,我們先建立一個 class 為服務層入口 MessageService,並且我們使用單例模式來處理需求 1

接下來,我們實作 SwiftEntryKit 的容器:

EKAttributesSwiftEntryKit 設計中的一塊組件,它是整個 Alert 的容器,裡面可以放置其他 SwiftEntryKit 設計好的 view 或客製化 view,建議搭配官方文檔作參考。

接下來,我們建置一個 function,用來呼叫我們理想中的 Alert

showTableSelectionView 是一個泛型方法,範圍於 RawCaseable 內。RawCaseable 是我們自己定義的一個 protocol,用於生成選項列舉。

對於 RawRepresentableCaseIterable 的協定內容,可以參考官方文檔。這是為了方便於讓 enum 可以使用 foreach,來處理不特定數量與不特定標題的方式。

其中各個參數代表不同的東西:

  • title 代表這個 Alert 的顯示標題文字
  • description 代表描述文字
  • data 代表列舉選項的目標型別
  • image 代表 Alert 的圖示
  • imageSize 用於設定 image 的大小
  • isHaveCancel 代表是否要顯示取消的選項,預設是 true,並會返回一個 Promise,可以接續選擇後的處理流程

接下來,讓我們進入重點!

首先,建立一個 Promise

然後,我們開始根據傳入值繪製 Alert

根據官方文檔,我們將 titledescriptionimageisHaveCancel 參數繪製成符合 App 中樣式的組件,並放入 simpleMessage 中。

我們可以看到取消中的執行方法為:

這代表我們 resolver.rejectPromise,並且關閉了 Alert

接著,進入重頭戲 —— 繪製我們的商業邏輯領域選項:

這邊我們利用了剛剛設置好的協定,實現繪製選項與選項標題,並且於返回值 resolver.fulfill 使用者選的選項,加入到了 buttonsBarContent.content 列表,並且關閉了 Alert

最後,讓我們 display 繪製好的 Alert

大功告成!我們完成了統一、而可復用的 Alert

整個 function 如下:

使用接口

讓我們回到剛剛繪製好的 view 來執行 Alert 吧!我們建立一個 enum FoodKind 模擬使用者點餐的選項:

找到 CertificateViewonTapGesture 並且寫入:

此時,執行並點擊卡片,就會出現以下畫面:

試著點擊牛排或取消,你會發現我們可以知道使用者選取了甚麼:

讓我們再感受一下它的強大!我們建立遊戲職業種類的 enum HeroKind

找到 TitleView 中的 Image onTapGesture 寫入:

點一下圖,我們可以看到以下畫面:

恭喜你!我們已經解決了所有的需求,就是建立樣式統一、可復用、可自由擴充選項的 Alert

總結

本文所闡述的設計理念並不困難,我覺得實現程式碼時,先描述需求與設計思路是更重要的。有了明確的需求與設計思路,我們就可以很好地解決問題。如果在設計中遇到沒有想到的問題,我們可以透過反覆迭代與設計去解決複雜問題。希望這篇文章有帶給你幫助。感謝你的閱讀,祝你有個美好的 Coding 夜晚,下次見。

yasuoyuhao,自認為終身學習者,對多領域都有濃厚興趣,喜歡探討各種事物。目前專職軟體開發,系統架構設計,企業解決方案。最喜歡 iOS with Swift。

此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This