可調整高度的客製化鍵盤
Dcard 在 2021 年中上線了貼圖的功能,不知道大家是不是都用過了呢?本篇將分享我們如何實現客製化鍵盤與系統鍵盤互相切換的功能。
本文將提及如何「切換鍵盤」以及「動態改變鍵盤高度」,分為「切換鍵盤」和「鍵盤模板」兩個部分,不會提到貼圖鍵盤內容的實作方式。
切換鍵盤
在切換鍵盤的功能上,我們有幾個設計想要實現,以符合系統原生的行為:
- 如果畫面上沒有鍵盤,則鍵盤應該從螢幕底部滑入畫面內
- 如果畫面上已有鍵盤,則應立即切換至新的鍵盤
首先,若一個 UITextView 要使用客製化的鍵盤,系統有提供 UITextView.inputView
的參數來設定客製化的 view 作為鍵盤使用, 相反的,當UITextView.inputView
被設定為 nil
時,UITextView 則會使用系統鍵盤。我們先建立一個 function 來描述這件事。
這時我們會發現當 UITextView 正在編輯時( UITextView.isFirstResponder == true
),修改 UITextView.inputView
並沒有任何效果。這時候,當我們更新 .inputView
後,我們需要額外執行 UITextView.reloadInputViews()
讓 UITextView 更新目前的 .inputView
。
下面範例的最後,我們把 keyboard manager 的實體設定到 keyboard-template view 身上(line: 15)。這邊是為了讓 keyboard-template view 可以取得上次鍵盤高度的資訊,詳細會在下個章節「鍵盤模板」說明。
接下來,為了在切換鍵盤時,新的鍵盤可以和前一次的系統鍵盤一樣高,我們會需要記下最後鍵盤的高度,這邊透過接收 UIResponder.keyboardWillChangeFrameNotification
和 UIResponder.keyboardDidChangeFrameNotification
來獲取目前鍵盤的高度並記錄下來。同時,為了方便重複利用,我們將以上的功能封裝為 KeyboardManager。
你或許會想說既然我們都接收 keyboardWillChangeFrame
的事件了,為什麼還要接收 keyboardDidChangeFrame
的事件呢?這是因為在 iPad 上,iOS 14 的盤在 splitting 模式調整位置時,會先發送 keyboardWillChangeFrame
事件, 其keyboardFrameEndUserInfoKey
的值為 CGSize.zero
,等到型態切換完畢時,才會發送keyboardDidChangeFrame
,這時keyboardFrameEndUserInfoKey
的值才會是正確的。
// 開始拖曳 splitting keyboard
----- will change frame = (0.0, 0.0, 0.0, 0.0)
----- will change frame = (0.0, 0.0, 0.0, 0.0)// 結束拖曳
----- did change frame = (0.0, 197.0, 1112.0, 261.0)
----- did change frame = (0.0, 142.0, 1112.0, 316.0)
鍵盤模板
接下來我們要製作一個鍵盤的模板,這個鍵盤模板將會封裝高度設定及調整的功能。一般來說,我們會在 input view 被呈現前,設定 UIView.frame.size.height
來設定鍵盤的高度,但這個作法無法在鍵盤呈現後動態的改變高度。
為了要在鍵盤呈現後才改變高度,會需要在 input view 身上加上一個高度的 NSLayoutConstraint, 且 input view 的 .intrinsicContentSize
也需要回傳一樣的高度,這樣才能順利動作。
這邊我們建立一個 KeyboardTemplateView 來實作高度相關的功能,透過 .preferredHeight
來改變鍵盤高度,讓之後客製化的鍵盤可以直接繼承這個類別來實作。
接下來我們建立一個 HeightLayout 來描述鍵盤的高度,之後繼承 KeyboardTemplateView 實作鍵盤的類別,可以透過修改這個參數去設定鍵盤高度。高度類型分為 system
和 custom
兩種。system
會需要給定「最小」、「最大」及「預設」高度,在鍵盤呈當下畫面上沒有系統的鍵盤可以參照高度時,就會使用「預設」高度。若是從系統鍵盤切換過來的話,則使用最後系統鍵盤的高度,但不超過「最大」。
system
模式下「最小」高度同時會被套用在使用外接鍵盤的情況,在這種情況下,會沒有鍵盤的高度,因此需要一個最小高度來避免客製化鍵盤消失。
custom
則是直接指定鍵盤的高度,為了讓有固定高度的鍵盤可以不受系統鍵盤高度的影響。下面我們透過 enum 來建立 HeightLayout,在 KeyboardTemplateView.updatePreferredHeight()
中,實作上述的需求。
在上面的範例第 33 行處,在計算前一次鍵盤高度時,會扣掉當前的 frame.minY
原因是上方可能還有 accessory view 或是 assistant view (iPad 裝置),但系統 keyboardWillChangeFrame
通知中給的會是包含上述兩個元件的整體高度,因此扣除 frame.minY
才能得到鍵盤內容真正的高度。
整合起來
把上面的實作整合起來後,我們建立一個 ViewController 來展示實際上的應用方式。這邊我們有個繼承 KeyboardTemplateView 的 DemoKeyboardView,在點擊 switch-keyboard button 時,會切換至 demo keyboard。
DemoKeyboardView 是繼承自 KeyboardTemplateView,透過修改 .preferredHeight
來改變高度,這邊就讓大家自己試試看囉。
以上就是 Dcard 在實作客製化鍵盤上的分享,有任何問題都可以在留言與我們交流喔!
另外,最近我們正在招募 iOS 的新夥伴加入,iOS 團隊專注於開發社群、廣告、電商、跨國(香港及日本)等產品,想暸解更多的朋友們,可以一起來看看之前介紹 iOS 團隊介紹的文章,也歡迎你們來聊聊!
Senior iOS Developer — https://dcard.link/DCCCrm
iOS Developer, E-commerce — https://dcard.link/CFZT7F
Senior iOS Developer, Advertising — https://dcard.link/OXzgGU