用用看 ListFormatter, "烤肉、月餅和柚子"
目錄
本文使用環境或工具版本
這是在 WWDC 2019 上 723. Advances in Foundation 中提到的一個可以說是語法糖衣的東西。實務上除了 SNS 或是公司骨幹系統一些人性化的表示,是有點想不太到他可以用自哪邊 XDD
不過會想來玩玩是因為在 Twitter 上滑到最近這則 推文 和他提到的 Formatting Notes and Gotchas 於是就來認真玩玩看。
官方的文件
算是 Apple 的正常發揮,沒有進一步的詳細訊息 XDDD
但是這的類別也不會太複雜,看看 WWDC 的影片 和稍微試一下就可以知道怎麼用了。
還想要知道更細節可以看標頭檔,網頁上沒寫的這邊其實都寫的很清楚。由於篇幅滿大的所以我把它收闔起來,有興趣的人請再自己打開來看:
打開看 ListFormatter 的定義
1 | import CoreFoundation |
工具
本篇都是在 Xcode 12.0 的 Playground 裡面執行。
ListFormatter 可以做什麼
把一個資料的陣列,搭配 localization ,轉換成我們人看的懂得文字。
例如有以下的陣列,因為最近中秋節剛過就拿這個當例子吧:
1 | ["烤肉", "月餅"] // 兩筆 |
經過轉換之後,就會變成這樣。根據不同的語言,還會有不同的表示:
語言 | 轉換後的字串 - 兩筆 | 轉換後的字串 - 三筆 |
---|---|---|
zh_TW | 烤肉和月餅 | 烤肉、月餅和柚子 |
ja_JP | 烤肉、月餅 | 烤肉、月餅、柚子 |
en_US | 烤肉 and 月餅 | 烤肉, 月餅, and 柚子 |
en_UK | 烤肉 and 月餅 | 烤肉, 月餅 and 柚子 |
看了轉換後的字串可以發現,他不只幫我們挑語言適合的連接詞和逗號(頓號),還幫我們判斷兩個,或三個及以上該怎麼呈現都幫我們打理好了。在有這個糖衣類別出來之前我們都要自己寫一些 helper methods 來處理。
另外看了 Formatting Notes and Gotchas 才知道原來美國會比英國多一個逗號。多找了一下資料才發現這個叫 牛津逗號 的東西可以避免一些文書表達上的誤會 (參考 1, 參考 2)。
還有很驚訝日文沒有用 と
Get Hands Dirty
基本用法
先宣告一個陣列
1 | let moonFestival = ["烤肉", "月餅", "柚子"] |
使用 .localizedString(byJoining:)
最簡單的用法如下
1 | ListFormatter.localizedString(byJoining: moonFestival) |
就會得到以下的結果
1 | 烤肉, 月餅, and 柚子 |
用 .localizedString(byJoining:)
的時候,他會自動取得目前的 locale 資訊,組出相對應的字串並回傳
所以如果動態改變語言設定,他也會跟著改變:
1 | UserDefaults.standard.set(["zh_TW"], forKey: "AppleLanguages") |
初始化並自定 Locale
除了用系統預設的 locale 之外,初始化 ListFormatter 之後,也可以自行設定語言:
1 | let listFormatter = ListFormatter() |
非字串輸入與 itemFormatter
從產出格式化字串的方法定義可以知道,輸入的資料不是只有字串陣列,而是可以任何型別的 Any
陣列
1 | open func string(from items: [Any]) -> String? |
而我們也可以透過指定 itemFormatter
這個屬性來設定每一個元素要該怎麼格式化。
1 | open var itemFormatter: Formatter? |
如果沒有指定的話,還會幫我們 fallback 找到適合的字串組合好並回傳。
Date 陣列
以這個陣列為例
1 | let dates = [ |
厲害的地方是不用改變 itemFormatter
就可以有東西輸出:
1 | let listFormatter = ListFormatter() |
也可以設定自己的 date formatter :
1 | let dateFormatter = DateFormatter() |
自定義 model 陣列
Model 的定義和陣列如下
1 | struct Food { |
1 | let items = [ |
格式化時直接丟進去會變這樣:
1 | let listFormatter = ListFormatter() |
會印出赤裸裸的 struct 相關資訊的字串
這時候有至少兩個方法來避開
傳入前整形
這應該是最輕鬆的方法,透過 map 和 KeyPath 來整形輸入的字串。程式碼也很好閱讀和理解。
1 | lf.string(from: items.map(\.name)) |
自定義 Formatter
我們可以自定義一個 Formatter ,用點是可以把格式化的邏輯再封裝起來。
1 | final class FoodFormatter: Formatter { |
實作上雖然非 Food
型別的物件會回傳 nil ,但是 ListFormatter 內部在接到空值之後會自己在去找適合的字串來組合。
接著來套用看看:
1 | listFormatter.itemFormatter = FoodFormatter() |
從結果看,就可以看到輸出和自己預期的一樣了。
以上就是 ListFormatter 的一些基本和可能會有的變化用法,
這篇就到這邊,不妨也自己動手做做看吧!