在iOS8 下用Swift 建立自定義的鍵盤

本文翻譯自How to make a custom keyboard in iOS 8 using Swifthtml

我將講解一些關於鍵盤擴展的基本知識,而後使用iOS 8 提供的新應用擴展API來建立一個莫斯碼鍵盤。大概須要你花20多分鐘來走完全部的步驟。 完整代碼ios

綜述

一個自定義的鍵盤會替換系統的鍵盤,來提供給用戶一個新的文本輸入方法,或者輸入哪些iOS系統還不支持的語言。一個自定義鍵盤的基本功能很簡單:響應點擊,手勢或者其它輸入事件以及在當前的文本輸入對象的文本插入點上提供非屬性化的NSString對象的文本。git

當用戶選擇了一個鍵盤,那麼當用戶打開一個app時,這個鍵盤會做爲默認的鍵盤顯示。所以這個鍵盤必須容許用戶切換到另外一個鍵盤。github

對於每一個自定義鍵盤,有兩個開發要素:數據庫

信任 : 你的自定義鍵盤能夠訪問用戶輸入的每一個字符,因此你和你用戶之間的信任很是重要。swift

下一個鍵盤按鍵 : 可以讓用戶切換另外一個鍵盤這種可見性的功能應該是一個鍵盤用戶界面的一部分;你必須提供這個切換功能。安全

注意:若是你只須要添加幾個按鈕到系統的鍵盤,你應該查看 Custom Views for Data Input網絡

一個自定義鍵盤不可以作什麼

有一些特定的輸入對象是你的自定義鍵盤沒資格輸入的:安全領域(例如密碼輸入框), 電話鍵盤對象(如在通信錄中的電話號碼輸入框)。app

你的自定義鍵盤不能訪問輸入視圖的層級結構,不能控制光標和選擇文本。ide

另外,自定義鍵盤沒法在頂行以上顯示任何東西(如系統鍵盤,當你在頂行長按一個按鍵時)。

沙盒

默認狀況下,一個鍵盤是沒有網絡訪問權限的,並且也沒法與鍵盤的容器app分享文件。爲了得到這些權限,能夠在Info.plist 文件中設置 RequestsOpenAccess 這個布爾類型的鍵的值爲 YES。 作這些會擴展鍵盤的沙盒,如 Establishing and Maintaining User Trust.中描述的。

如何你這麼作來申請開放權限,你的鍵盤會得到一下功能,每個都伴隨着責任:

  • 訪問位置服務和 Address BOOK 數據庫,在第一次訪問時會要求申請用戶權限。

  • 能夠與包含鍵盤的app共享一個容器,例如這樣能夠容許在包含鍵盤的app裏面提供一個自定義的詞庫管理界面。

  • 可以發送鍵盤的點擊和其它輸入事件到服務端去處理。

  • 訪問iCloud,例如確保同一個用戶的鍵盤的設置和你的自動更正詞庫在全部設備上同步。

  • 經過包含鍵盤的app訪問Game Center 和 應用內購買。

  • 若是你設計你的鍵盤支持手機設備管理(MDM),那麼還能夠容許與管理的app一塊兒工做。

確保你閱讀了Designing for User Trust,它描述了在你申請開放權限的狀況下,你尊重和保護用戶數據的責任。

高層視圖

下面的圖片顯示了在一個運行的鍵盤中一些重要的對象,而且顯示了在一個典型的開發流程中這些對象來源於哪裏。在一個最基本的形式中,咱們有一個app包含了鍵盤擴展和一個控制這個鍵盤和響應用戶事件的UIInputViewController對象。

這個自定義的鍵盤模版包含一個 UIInputViewController的子類,這是你的鍵盤的主視圖控制器。讓咱們看看它的接口是怎麼定義的:

class UIInputViewController : UIViewController, UITextInputDelegate, NSObjectProtocol {

    var inputView: UIInputView!

    var textDocumentProxy: NSObject! { get }

    func dismissKeyboard()
    func advanceToNextInputMode()

    // This will not provide a complete repository of a language's vocabulary.
    // It is solely intended to supplement existing lexicons.
    func requestSupplementaryLexiconWithCompletion(completionHandler: ((UILexicon!) -> Void)!)
}
  • inputView 是這個鍵盤的視圖,與view屬性同樣

  • dismissKeyboard方法能夠被調用來關閉鍵盤視圖

  • advanceToNextInputMode 是用來切換鍵盤的

  • textDocumentProxy 是你將用來與當前的文本輸入進行交互的對象。

例如:

