Apple 在 iOS 8 里就引入了 Keyboard Extension著觉,但網(wǎng)上相關(guān)但開發(fā)但資料很少,我在開發(fā)中也遇到了不少坑晴圾,為了給大家分享下這方面但知識(shí)懦冰,所以才有了這篇文章灶轰。
Custom Keyboard 要實(shí)現(xiàn)起來也非常簡單,我們只需要在項(xiàng)目里新建一個(gè) Custom Keyboard Extension 的 Target刷钢,Xcode 就自動(dòng)會(huì)給我們創(chuàng)建一個(gè) KeyboardViewController笋颤,開發(fā)者通過這個(gè)類就可以做簡單的開發(fā)了。但是往往實(shí)際情況并沒有那么簡單内地。我們可能需要在鍵盤請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)伴澄,或者和Containing App 通信等等,這時(shí)我們會(huì)遇到很多問題阱缓。這邊文章我會(huì)講述一下常見的問題和解法非凌。
概要
- Extension 如何通訊
- 檢測(cè)鍵盤是否已經(jīng)添加
- 檢測(cè)鍵盤是否激活
- 播放系統(tǒng)聲音
- 檢測(cè)是否已經(jīng)獲取(最高訪問權(quán)限) RequestsOpenAccess
- 鍵盤切換方法
Extension 如何通訊
這是蘋果官方給出的一張圖荆针,Containing App 是我們的主 App, Host app 是 Extension 所運(yùn)行的第三方 App(比如微信)敞嗡,為了方便理解下面我們會(huì)把 Containing App 稱為“主 App”,Host App 稱為“第三方 App”航背。
總結(jié)如下:
- App extension 無法和第三方 App 直接通信(簡單理解它們就是兩個(gè)不同的進(jìn)程喉悴,extension 啟動(dòng)了并不代表主App也會(huì)啟動(dòng))
- App extension 可以通過 open URL 和主 App通信,但這條鏈路只是單向的
- 主 App 和 App extension 可以通過讀寫共同的文件資源來通信(比如 UserDefault)
- 另外一個(gè)方式這里沒提到:就是可以用更底層的 DarwinNotify 來建立 Extension 和 Containing app 之間的通訊玖媚,可以參考下面的??開源庫
choefele/CCHDarwinNotificationCenter
通過 App Group 共同維護(hù) UserDefault 是一種比較簡單的通訊方法箕肃。但是開發(fā)者也需要注意的是如果我們的鍵盤沒有獲取到?jīng)]有完全訪問權(quán)限,鍵盤是只能讀取今魔,沒法修改 UserDefault 的值的(如果這個(gè) UserDefault 是 Containing app 創(chuàng)建的)勺像。另外一點(diǎn)是 DarwinNotify 現(xiàn)在也需要完全訪問權(quán)限了。
最后給大家一個(gè)忠告: 千萬一定要在真機(jī)上調(diào)試错森!
檢測(cè)鍵盤是否已經(jīng)添加
var isKeyboardEnabled: Bool {
guard let keyboards = UserDefaults.standard.object(forKey: "AppleKeyboards") as? [String] else {
return false
}
return keyboards.contains("你的 extension bundle id")
}
檢測(cè)鍵盤是否已經(jīng)激活
鍵盤類型的應(yīng)用往往有個(gè)需求是:當(dāng)用戶第一次切換到我們開發(fā)的 Keyboard咏删,需要在主 App 顯示歡迎??圖文或者開啟引導(dǎo)流程。我們可以通過 openURL
的方式來通知主App问词。
// Step1: 在 keyboard 中調(diào)用
// 打開主APP,比如 openURL(scheme:"yourAppScheme://actived")
func openURL(scheme: String) {
let url = URL(string: scheme)!
let context = NSExtensionContext()
context.open(url, completionHandler: nil)
var responder = self as UIResponder?
let selectorOpenURL = sel_registerName("openURL:")
while (responder != nil) {
if responder!.responds(to: selectorOpenURL) {
responder!.perform(selectorOpenURL, with: url)
break
}
responder = responder?.next
}
}
// step2:
// app 在前臺(tái)的時(shí)候接收通知
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
if url.scheme == "yourAppScheme" && url.host == "actived" {
// do something
}
return true
}
這里要注意的是openURL 會(huì)喚起主 App(如果主 App 不在前臺(tái))嘀粱,所以這里需要額外做判斷激挪。
播放系統(tǒng)聲音
// 直接上代碼
// 點(diǎn)擊: SystemSoundID = 1123
// 刪除按鈕: SystemSoundID = 1155
// 系統(tǒng)鍵盤: SystemSoundID = 1156
AudioServicesPlaySystemSound(SystemSoundID)
檢測(cè)是否已經(jīng)獲瘸浇啤(最高訪問權(quán)限) RequestsOpenAccess
// Keyboard Extension
override var hasFullAccess: Bool {
if #available(iOS 11.0, *) {
return super.hasFullAccess// super is UIInputViewController.
}
if #available(iOS 10.0, *) {
let original: String? = UIPasteboard.general.string
UIPasteboard.general.string = " "
let val: Bool = UIPasteboard.general.hasStrings
if let str = original {
UIPasteboard.general.string = str
}
return val
}
return UIPasteboard.general.isKind(of: UIPasteboard.self)
}
主 App 沒有直接獲取的方法,但我們可以通過上面提到的通訊方法垄分,在 Keyboard Extension 中把狀態(tài)傳過去
全面屏幕隱藏切換 keyboard 按鍵
在全屏但設(shè)備系統(tǒng)會(huì)給我們默認(rèn)提供切換鍵盤但按鈕宛篇,所以我們開發(fā)的鍵盤就不需要提供此按鈕了,我們可以通過以下方式來判斷:
// needsInputModeSwitchKey
var needsSwitchKey: Bool {
if #available(iOSApplicationExtension 11.0, *) {
return needsInputModeSwitchKey
} else {
return true
}
}
其他資料
Custom Keyboards - Extensions - iOS - Human Interface Guidelines - Apple Developer