iOS today extension (swift) 教學筆記
使用語言: Swift4,XCode版本: 9.4,模擬器版本: iOS11
today extension是iOS8就有的東西,不過之前一直沒用到
近期想寫個待辦事項的app,那就順便練習一下today widget吧
此筆記為記錄以下功能
1. 怎麼從專案中新增today widget
2. today extension的UI
3. today伸縮的按鈕
4. 從today中啟動App
5. today widget與APP之類如何傳遞資料(UserDefault)
1. 怎麼從專案中新增today widget
首先先新增一個空白專案
接下來再新增一個target(File -> New -> Target)
再選擇Today Extension,會發現裡頭非常多extension種類,可以善用filter,直接打上「today」就只剩一個了(請見附圖)
選擇today,再點選「Next」後會出現詢問視窗,直接點選「Activate」即可
新增完畢
這時候會發現專案的資料夾不一樣,而且執行的App的圖示也不同。
由這種資料夾分類方式也可看出,Apple不希望開發者把兩個搞混在一起。
App為一包資料夾,而Widget又是另外一包資料夾,其中Storyboard也是分開的。
基本上可以把App與Widget想成是兩件不一樣的事,這樣開發的時候比較不會有「啊~~怎麼傳個東西這麼麻煩」的感覺
如果你從E直接執行,就會發現畫面是停在Today的小工具頁。畫面應該會如下圖,有一個Widget,並且內容是白字的Hello World
2. today extension的UI
打開Widget的Storyboard,你可以發現只有一個ViewController,並且畫面超級小,也看不到那個Label在哪。
原因是Label預設是白字的(所以你執行起來才會看到白字的Hello World),而背景Default是透明,所以你是看不到Label的。
你可以先把ViewController放大(標示4、5處),你可以直接把Freeform改成Fix,這樣畫面就清楚多了,再將Label改成黑字
Apple滿建議直接用Autolayout的,因此就像你平常一樣使用Storyboard的方式一樣操作吧。
注意事項
比較要注意的是Widget並不建議使用滾動(你故意用也可以,你就會發現怎麼沒有正常滾動)。
也就是說ScrollView / TableView / CollectionView / Slider…這種東西都不能用,其實也是很好理解的,畢竟Today可以新增非常多個Widget,萬一每個Widget都可以上下滑,使用者就崩潰了;同理左右滑也一樣,到底是滑Widget還是在滑App的左右頁呢?
除了滑算以外,不應該有任何需要「輸入」的操作,也就是說不該發生彈出鍵盤的操作,也很好理解,大多使用者已經習慣「點畫面任一處,即縮下鍵盤」,但畫面通通都是Widget,點下去多半會觸發其它動作,這樣會非常容易讓使用者崩潰。
因此要記得「不要使用到任何操作滑動的元件」以及「不要輸入的操作」。
3. today伸縮的按鈕
如果有在用Today Widget的話,應該會注意到有些App可以切換展開與折合模式。(顯示更多 / 顯示更少)
這邊需要寫到程式碼,在Widget資料夾的TodayViewController中新增以下程式碼
下圖中的標號「3」(或見16行),以及標號「4」(或見20~25行)
寫了標號「3」,直接執行你會發現有出現按鈕,按了卻不會有反應
需要標號「4」,寫出模式改變時,整體的大小也需要改變,其中第22行我寫了高度要500,其實他有限制最大高度,似乎就是「手機的最大高度」,也就是就算我寫了「9999」,他也就只有手機那麼高而已了。
另外可以注意到第12行,他除了繼承一般的UIViewController以外,也有NCWidgetProviding,所以才有標題「3、4」這兩種方法。
附上程式碼,比較方便貼(貼上XCode後,再將貼上的程式碼反白選取,並且按下control+I即可自動縮排)
override func viewDidLoad() {super.viewDidLoad()self.extensionContext?.widgetLargestAvailableDisplayMode = .expanded}func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {if activeDisplayMode == .expanded {self.preferredContentSize = CGSize(width: maxSize.width, height: 500)}else {self.preferredContentSize = maxSize}}
4. 從today中啟動App
接下來,我們直接在Widget的Storyboard直接新增一個按鈕,並且拉線,準備要做點按鈕,開啟App的行為。
在寫程式之前,必須先新增Schemes,請參考以下附圖的標號順序
第6步的URL Scheme請你自己記好你寫的
接下來再回到TodayViewController,在IBAction點下的動作中寫下以下程式碼
要注意的是url是「YOUR_SCHEMES://」,不要貼的太開心,只貼到「SCHEMES」,否則點下按鈕會出現以下錯誤訊息
[_NCWidgetExtensionContext openURL:completionHandler:]_block_invoke failed: Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"
他會說找不到你要他開啟的Url
一樣,附上程式碼比較好貼
let url = URL(string: "YOUR_SCHEMES_NAME://")!self.extensionContext?.open(url, completionHandler: nil)
點下去之後應該就會出現一片白,是正常的。畢竟我們App自從新增專案後,就再也沒有去修改,因此預設畫面的確是全白。
5. today widget與APP之類如何傳遞資料(UserDefault)
這邊先介紹UserDefault,CoreData需要研究一下3口3
首先,先拉元件。
我們在App在Storyboard拉出TextField以及按鈕,按鈕負責存下TextField所輸入的內容;並在Widget的Storyboard中拉出一個Label,負責顯示App中所儲存的內容。
再來,需要讓App與Widget互相認識。
請參考以下附圖的標號流程操作,簡言之就是去把App Groups打開並新增Groups。
這裡有兩點要注意
- 點選標號6的「+」後,AppGroup的名稱不是隨便亂打,是打你的bundleID,bundleID可參考下圖標號流程
2.我框選的那兩個打勾圖示,是因為我這次做這個功能時,剛好Apple權限有修改,因此他原本是出現錯誤提示的。這時候只要去Itunes點選同意權限即可。
接下來,新增完App的Groups之後,一樣Widget也要重複操作一次。請參考下圖標號順序操作。這時候你應該不需要再打一次BundleID,而是可以直接勾選了。
好了,以上步驟即可完成讓他們互相認識的依據了。
最後,寫程式
分成兩部份,分法同UI,一個是App;另一個是Widget
先是App類,
在15行全域的部份先宣告UserDefault,其中要注意的是suitName就是你剛剛寫的AppGroup,並不是亂打的。
接下來IBAction的點選就負責把資料存起來。
一樣,附上程式碼比較好貼
let myUserDefault = UserDefaults(suiteName: "group.YOUR_BUNDLE_ID")
myUserDefault?.setValue(textField.text, forKey: "test")
App部份即完成了。
再來是Widget部份
基本上是跟App一樣的,開一個全域的UserDefault,接下來畫面要出現時再讀出UserDefault所儲存的內容。
直接執行後,即可完成。