https://www.jianshu.com/p/0713849954de
(本文來自《Custom Keyboard》)
自定義鍵盤為那些希望體驗更新穎的輸入法或者需要用到iOS不支持的語言的用戶,提供了替代系統鍵盤的備選。自定義鍵盤的核心功能很簡單:響應按鍵、手勢或其它輸入事件,並提供轉換后的文本字串,並將該字串插入到當前的光標位置。
開始閱讀前
請先確認你需要開發的的確是系統范圍的自定義鍵盤。如果只是希望在你的app內部提供可自定義的鍵盤,可以到《Custom Views for Data Input 》和《Text Programming Guide for iOS》了解自定義輸入視圖
和輸入輔助視圖
的相關內容,iOS SDK為此提供了更好的備選方案。
當用戶選擇了自定義鍵盤,該鍵盤即成為每個app的鍵盤。因此,你創建的鍵盤必須包含一些基本的功能。其中最重要的是,必須允許用戶切換到其它鍵盤。
理解用戶對鍵盤的預期
要理解用戶對於自定義鍵盤的預期,可以系統鍵盤為標桿——它反應靈敏且高效。它不會用垃圾信息或請求打斷用戶輸入。如果你提供了需要用戶交互的功能,應該把它們放到鍵盤的app里,而不是鍵盤上。
iOS用戶預期的鍵盤功能
每個自定義鍵盤都必須提供的iOS用戶所預期的鍵盤功能是:切換到其它鍵盤的方法。在系統鍵盤中,有一個小地球的按鍵用來完成此功能。iOS 8也提供了專門的API用於切換到下一個鍵盤,可以參見《提供一種切換到其它鍵盤的方法》。
系統鍵盤會根據當前文本輸入對象的UIKeyboardType屬性,展現與之匹配的鍵盤布局。如果當前的輸入對象需要輸入郵箱,系統鍵盤的句號建就會變化:長按會冒出一些頂級域名的后綴作為候選。你在設計自己的鍵盤布局時也應當考慮到當前的輸入對象屬性。
iOS用戶還期望自動大寫:在一個標准的文本輸入區域,對於大小寫敏感的語言來說,應當讓句首的字母自動大寫。
這類功能列出如下:
- 對輸入對象的屬性考慮適當的鍵盤布局
- 自動糾錯和建議
- 自動大寫
- 兩個空格后自動添加句號
- Caps lock鍵的支持
- 鍵帽上的美觀
- 對於象形文字的多層轉換
你可以自行決定是否實現這些功能;系統並沒有為這些功能提供專用的API,在自己的輸入法中提供這些功能可以讓你的產品更有競爭力。
系統鍵盤中的哪些功能不適用於自定義鍵盤
自定義鍵盤不能訪問在系統設置中的通用鍵盤設置數據(設置 > 通用 > 鍵盤),比如自動大寫、使Caps Lock可用。自定義鍵盤也不能訪問字典還原信息(設置 > 通用 > 還原 > 還原鍵盤詞典)。要滿足你用戶的靈活性要求,你應該創建一個標准的設置bundle,這個話題在《偏好和設置編程指南》的《實現iOS設置Bundle》中有討論。這樣你的自定義鍵盤的設置就會出現在系統設置的鍵盤區域。
還有一些文本輸入對象,自定義鍵盤是不能在其上進行輸入的。首先就是任何安全相關的文本輸入對象。這類文本輸入對象設置了其secureTextEntry屬性為YES
,在其上的輸入內容將呈現為圓點。
當用戶在密碼框里輸入時,系統會臨時用系統鍵盤來替代自定義鍵盤。當用戶在非密碼框里輸入時,自定義鍵盤又會恢復回來。
自定義鍵盤也無權在撥號輸入的位置出現,例如通訊錄的電話號碼輸入區域。對於這類輸入對象,鍵盤是由運營商指定的一個數字/字母的小集合組成,並且具備如下屬性:
UIKeyboardTypePhonePad
UIKeyboardTypeNamePhonePad
當用戶點擊撥號輸入對象時,系統將臨時用系統鍵盤替換掉你的自定義鍵盤。當用戶再點擊其它標准輸入對象時,自定義鍵盤又會恢復回來。
app的開發者可以選擇在app內部不使用自定義鍵盤。例如銀行類app,或者必須遵守美國HIPAA隱私規則的app,可以這么干。這類app實現來自UIApplicationDelegate
協議的application:shouldAllowExtensionPointIdentifier:方法,並返回NO,以達到使用系統鍵盤的效果。
由於自定義鍵盤只能繪制其UIInputViewController對象內的主視圖,在它上面不能選擇文字。選擇文字是使用鍵盤的應用程序控制的。如果app提供了編輯菜單(如剪切、拷貝和粘貼),鍵盤是無權訪問它的。自定義鍵盤不能提供在光標位置的自動inline糾錯能力。
在iOS8.0下,如所有擴展app一樣,自定義鍵盤不能訪問麥克風,因此不能實現語音輸入。
最后,顯示插圖不能超過鍵盤的主視圖上邊緣,系統鍵盤可以,但自定義鍵盤不行。如下圖,可以發現自定義鍵盤和系統輸入法的差別:

