iOS Keyboard Extension 開發(fā)筆記

keyboard_architecture_2x.png

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 如何通訊

detailed_communication_2x.png

這是蘋果官方給出的一張圖荆针,Containing App 是我們的主 App, Host app 是 Extension 所運(yùn)行的第三方 App(比如微信)敞嗡,為了方便理解下面我們會(huì)把 Containing App 稱為“主 App”,Host App 稱為“第三方 App”航背。

總結(jié)如下:

  1. App extension 無法和第三方 App 直接通信(簡單理解它們就是兩個(gè)不同的進(jìn)程喉悴,extension 啟動(dòng)了并不代表主App也會(huì)啟動(dòng))
  2. App extension 可以通過 open URL 和主 App通信,但這條鏈路只是單向的
  3. 主 App 和 App extension 可以通過讀寫共同的文件資源來通信(比如 UserDefault)
  4. 另外一個(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 Keyboard

Custom Keyboards - Extensions - iOS - Human Interface Guidelines - Apple Developer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末薄湿,一起剝皮案震驚了整個(gè)濱河市叫倍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌豺瘤,老刑警劉巖吆倦,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異坐求,居然都是意外死亡蚕泽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門桥嗤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來须妻,“玉大人,你說我怎么就攤上這事泛领』睦簦” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵渊鞋,是天一觀的道長绰更。 經(jīng)常有香客問我,道長篓像,這世上最難降的妖魔是什么动知? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮员辩,結(jié)果婚禮上盒粮,老公的妹妹穿的比我還像新娘。我一直安慰自己奠滑,他們只是感情好丹皱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宋税,像睡著了一般摊崭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上杰赛,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天呢簸,我揣著相機(jī)與錄音,去河邊找鬼。 笑死根时,一個(gè)胖子當(dāng)著我的面吹牛瘦赫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛤迎,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼确虱,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了替裆?” 一聲冷哼從身側(cè)響起校辩,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辆童,沒想到半個(gè)月后宜咒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胸遇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年荧呐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纸镊。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倍阐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逗威,到底是詐尸還是另有隱情峰搪,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布凯旭,位于F島的核電站概耻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏罐呼。R本人自食惡果不足惜鞠柄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫉柴。 院中可真熱鬧厌杜,春花似錦、人聲如沸计螺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽登馒。三九已至匙握,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陈轿,已是汗流浹背圈纺。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工秦忿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赠堵。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓小渊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茫叭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345