關於 macOS 輸入框你需要了解的一些基礎
這是 macOS 開發系列的第三篇文章 —— 文字輸入系統基礎。
電腦的文字編輯功能比手機上的強大(難搞)真不是吹!
認識輸入框
The Macintosh operating system has provided sophisticated text handling and typesetting capabilities from its beginning. In fact, these features sparked the desktop publishing revolution. —— Apple
在 macOS 的世界裡,要顯示或者編輯文字主要會用到兩個控制元件,一個是 ofollow,noindex">NSTextView ,另一個是 NSTextField 。
雖然控制元件庫裡面有個叫 Label 的東西,但是拖出來之後就會發現,它其實也是一個 NSTextField
它們的繼承關係是這樣的:

NSTextView 是蘋果花了大心血打造的一個“滿足幾乎所有顯示和管理文字需求”的一個控制元件,也是 macOS 引以為傲的文字編輯系統的主心骨;NSTextField 則相當於一個簡化版的 NSTextView,在大多數情況下它可以滿足字數較少的輸入需求。
正如我們在 iOS 開發裡常做的那樣,在需要使用者輸入的地方,通常是直接展示一個 Text 相關的控制元件,然後通過 delegate 方法來控制輸入的內容;又或者繼承一個官方的控制元件,然後在內部直接實現想要的內容控制邏輯。
在 macOS 上,這個流程也是大致相同的。
以前寫過一篇簡單介紹 NSTextView 用法的文章( Windows-in-macOS/" rel="nofollow,noindex" target="_blank">20分鐘手把手教你寫 macOS 文字編輯器 ),等不及的童鞋們可以在這篇文章裡過過癮。
文字輸入的幕後玩家
雖然從使用上來看,跟開發者直接打交道的就是 NSTextView 和 NSTextField 這兩個類,最多再加上它們帶著的一些協議/代理,但是繼續往深了看,會發現有一個未知的世界在支撐這這一切。
Field Editor
macOS 上有一個叫 Field Editor 的概念。
在輸入框獲得焦點的時候,系統會例項化一個 NSTextView 作為 Field Editor,並把它作為 first responder 插入到這個輸入框的事件響應鏈當中。如此一來,Field Editor 會負責處理所有的使用者輸入事件,在這個過程中,獲得焦點的輸入框會作為 Field Editor 的代理,以便對文字的內容進行控制處理。
這就是為什麼 NSWindow 的 firstResponder
返回的是一個不可見的物件,而不是我們獲取了焦點的輸入框,因為這個物件就是上面說的 Field Editor。
Field Editor 是同一個窗口裡所有輸入框共用的,所以在我們用 Tab 鍵切換輸入框的時候,Field Editor 就會切換事件響應的物件。另一方面,這個機制也確保了同一個視窗中只能有一個控制元件去響應使用者輸入事件。不過,我們也可以實現自定義的 Field Editor 來推翻上面說的這些功能。
雖然 Field Editor 一般會是一個 NSTextView 的例項,但是它們對 Tab 和 Return 的事件處理是不同的。對於 Field Editor 來說,這兩個鍵盤事件是“結束編輯”的意思。
NSTextInputContext
這是外界和文字輸入系統之間溝通的橋樑。
在使用者進行輸入的時候, keyDown
訊息會被傳遞到獲取了焦點的輸入框裡,輸入框接下來會呼叫 NSTextInputContext 的 handleEvent
方法,以便讓 NSTextInputContext 告訴自己需要怎麼處理這個使用者事件。而作為響應,NSTextInputContext 會把處理結果通過 NSTextInputClient 這個協議告知輸入框。
從上圖可以看到,NSTextInputContext 會跟一個叫 Key-bindings dictionary 的字典保持密切聯絡。這個字典預設來自於 AppKit 內部的一個檔案( /System/Library/Frameworks/AppKit.framework/Resources/StandardKeyBinding.dict ),裡面儲存了系統預設定義好的所有快捷鍵,只有當用戶輸入的值在這個字典裡找不到匹配的鍵值對時,這次輸入才會作為普通字元回撥給輸入框,否則 NSTextInputContext 就會在這裡把這次輸入攔截下來,並讓輸入框執行相應的特殊操作。
我們可以通過修改 ~/Library/KeyBindings/DefaultKeyBinding.dict 裡的值來覆蓋預設的快捷鍵
小結
到此為止,macOS 文字編輯系統的一些內在機理已經瞭解地差不多了。在往後使用 NSTextView 和 NSTextField 的過程中,碰到一些不明覺厲的問題也能有個大概的問題排查方向了。(不過也僅限於“大概方向”了)
好吧,我知道這篇文章偏理論了一些,大多數情況下也不會用到。更多詳細的說明可以在文章裡提供的各種連結上找到。
在後續的文章裡,還會講到 NSTextField 實際應用上的一些內容。NSTextView 因為暫時沒有用上,所以可能會等有機會研究清楚些再講咯。