self.textDocumentProxy.insertText("We ❤ Swift") // inserts the string "We ❤ Swift" at the insertion point

self.textDocumentProxy.deleteBackward() // Deletes the character to the left of the insertion point
  • UIInputViewController 實現了UITextInputDelegate協議,當文本或者選擇的文本發生變化時,會使用selectionWillChange , selectionDidChange, textWillChangetextDidChange 消息來通知你。

建立一個莫斯碼鍵盤

咱們將建立一個簡單的鍵盤,能夠輸入點和破折號,切換鍵盤,刪除一個字符以及關閉鍵盤。這個例子只經過代碼來建立用戶界面。咱們也可使用Nib 文件來建立界面-這個會在教程末尾涉及到。 加載Nibs 文件可能會對性能產生負面影響。

建立一個新的工程

打開Xcode 6, 建立一個新的「Single Page Application」 項目,選擇 Swift做爲開發語言。

添加一個text field 文本框

打開 Main.storyboard ,而後從 Component Library 中拖動一個文本框。咱們將在後面使用這個來測試咱們的鍵盤。

把這個文本框居中,添加必要的約束。

暗示: 若是你在 viewDidLoad 中調用 textField.becomeFirstResponder() 那麼當你打開這個app時鍵盤就會打開。

添加鍵盤擴展

在navigator中選擇項目文件,點擊 + 號添加一個新target。

選擇 Application Extension ,使用 Custom Keyboard 模版, 命名爲MorseCodeKeyboard

這樣就會建立一個新的組,名叫 MorseCodeKeyboard,裏面包含了兩個文件 KeyboardViewController.swiftInfo.plist

清理

打開 KeyboardViewController.swift 文件。這個模版鍵盤有一個已經建立好的按鈕,用來進行切換鍵盤的。把這些代碼從 viewDidLoad 中移到一個新的方法 addNextKeyboardButton 中。

func addNextKeyboardButton() {
    self.nextKeyboardButton = UIButton.buttonWithType(.System) as UIButton

    ...

    var nextKeyboardButtonBottomConstraint = NSLayoutConstraint(item: self.nextKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1.0, constant: -10.0)
    self.view.addConstraints([nextKeyboardButtonLeftSideConstraint, nextKeyboardButtonBottomConstraint])
}

建立一個 addKeyboardButtons 方法,而後在 viewDidLoad 中調用它。這樣會有助於組織代碼。如今咱們只是有了幾個按鈕,可是在實際的項目中會有更多的按鈕。 在 addKeyboardButtons 中調用 addNextKeyboardButton

class KeyboardViewController: UIInputViewController {

    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        addKeyboardButtons()
    }

    func addKeyboardButtons() {
        addNextKeyboardButton()
    }

    ...

}

如今添加點按鈕。 建立一個類型爲 UIButton! 爲的 dotButton 屬性。

class KeyboardViewController: UIInputViewController {

    var nextKeyboardButton: UIButton!
    var dotButton: UIButton!

    ...
}

添加一個 addDot 方法。 以一個系統類型的按鈕來初始化這個 dotButton 屬性。給 TouchUpInside 事件添加一個回調。 設置一個更大的字體和添加一個圓角。 添加約束來把這個按鈕放在離水平中心位置左邊 50個點,垂直居中的位置。 代碼與 nextKeyboardButton 的相似。

func addDot() {
    // initialize the button
    dotButton = UIButton.buttonWithType(.System) as UIButton
    dotButton.setTitle(".", forState: .Normal)
    dotButton.sizeToFit()
    dotButton.setTranslatesAutoresizingMaskIntoConstraints(false)

    // adding a callback
    dotButton.addTarget(self, action: "didTapDot", forControlEvents: .TouchUpInside)

    // make the font bigger
    dotButton.titleLabel.font = UIFont.systemFontOfSize(32)

    // add rounded corners
    dotButton.backgroundColor = UIColor(white: 0.9, alpha: 1)
    dotButton.layer.cornerRadius = 5

    view.addSubview(dotButton)

    // makes the vertical centers equa;
    var dotCenterYConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0)

    // set the button 50 points to the left (-) of the horizontal center
    var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50)

    view.addConstraints([dotCenterXConstraint, dotCenterYConstraint])
}

使用 textDocumentProxy實現 dotButton 的回調。

func didTapDot() {
    var proxy = textDocumentProxy as UITextDocumentProxy

    proxy.insertText(".")
}

addKeyboardButtons 中調用 addDot

func addKeyboardButtons() {
    addDot()

    addNextKeyboardButton()
}