自定義鍵盤API
本節將給出開發自定義鍵盤的快速入門。如下圖,它展示了鍵盤運行過程中一些重要的對象,以及它們在開發流程中的的位置:

自定義鍵盤模板(在iOS“Application Extension”目標模板組)包含一個UIInputViewController的子類,它是你開發的鍵盤的主視圖控制器。該模板包含鍵盤所必需的“下一個鍵盤”按鈕的實現,它調用了UIInputViewController
類的advanceToNextInputMode方法。如上圖所示,可以在輸入視圖控制器的主視圖(在其inputView屬性)中添加子視圖、控制器以及手勢識別器等。對於其它類型的擴展應用,在目標上並不存在窗體,因此也就沒有根視圖控制器了。
在模板的Info.plist
文件中有預先配置好的鍵盤所需要的最基本的值。參見其中的NSExtensionAttributes
字典關鍵字,配置一個鍵盤的關鍵字在《配置自定義鍵盤的Info.plist文件》中有介紹。
默認,鍵盤不能訪問網絡,不能和它的app共享容器。如果要具備這種能力,必須要將Info.plist
文件中RequestsOpenAccess
的值置為YES
。這需要擴展鍵盤的沙盒,在《設計用戶信任》中有介紹相關內容。
一個輸入視圖控制器遵從各種與文本輸入對象內容交互的協議:
- 響應觸摸消息時如果要插入或刪除文本,可以使用UIKeyInput協議的insertText:和deleteBackward方法。可以在視圖控制器的textDocumentProxy屬性中調用這些方法,該屬性代表當前文本輸入對象,它遵從UITextDocumentProxy協議。如下:
[self.textDocumentProxy insertText:@"hello "]; // Inserts the string "hello " at the insertion point [self.textDocumentProxy deleteBackward]; // Deletes the character to the left of the insertion point [self.textDocumentProxy insertText:@"\n"]; // In a text view, inserts a newline character at the insertion point
- 在調用deleteBackward之前要先決定刪除的字符數。可以通過textDocumentProxy的documentContextBeforeInput屬性,來獲得光標附近的文本上下文信息。如下:
NSString *precedingContext = self.textDocumentProxy.documentContextBeforeInput;
然后就可以刪除你指定的文字區域了,比如單個字符還是空格后的所有字符。如果要按照語義執行刪除,比如一個單詞、句子、還是一個段落,可以使用[《 CFStringTokenizer Reference》](https://developer.apple.com/reference/corefoundation/cfstringtokenizer-rf8)中描述的函數,注意每個語種的語義規則是不同的。
- 為了控制光標所在位置的操作,比如支持向前刪除文字,需要調用
UITextDocumentProxy
協議中的adjustTextPositionByCharacterOffset:方法。比如向前刪除一個字符,代碼如下:
- (void) deleteForward { [self.textDocumentProxy adjustTextPositionByCharacterOffset: 1]; [self.textDocumentProxy deleteBackward]; }
- 通過實現UITextInputDelegate協議中的方法,可以響應當前輸入文本對象的一些變化,比如內容變化以及用戶觸發的光標位置的變化。
為了展現與當前文本輸入對象適配的鍵盤布局,需要參照該對象的UIKeyboardType屬性,根據每種你的鍵盤所能支持的屬性,變化布局內容。
在自定義鍵盤中,有兩種方式來支持多語言:
- 為每個語言創建一個鍵盤,每個鍵盤都作為向容器app添加的獨立的Target
- 創建一個多語言鍵盤,動態切換當前語言。可以使用
UIInputViewController
類的primaryLanguage屬性來動態切換語言。
根據你要支持的語言數量以及你想提供的用戶體驗,你可以從上面選擇最合適的方案。
每種自定義鍵盤(需要RequestsOpenAccess
)都可以通過UILexicon類訪問自動糾錯的詞典。通過使用該類,並結合你自己的詞典設計,可以在用戶輸入過程中為他提供輸入建議和自動糾錯。UILexicon
對象包含來自如下源的單詞:
- 來自用戶通訊錄的人名和姓
- 在 設置 > 通用 > 鍵盤 > 快捷方式(文本替換) 列表
- 通用詞典
你可以使用自動布局來調整你的自定義鍵盤主視圖的高度。默認情況下,自定義鍵盤會根據屏幕尺寸以及設備方向,和系統鍵盤的尺寸保持一致。自定義鍵盤的寬度通常與屏幕當前寬度一致。修改自定義鍵盤主視圖的高度約束即可修改其高度。
下面的代碼展示如何定義和添加約束:
CGFloat _expandedHeight = 500; NSLayoutConstraint *_heightConstraint = [NSLayoutConstraint constraintWithItem: self.view attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 0.0 constant: _expandedHeight]; [self.view addConstraint: _heightConstraint];
注意
在 iOS8.8下,你可以在主視圖畫到屏幕之后的任何時間里調整鍵盤高度。
自定義鍵盤的開發關鍵
自定義鍵盤開發有兩個關鍵點:
- 信任。 自定義鍵盤能訪問用戶輸入的內容 ,因此在鍵盤和用戶間建立信任非常關鍵。
- “下一個鍵盤”鍵。 通過鍵盤界面必須能讓用戶能切換到下一個鍵盤。
為用戶信任所做的設計
作為自定義鍵盤的開發者,你首先應當考慮的是如何建立和維護用戶信任。你要理解隱私策略的最佳實踐並知道如何實現它才能很好地踐行。
注意
本節為你創建自定義鍵盤提供相關的開發手冊,該手冊要求尊重用戶隱私。了解iOS編程要求,請閱讀應用商店審核手冊
,iOS人機交互手冊
,iOS開發許可協議
,請參見蘋果的《應用審核支持》,《支持用戶隱私》,《iOS應用編程指南》。
對於鍵盤,如下三個方面對於建立和維護用戶信任至關重要:
按鍵數據的安全。 用戶希望他們的敲鍵會落在文檔以及輸入區域內,而不是上傳到服務器或者用於其他不明目的。
最小化合理利用其它用戶數據。 如果你的鍵盤還需要使用其他用戶數據,例如定位服務或者通訊錄,你有義務解釋這給用戶帶來的好處是什么。
准確。把輸入事件轉換成文本要求精准,這本身雖然不是一個隱私話題,但他會影響到信任:每次文字轉換需要體現出你的代碼的精准。
在信任的開發設計過程中,首先考慮的是是否要獲取open access權限。盡管開啟了open access權限能給自定義鍵盤開發帶來極大便利,但這也增加了你作為開發者的責任。下面是標准的open access的能力和隱私考慮:
Open Access | 能力和限制 | 隱私考慮 |
---|---|---|
Off(default) | ·鍵盤可以執行所有基本鍵盤的職責 |
·可以訪問通用詞典以支持自動糾錯和輸入建議
·訪問設置里的快捷短語
·不與containing應用共享容器
·不訪問鍵盤容器以外的文件系統
·不訪問鍵盤容器以外的文件系統
·不能直接或間接訪問iCloud或游戲中心或應用內購買 | 用戶了解按鍵僅僅被發送到當前使用鍵盤的應用里 |
| On | ·具備非聯網自定義鍵盤的所有能力
·在用戶許可情況下可以訪問位置服務和通訊錄
·鍵盤和containing app可以訪問共享容器
·鍵盤可以為服務器側處理過程發送按鍵或其他輸入事件
·containing app自動糾錯字典提供編輯界面
·通過containing app鍵盤可以使用iCloud來保證自動糾錯詞典和設置的更新
·通過containing app,鍵盤可以參與到游戲中心和應用內購買
·如果鍵盤支持移動設備管理(MDM),它可與被管理的應用共同工作 | ·用戶了解鍵盤開發者會利用按鍵數據
·你必須遵守有聯網能力的鍵盤開發手冊
和iOS開發許可協議
,可參見《應用審核支持》 |
如果你的自定義鍵盤不需要open access權限,系統確保敲鍵信息不會被發送給你的鍵盤以及別的地方。如果只想提供一般的鍵盤功能,請不要給鍵盤配備聯網能力。由於有沙盒限制,不聯網的鍵盤一定是滿足蘋果的數據隱私手冊並能獲得用戶信任的。
開啟open access權限(如上所述,可以在Info.plist文件中配置),能給你的開發帶來更多可能性,同時也帶來更多的責任。
注意
向應用商店提交一個open-access的鍵盤必須遵守蘋果《應用審核支持》中的相關條款。
每一個與open access相關的功能都需要你履行相應的責任,應當最大限度地尊重用戶數據,不得用於與用戶輸入無關的其他任何目的。下表列出了open access帶來的好處以及開發者需承擔的責任:
能力 | 用戶利益示例 | 開發者責任 |
---|---|---|
與containing app共享容器 | 為鍵盤的自動糾錯詞典管理UI界面 | 要考慮到自動糾錯數據屬於用戶隱私。不要把他發到你的服務器,用作與輸入無關的用途。 |
把按鍵數據發到你的服務器 | 通過開發者的計算資源可以提供更好的按鍵處理結果和輸入預測 | 只有為用戶提供更好的輸入體驗之用時,才能保存按鍵和語音數據 |
基於雲的自動糾錯詞典 | 把人名、地名、熱點新聞加入到自動糾錯詞典中 | 不要把用戶身份與輸入數據關聯起來,不得將用戶信息用作與輸入體驗無關的其他目的 |
通訊錄 | 把人名、地名、電話號碼添加到自動糾錯詞典中 | 不得講通訊錄用作與輸入體驗無關的其他目的 |
位置服務 | 將附近的地名添加到自動糾錯詞典中 | 不要在后台使用位置服務,不得將位置信息發送到你的服務器並用於與輸入體驗無關的其他目的 |
一個具有open-access權限的鍵盤和其containing app能將按鍵數據發送到服務器端,通過這些數據可以為用戶提供更好的輸入體驗。如果你使用了這些能力,當不需要這些數據的時候,請及時在服務器端刪除。參見上面的表格來履行你使用open-access權限中的義務。
提供切換到其他鍵盤的方法
系統鍵盤的小地球按鍵用於切換到其他鍵盤,如下所示:

你的自定義鍵盤必須提供類似的機制能切換到其他鍵盤。
注意
要通過應用審核,必須在你的鍵盤上提供明顯允許用戶切換鍵盤的UI標識。
調用UIInputViewController類的advanceToNextInputMode方法可以切換到其他鍵盤。系統會選擇下一個鍵盤,沒有能獲得鍵盤列表的API,也沒有切換到指定鍵盤的API。
Xcode自定義鍵盤模板中就已經在下一個鍵盤
按鈕上具備了advanceToNextInputMode的功能。為了提供最好的用戶體驗,應當把你的下一個鍵盤
按鍵放在靠近系統鍵盤的小地球鍵的位置。
開始自定義鍵盤的開發
本節中你將學習到如何創建自定義鍵盤,根據你的目標配置並在iOS模擬器或物理機上把它運行起來。你還將學習到一些替代系統鍵盤應謹記的UI要點。
使用Xcode自定義鍵盤模板
創建鍵盤及其containing app與其他擴展應用略有不同。本節將帶你領略基本鍵盤的開發和運行。
在一個容器app中創建鍵盤,步驟如下:
- 在Xcode中選擇
File > New > Project > iOS > Application
選擇Single View Application
模板。 - 點擊
Next
。 - 填寫
Project Name
(如CKIme),點擊Next
。 - 選擇要保存的位置,點擊
Create
。這樣,你就有了一個空app,該app只能完成一個簡單的操作,接下來它將承載鍵盤。在你提交到應用商店之前,你需要完成一些有用的功能。請到應用審核支持參考應用商店審核指南
。 - 選擇
File > New > Target > iOS > Application Extension
選擇Custom Keyboard Extension
,點擊Next
。 - 填寫
Product Name
(如CKbd),點擊Finish
。 - 確認
Project
和Embed in Application
中都顯示的是容器app的名字(CKIme),點擊Finish
。如果彈出Activate “CKbd” scheme
提示讓激活鍵盤工程,點擊Activate
。
接下來你可以根據需要決定是否要自定義鍵盤的group name,它會出現在設置中的已購買鍵盤列表中。
自定義鍵盤group name,步驟如下:
- 在Xcode工程導航視圖中,選擇容器app的
Info.plist
文件, - 在右側plist編輯器中,鼠標hover到
Bundle name
上,點“+”按鈕創建一行空屬性。 - 在Key中填寫
Bundle display name
,回車 - 雙擊該行的Value,填寫你要自定義的鍵盤group name。
- 選擇
File > Save
保存設置。
下表匯總了在容器app和鍵盤app的Info.plist
文件中你可以配置的UI字符串:
iOSUI字符串 | Info.plist關鍵字 |
---|---|
· 在系統設置的已購鍵盤列表中的鍵盤group name | 在容器app的Info.plist文件中的Bundle display name |
· 系統設置中的鍵盤名稱 | |
· 鍵盤換列表中的鍵盤名稱 | 在鍵盤app的Info.plist文件中的Bundle display name |
現在你可以在iOS模擬器或真機上運行該鍵盤,看看它目前都具備什么行為和能力吧。
運行自定義鍵盤並將Xcode調試器attach到它上面
- 在Xcode,你的view controller實現中設置一個斷點(比如可以斷在viewDidLoad上)。
- 在Xcode工具欄確保當前活動的項目為鍵盤項目,並對應iOS模擬器或設備。
- 選擇菜單
Project > Run
,或點擊Build and then run the current scheme
按鈕(即播放按鈕)。Xcode會提示選擇host app。選擇一個帶有輸入框的,比如通訊錄或Safari。 - 點擊
Run
。
Xcode將運行起你指定的host app。如果這是你第一次使用鍵盤擴展應用,需要現在設置中添加並啟用鍵盤:Settings > General > Keyboard > Keyboards
- 點擊
Add New Keyboard...
- 在
OTHER IPHONE KEYBOARDS
中點擊你剛剛創建的鍵盤
- 在iOS模擬器或真機上,調出你的自定義鍵盤。
點擊任意可輸入區域,將顯示出系統鍵盤。按住小地球,選擇你的自定義鍵盤。
此時你將看到自定義鍵盤,但是調試器尚未attach上來。一個從模板構建而來的極簡鍵盤僅有一個Next Keyboard
按鈕,點擊后切換回前一個鍵盤。 - 取消你的鍵盤(以便在第8步中你可以再次調出鍵盤以命中
viewDidLoad
斷點) - 在Xcode中,選擇
Debug > Attach to Process > By Process Identifier(PID) or Name
在彈出對話框中,輸入你的鍵盤擴展應用的名字(包含空格).默認就是該擴展應用在工程導航窗口里的group name。 - 點擊
Attach
。
Xcode將顯示出等待attach的調試器。 - 在任意能輸入文字的app中調出鍵盤。
當你的鍵盤主視圖開始加載時,Xcode調試器將attache到你的鍵盤,並命中斷點。
為自定義鍵盤配置Info.plist文件
自定義鍵盤的Info.plist文件允許靜態定義鍵盤的現式特征,包括主要語言,以及是否需要open access權限。
打開Xcode並切換到自定義鍵盤的target。在工程導航欄選擇Info.plist文件,按文本格式呈現如下:
<key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>IsASCIICapable</key> <false/> <key>PrefersRightToLeft</key> <false/> <key>PrimaryLanguage</key> <string>en-US</string> <key>RequestsOpenAccess</key> <false/> </dict> <key>NSExtensionPointIdentifier</key> <string>com.apple.keyboard-service</string> <key>NSExtensionPrincipalClass</key> <string>KeyboardViewController</string> </dict>
每個關鍵字在App Extension Keys中都有解釋。可以使用字典NSExtensionAttributes
中的關鍵字來描述你的自定義鍵盤的特征和需求,如下:
IsASCIICapable
- 默認為NO的布爾值。用戶鍵盤是否可以向文檔中插入ASCII字串。如果要為UIKeyboardTypeASCIICapable
屬性的輸入對象展現單獨類型的鍵盤,需要將該值置為YES。
PrefersRightToLeft
- 默認為NO的布爾值。是否為從右到左的語種設計的的自定義鍵盤。
PrimaryLanguage
- 默認為en-US
的字串。以<語種>-<區域>的形式描述鍵盤的主語言。可以到http://www.opensource.apple.com/source/CF/CF-476.14/CFLocaleIdentifier.c找到對應的語種和區域。
RequestsOpenAccess
- 默認為NO的布爾值。是否需要比基礎鍵盤更大的沙盒范圍。把該值置為YES將需要完全訪問
權限,你的鍵盤將獲得如下能力,每個能力都伴隨有相應的權限:
- 訪問定位服務,通訊錄數據庫,相機,每個都需要用戶允許
- 與鍵盤的容器app共享容器數據,以便完成比如在容器app中管理用戶詞庫的界面的功能
- 通過網絡發送按鍵、輸入事件之類的數據供雲端處理
- 使用UIPasteboard類
- 播放音頻,包括使用playInputClick方法播放按鍵音
- 訪問iCloud,可以用來根據用戶身份同步比如鍵盤設置、自定義自動糾錯詞典
- 通過容器app訪問游戲中心和應用內購買
- 如果你的鍵盤支持移動設備管理(MDM),可以與被管理的app無縫合作
當考慮是否將這些關鍵字設置為YES之前,一定要先閱讀《用戶信任設計》,這里描述了如何尊重和保護用戶數據。
作者:RickMao
鏈接:https://www.jianshu.com/p/0713849954de
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。