我將講解一些關於鍵盤擴展的基本知識,而後使用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
, textWillChange
和 textDidChange
消息來通知你。咱們將建立一個簡單的鍵盤,能夠輸入點和破折號,切換鍵盤,刪除一個字符以及關閉鍵盤。這個例子只經過代碼來建立用戶界面。咱們也可使用Nib 文件來建立界面-這個會在教程末尾涉及到。 加載Nibs 文件可能會對性能產生負面影響。
打開Xcode 6, 建立一個新的「Single Page Application」 項目,選擇 Swift做爲開發語言。
打開 Main.storyboard
,而後從 Component Library 中拖動一個文本框。咱們將在後面使用這個來測試咱們的鍵盤。
把這個文本框居中,添加必要的約束。
暗示: 若是你在 viewDidLoad
中調用 textField.becomeFirstResponder()
那麼當你打開這個app時鍵盤就會打開。
在navigator中選擇項目文件,點擊 + 號添加一個新target。
選擇 Application Extension
,使用 Custom Keyboard
模版, 命名爲MorseCodeKeyboard
。
這樣就會建立一個新的組,名叫 MorseCodeKeyboard
,裏面包含了兩個文件 KeyboardViewController.swift
和 Info.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() }
對於 dash
,delete
, 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]) }
爲了避免用手寫這些佈局約束,你能夠建立一個界面文件,而後直接在上面添加約束。
右擊 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 } ... }
在 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後,以下來添加你的自定義鍵盤:
選擇鍵盤。
選擇添加一個新鍵盤。找到咱們的 MorseCode
鍵盤:
如今從新運行咱們的應用,盡情享受咱們的新鍵盤吧。