完美主義:實現iOS輸入框自動移動

發表於2015-09-23

iOS“資訊”中輸入框互動式彈回。

在設計以輸入框(接受並顯示文字輸入)為主的App使用者介面時,常見的模式是輸入框自動移動(一直顯示在螢幕上),輸入框位於鍵盤的頂部並隨著鍵盤的移動而移動。

Apple的“訊息”是一個很好的例子,它的UI就屬於上述模式。從iOS 7起,它還可以通過滾動訊息內容互動式彈回鍵盤。

這種模式很常見(尤其是聊天類App),但很少有App運用地像Apple的“訊息”這麼好。眾所周知效仿的例子有Facebook MessengerWhatsApp

這並不是什麼了不得的事,但也並不簡單

能夠把鍵盤互動式彈回並不是什麼了不得的事,這可能是大多數使用者都不會注意到的小細節。但是,它的確增強了App的手感。

然而不幸的是,解決方案並不明朗。開發人員已經嘗試了很多方案並取得了不同程度的成功。有幾個相關的開源專案,相關的博文和相關的StackOverflow提問,提供了從簡單到複雜的解決方案。

簡而言之,實現輸入框自動移動很簡單。但互動式彈回鍵盤就複雜了。以下是一些流行聊天App的嘗試:

Google Hangouts:互動式彈回,輸入框移動,鍵盤覆蓋在上面隨之移動。缺陷:彈回時,鍵盤和輸入框之間的有空隙。

Slack for iPad:不支援互動式彈回,不過它在iPhone上是支援的。

Telegram、Line、微信:不支援互動式彈回。

收集資料

在尋找解決方案時,我發現了一些部落格詳細介紹了一些非常有用的技術,這完成了我們的解決方案的大半:賦給UIViewController根檢視的inputAccessoryView一個非空的值,並讓它在載入時立即響應(first responder)。詳情見此:https://robots.thoughtbot.com/input-accessorizing-uiviewcontrollerhttp://derpturkey.com/uitextfield-docked-like-ios-messenger/

不管怎樣,這至少說明了兩點:

  • 輸入框是鍵盤的視窗的子檢視。我們並不想這樣,尤其是對聊天App。
  • 輸入框的寬度擴大了鍵盤的寬度。除了iPad上的拆分鍵盤。

醞釀解決方案

我們已經知道,賦給inputAccessoryView一個非空的值能達到我們想要的效果,但會出現一些警告。

那麼,如果:

  • 我們賦給inputAccessoryView一個不可見檢視
  • 同時為了得知鍵盤frame的變化(作為互動式彈回的結果),我們使用KVO(鍵值觀察)觀察不可見檢視的父檢視(即鍵盤)的bounds/center屬性

彙總

解決方案設計

基於前面描述的想法,上圖展示瞭解決方案的各關鍵部分。這裡我們把不可見的檢視稱為“Pseudo Input Accessory View”。

  • 該解決方案的核心是鍵盤跟蹤器。它通過兩個來源跟蹤並儲存鍵盤狀態:鍵盤通知(UIKit keyboard notifications)和Pseudo Input Accessory View父檢視邊界/中心變化的回撥。
  • Pseudo Input Accessory View Coordinator管理Pseudo Input Accessory View的單例,同時提供了改變其高度的方法(為了滿足輸入框變寬的需要)。
  • Pseudo Input Accessory View由其Coordinator建立和管理。它使用KVO跟蹤父檢視的bounds/center屬性並向Coordinator報告。

在這基礎上,App在任何時候可以通過鍵盤跟蹤器的例項查詢當前鍵盤的狀態。並且通過跟蹤器的委託回撥實時更新鍵盤。

根據鍵盤當前的frame調整輸入框的佈局,這就是隨著鍵盤“自動移動”。在跟蹤器的委託回撥裡,我們只需要在必要時重新調整佈局。

管用嗎?

是的,我認為我們已經搞定它了!

實現自動移動輸入框的 Pie for iPad

以上是一個Pie for iPad的demo,並實現了自動伸縮輸入框。

開源解決方案

在Pie中,我們努力打造最好的工作交流體驗。但除此之外,我們也愛回饋社群。因此,我們打包了上述的解決方案包括一個demo作為一個小的開源庫。

我們希望這對其他有類似要求的開發人員是有用的。如果你感興趣,請參閱https://github.com/meiwin/NgKeyboardTracker

相關文章