對於 dashdelete, hideKeyboard 按鈕,過程相似。

破折號

代碼相似於 dotButton,爲了把它對稱地放在水平中心位置,只須要改變水平約束的常量便可:

func addDash() {
    ...

    // set the button 50 points to the left (-) of the horizontal center
    var dotCenterXConstraint = NSLayoutConstraint(item: dotButton, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: -50)

    view.addConstraints([dashCenterXConstraint, dashCenterYConstraint])
}

func didTapDash() {
    var proxy = textDocumentProxy as UITextDocumentProxy

    proxy.insertText("_")
}

刪除按鈕

刪除按鈕會使用 deleteBackward 方法從 textDocumentProxy 中刪除一個字符。 這個佈局約束與 nextKeyboardButton 對稱( .Left -> .Right, .Bottom-> .Top)。

func addDelete() {
    deleteButton = UIButton.buttonWithType(.System) as UIButton
    deleteButton.setTitle(" Delete ", forState: .Normal)
    deleteButton.sizeToFit()
    deleteButton.setTranslatesAutoresizingMaskIntoConstraints(false)
    deleteButton.addTarget(self, action: "didTapDelete", forControlEvents: .TouchUpInside)

    deleteButton.backgroundColor = UIColor(white: 0.9, alpha: 1)
    deleteButton.layer.cornerRadius = 5

    view.addSubview(deleteButton)

    var rightSideConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0)

    var topConstraint = NSLayoutConstraint(item: deleteButton, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1.0, constant: +10.0)

    view.addConstraints([rightSideConstraint, topConstraint])
}

func didTapDelete() {
    var proxy = textDocumentProxy as UITextDocumentProxy

    proxy.deleteBackward()
}

隱藏鍵盤

hideKeyboardButton 會在點擊時,調用 dismissKeyboard 來隱藏鍵盤:

func addHideKeyboardButton() {
    hideKeyboardButton = UIButton.buttonWithType(.System) as UIButton

    hideKeyboardButton.setTitle("Hide Keyboard", forState: .Normal)
    hideKeyboardButton.sizeToFit()
    hideKeyboardButton.setTranslatesAutoresizingMaskIntoConstraints(false)

    hideKeyboardButton.addTarget(self, action: "dismissKeyboard", forControlEvents: .TouchUpInside)

    view.addSubview(hideKeyboardButton)

    var rightSideConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1.0, constant: -10.0)

    var bottomConstraint = NSLayoutConstraint(item: hideKeyboardButton, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1.0, constant: -10.0)

    view.addConstraints([rightSideConstraint, bottomConstraint])
}

使用 Nib 文件

爲了避免用手寫這些佈局約束,你能夠建立一個界面文件,而後直接在上面添加約束。

建立一個界面文件

右擊 MorseCodeKeyboard組,而後選擇 New File.

選擇 User Interface 和 View 模版。 命名爲 CustomKeyboardInterface

選擇 File's Owner ,改變類名爲 KeyboardViewController

在視圖中添加一個按鈕,設置標題爲 We ❤ Swift 。 界面相似下面這樣:

加載界面

init(nibName, bundle) 構造器中加載 CustomKeyboard nib 文件

class KeyboardViewController: UIInputViewController {

    ...

    var customInterface: UIView!

    init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        var nib = UINib(nibName: "CustomKeyBoardInterface", bundle: nil)
        let objects = nib.instantiateWithOwner(self, options: nil)
        customInterface = objects[0] as UIView
    }

    ...

}

添加到 inputView

viewDidLoad 方法中,添加自定義的界面到inputView中。

class KeyboardViewController: UIInputViewController {

    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(customInterface)

        ...
    }

    ...
}

給按鈕添加一個回調

class KeyboardViewController: UIInputViewController {

    ...

    @IBAction func didTapWeheartSwift() {
        var proxy = textDocumentProxy as UITextDocumentProxy

        proxy.insertText("We ❤ Swift")
    }

    ...
}

鏈接按鈕的事件到這個回調上

右鍵這個按鈕,而後點擊 touchUpInside 並拖動到 didTapWeHeartSwift 這個 IBAction中

最後,代碼應該是這樣的

在你的設備上安裝這個容器app

在你的設備上運行這個app後,以下來添加你的自定義鍵盤:

選擇鍵盤。

選擇添加一個新鍵盤。找到咱們的 MorseCode鍵盤:

如今從新運行咱們的應用,盡情享受咱們的新鍵盤吧。

相關文章
相關標籤/搜索