swift -> k to develop a simple input method

 

  Reference: http://www.cocoachina.com/ios/20140922/9706.html

 

Open Xcode 6 and create a new project. File->New->Project
 
Name the project CustomKeyboardSample and save it in an appropriate location. This is our main project, but we also need to add extensions.
 
Now, let's add a Text Field to the project. Open the Main.Storyboard file and drag and drop a UITextField control on the view controller on the screen.
This is where we use to test the input. Now is the time to add the extension.
 
Click File->New->Target, select Custom Keyboard in the iOS/Application Extension list. Name the extension CustomKeyboard and choose the Swift programming language.
add extension


 
You should now have a new target folder called CustomKeyboard with a file called KeyboardViewController.swift in it. Xcode has added some initial code for us and the keyboard should work (albeit without much functionality).
 
Now you can try running CustomKeyboardSample and try out the new input method.
 
When you click on the text box, the system keyboard will be displayed. We can use the globe icon on the bottom row to switch input methods, but this icon won't show up until we install our new keyboard.
 
Go to the home screen (click on the menu bar, Hardware->Home). Open Settings and go to General -> Keyboard -> Keyboard. Click "Add New Keyboard" and select CustomKeyboard. Turn on the switch to enable it and agree to the warning message.
 
Click Done and you are ready to start!
 
If you run the app again on the emulator, you can switch input methods. Click on the globe icon until you see the keyboard with only the "Next Keyboard" button.
 
Now it's time to start adding the keys for the input method.
 
Open the file KeyboardViewController.h. In this file, you will see a class KeyboardViewController inherits from UIInputViewController. This is the keyboard class that manages the view. We can add a button to the containing view and it will show up in the keyboard.
Add a function called createButton
func createButtonWithTitle(title: String) -> UIButton {
    let button = UIButton(type: .System)
    button.frame = CGRectMake(0, 0, 20, 20)
    button.setTitle(title, forState: .Normal)
    button.sizeToFit()
    button.titleLabel.font = UIFont.systemFontOfSize(15)
    button.translatesAutoresizingMaskIntoConstraints = false
    button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
    button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
    button.addTarget(self, action: "didTapButton:", forControlEvents: .TouchUpInside)
    return button
}
 在上面的代码中,我们以编程的方式来添加一个按钮,并设置其属性。我们可以用nib文件来实现,但我们将不得不管理如此之多的按钮。因此这是一个更好的选择。
 
该代码会调用一个名为didTapButton的响应按钮被按下的方法。现在让我们添加一个方法。
func didTapButton(sender: AnyObject?) { 
    let button = sender as UIButton 
    let title = button.titleForState(.Normal) 
    var proxy = textDocumentProxy as UITextDocumentProxy 
 
    proxy.insertText(title) 
} 
 
在上面的方法中,我们用swift实现了对一个按钮事件的处理。AnyObject类型就像是在Objective-C中的ID的对象。我们给UIButton放置了一个传送器,然后得到该按钮的标题文字,这些文字是我们要在文本框中要输入的文本文字。
 
要使用输入法输入文本,我们使用textDocumentProxy对象,并调用insertText方法。
 
接下来的一步是添加一个按钮到我们的键盘视图。在viewDidLoad方法下面加入两行代码。您可以在viewDidLoad和textDidChange方法中删除自动生成的代码。
override func viewDidLoad() { 
   super.viewDidLoad() 
 
   let button = createButtonWithTitle("A")                        
   self.view.addSubview(button) 
} 
 
增加了标题为“A”到输入法的按钮。这是我们的关键所在。
 
现在,运行应用程序,然后点击文本框,你应该可以看到键盘的”A”键。 (你可能需要切换键盘)。
 
点击这个按钮,看看会发生什么......看!我们已经有了文字A!
 
输入效果


 
好吧,让我们添加更多的按键,让这个小子看起来像一个真正的输入法。
 
让我们修改我们在viewDidLoad方法中加入的代码。
override func viewDidLoad() { 
   super.viewDidLoad() 
 
   let buttonTitles = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"] 
   var buttons = UIButton[]() 
   var keyboardRowView = UIView(frame: CGRectMake(0, 0, 320, 50)) 
 
   for buttonTitle in buttonTitles{ 
       let button = createButtonWithTitle(buttonTitle) 
       buttons.append(button) 
       keyboardRowView.addSubview(button) 
       } 
 
   self.view.addSubview(keyboardRowView)  
 
现在有了这个新的代码,我们创建了一个包含按键标题的数组,同时我们也创造了包含这些按键的列表。每个按键都被添加到一个数组和一个UIView中,这将是我们的第一排键列。然后向主键盘图中添加该视图。
 
如果你运行这个程序,你可能只看到了P键,因为所有的按钮都在同一位置。我们需要以编程方式添加一些约束,使他们能够在一排对齐。
 
因此,我们将创建一个新的函数来创建约束。
    func addIndividualButtonConstraints(buttons: [UIButton], mainView: UIView){
        for (index, button) in (buttons).enumerate() {
            
            var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: mainView, attribute: .Top, multiplier: 1.0, constant: 1)
            
            var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: mainView, attribute: .Bottom, multiplier: 1.0, constant: -1)
            
            var rightConstraint : NSLayoutConstraint!
            
            if index == buttons.count - 1 {
                
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: mainView, attribute: .Right, multiplier: 1.0, constant: -1)
                
            }else{
                
                let nextButton = buttons[index+1]
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: nextButton, attribute: .Left, multiplier: 1.0, constant: -1)
            }
            
            var leftConstraint : NSLayoutConstraint!
            
            if index == 0 {
                
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: mainView, attribute: .Left, multiplier: 1.0, constant: 1)
                
            }else{
                
                let prevtButton = buttons[index-1]
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: prevtButton, attribute: .Right, multiplier: 1.0, constant: 1)
                
                let firstButton = buttons[0]
                var widthConstraint = NSLayoutConstraint(item: firstButton, attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
                
                mainView.addConstraint(widthConstraint) 
            } 
            
            mainView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint]) 
        } 
    }
 
这是一个很长的代码,不是吗?有了AutoLayout,你不能真正建立起它并且你需要立即添加所有约束,否则它将不工作。上面代码的主要工作是添加边界为1px的约束,这些约束将添加到每个按键的顶部和底部。它也增加了左右两边边界为1px的约束,添加到相邻键的左边和右边(或添加到行视图,如果它是该行中的第一个或最后一个键的话)。
 
如果在viewDidLoad中添加调用上面函数的功能,我们应该可以看到新的排按键显示出来。


 
 
现在,这看起来更像是一个输入法了。接下来的步骤是添加输入法的其他行。要做到这一点,让我们做一些快速重构。创建并实现添加每行按键的新方法。
    func createRowOfButtons(buttonTitles: [NSString]) -> UIView {
        
        var buttons = [UIButton]()
        var keyboardRowView = UIView(frame: CGRectMake(0, 0, 320, 50))
        
        for buttonTitle in buttonTitles{
            let button = createButtonWithTitle(buttonTitle as String)
            buttons.append(button)
            keyboardRowView.addSubview(button)
        }
        
        addIndividualButtonConstraints(buttons, mainView: keyboardRowView)
        
        return keyboardRowView 
    }
 
我们已经基本将viewDidLoad中的代码提取到它自己的方法中。这种新的方法将标题文字存放在数列中,并返回包含该行所有按钮的视图。现在,我们可以在任何一个我们想要添加的代码中调用这段代码。
 
因此,我们的新vieDidLoad方法是这样的:
override func viewDidLoad() { 
    super.viewDidLoad() 
 
    let buttonTitles1 = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"] 
    let buttonTitles2 = ["A", "S", "D", "F", "G", "H", "J", "K", "L"] 
    let buttonTitles3 = ["CP", "Z", "X", "C", "V", "B", "N", "M", "BP"] 
    let buttonTitles4 = ["CHG", "SPACE", "RETURN"] 
 
    var row1 = createRowOfButtons(buttonTitles1) 
    var row2 = createRowOfButtons(buttonTitles2) 
    var row3 = createRowOfButtons(buttonTitles3) 
    var row4 = createRowOfButtons(buttonTitles4) 
 
    self.view.addSubview(row1) 
    self.view.addSubview(row2) 
    self.view.addSubview(row3) 
    self.view.addSubview(row4) 
 
}
 在上面的代码中,我们已经加入4行键,然后加入这些按键,后者又被添加到主视图中的行中。
 
我们现在可以运行代码,但你只能看到最后一排,因为他们都在同一位置。 我们需要添加一些自动布局的约束。
    func addConstraintsToInputView(inputView: UIView, rowViews: [UIView]){
        
        for (index, rowView) in (rowViews).enumerate() {
            var rightSideConstraint = NSLayoutConstraint(item: rowView, attribute: .Right, relatedBy: .Equal, toItem: inputView, attribute: .Right, multiplier: 1.0, constant: -1)
            
            var leftConstraint = NSLayoutConstraint(item: rowView, attribute: .Left, relatedBy: .Equal, toItem: inputView, attribute: .Left, multiplier: 1.0, constant: 1)
            
            inputView.addConstraints([leftConstraint, rightSideConstraint])
            
            var topConstraint: NSLayoutConstraint
            
            if index == 0 {
                topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: inputView, attribute: .Top, multiplier: 1.0, constant: 0)
                
            }else{
                
                let prevRow = rowViews[index-1]
                topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: prevRow, attribute: .Bottom, multiplier: 1.0, constant: 0)
                
                let firstRow = rowViews[0]
                var heightConstraint = NSLayoutConstraint(item: firstRow, attribute: .Height, relatedBy: .Equal, toItem: rowView, attribute: .Height, multiplier: 1.0, constant: 0)
                
                inputView.addConstraint(heightConstraint)
            }
            inputView.addConstraint(topConstraint)
            
            var bottomConstraint: NSLayoutConstraint
            
            if index == rowViews.count - 1 {
                bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: inputView, attribute: .Bottom, multiplier: 1.0, constant: 0)
                
            }else{
                
                let nextRow = rowViews[index+1]
                bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: nextRow, attribute: .Top, multiplier: 1.0, constant: 0) 
            } 
            
            inputView.addConstraint(bottomConstraint) 
        } 
    }
 
这种新方法做了类似的功能,我们增加了最后的自动布局代码。它增加了相对于主视图向行的左右两边以及下面的1px的约束和添加每一行和和它上面行之间的0px约束。
 
现在,我们需要从我们的viewDidLoad方法中调用这段代码。
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //
        let buttonTitles1 = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]
        let buttonTitles2 = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
        let buttonTitles3 = ["CP", "Z", "X", "C", "V", "B", "N", "M", "BP"]
        let buttonTitles4 = ["CHG", "SPACE", "RETURN"]
        
        var row1 = createRowOfButtons(buttonTitles1)
        var row2 = createRowOfButtons(buttonTitles2)
        var row3 = createRowOfButtons(buttonTitles3)
        var row4 = createRowOfButtons(buttonTitles4)
        
        self.view.addSubview(row1)
        self.view.addSubview(row2)
        self.view.addSubview(row3)
        self.view.addSubview(row4)
        
        row1.translatesAutoresizingMaskIntoConstraints = false
        row2.translatesAutoresizingMaskIntoConstraints = false
        row3.translatesAutoresizingMaskIntoConstraints = false
        row4.translatesAutoresizingMaskIntoConstraints = false
        
        addConstraintsToInputView(self.view, rowViews: [row1, row2, row3, row4])
        
    }
 
这是我们新的viewDidLoad函数。你会看到,我们把每一行的TranslatesAutoresizingMaskIntoConstraints设为了false。这是为了确保更好的使用自动布局的方法,而不是使用约束的布局。
 
现在,如果你运行应用程序,你会看到输入法均能正常自如地布局。您可以点击所有的按钮,看看它们输入到文本框中。


 
成品图
还有一个小问题,非文本键无法正常工作(例如,退格键,空格键)。
 
为了解决这个问题,我们需要改变我们的didTapButton方法来添加响应这些键的正确方法。
    func didTapButton(sender: AnyObject?) {
        let button = sender as! UIButton
        let title = button.titleForState(.Normal)! as String
        let proxy = textDocumentProxy as UITextDocumentProxy
        
        //proxy.insertText(title!)
        switch title {
        case "BP" :
            proxy.deleteBackward()
        case "RETURN" :
            proxy.insertText("\n")
        case "SPACE" :
            proxy.insertText(" ")
        case "CHG" :
            self.advanceToNextInputMode()
        default :
            proxy.insertText(title)
        }
    }
 
这里用switch语句来对按下的键进行识别处理。退格键调用代理上的deleteBackward方法,空格键插入一个空格和CHG键改变输入法或者系统输入法或安装下一个输入法。
 
 

 

完整代码结果: KeyboardViewController.swift

 

//
//  KeyboardViewController.swift
//  CustomKeyboard
//
//  Created by   on 16/11/8.
//  Copyright © 2016年  . All rights reserved.
//

import UIKit

class KeyboardViewController: UIInputViewController {

    @IBOutlet var nextKeyboardButton: UIButton!

    override func updateViewConstraints() {
        super.updateViewConstraints()
    
        // Add custom view sizing constraints here
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //
        let buttonTitles1 = ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]
        let buttonTitles2 = ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
        let buttonTitles3 = ["CP", "Z", "X", "C", "V", "B", "N", "M", "BP"]
        let buttonTitles4 = ["CHG", "SPACE", "RETURN"]
        
        var row1 = createRowOfButtons(buttonTitles1)
        var row2 = createRowOfButtons(buttonTitles2)
        var row3 = createRowOfButtons(buttonTitles3)
        var row4 = createRowOfButtons(buttonTitles4)
        
        self.view.addSubview(row1)
        self.view.addSubview(row2)
        self.view.addSubview(row3)
        self.view.addSubview(row4)
        
        row1.translatesAutoresizingMaskIntoConstraints = false
        row2.translatesAutoresizingMaskIntoConstraints = false
        row3.translatesAutoresizingMaskIntoConstraints = false
        row4.translatesAutoresizingMaskIntoConstraints = false
        
        addConstraintsToInputView(self.view, rowViews: [row1, row2, row3, row4])
        
    }
    
    //
    
    func createButtonWithTitle(title: String) -> UIButton {
        let button = UIButton(type: .System)
        button.frame = CGRectMake(0, 0, 20, 20)
        button.setTitle(title, forState: .Normal)
        button.sizeToFit()
        button.titleLabel!.font = UIFont.systemFontOfSize(15)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
        button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
        button.addTarget(self, action: #selector(KeyboardViewController.didTapButton(_:)), forControlEvents: .TouchUpInside)
        return button
    }
    func didTapButton(sender: AnyObject?) {
        let button = sender as! UIButton
        let title = button.titleForState(.Normal)! as String
        let proxy = textDocumentProxy as UITextDocumentProxy
        
        //proxy.insertText(title!)
        switch title {
        case "BP" :
            proxy.deleteBackward()
        case "RETURN" :
            proxy.insertText("\n")
        case "SPACE" :
            proxy.insertText(" ")
        case "CHG" :
            self.advanceToNextInputMode()
        default :
            proxy.insertText(title)
        }
    }
    func addIndividualButtonConstraints(buttons: [UIButton], mainView: UIView){
        for (index, button) in (buttons).enumerate() {
            
            var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: mainView, attribute: .Top, multiplier: 1.0, constant: 1)
            
            var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: mainView, attribute: .Bottom, multiplier: 1.0, constant: -1)
            
            var rightConstraint : NSLayoutConstraint!
            
            if index == buttons.count - 1 {
                
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: mainView, attribute: .Right, multiplier: 1.0, constant: -1)
                
            }else{
                
                let nextButton = buttons[index+1]
                rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: nextButton, attribute: .Left, multiplier: 1.0, constant: -1)
            }
            
            var leftConstraint : NSLayoutConstraint!
            
            if index == 0 {
                
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: mainView, attribute: .Left, multiplier: 1.0, constant: 1)
                
            }else{
                
                let prevtButton = buttons[index-1]
                leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: prevtButton, attribute: .Right, multiplier: 1.0, constant: 1)
                
                let firstButton = buttons[0]
                var widthConstraint = NSLayoutConstraint(item: firstButton, attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
                
                mainView.addConstraint(widthConstraint) 
            } 
            
            mainView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint]) 
        } 
    }
    func createRowOfButtons(buttonTitles: [NSString]) -> UIView {
        
        var buttons = [UIButton]()
        var keyboardRowView = UIView(frame: CGRectMake(0, 0, 320, 50))
        
        for buttonTitle in buttonTitles{
            let button = createButtonWithTitle(buttonTitle as String)
            buttons.append(button)
            keyboardRowView.addSubview(button)
        }
        
        addIndividualButtonConstraints(buttons, mainView: keyboardRowView)
        
        return keyboardRowView 
    }
    func addConstraintsToInputView(inputView: UIView, rowViews: [UIView]){
        
        for (index, rowView) in (rowViews).enumerate() {
            var rightSideConstraint = NSLayoutConstraint(item: rowView, attribute: .Right, relatedBy: .Equal, toItem: inputView, attribute: .Right, multiplier: 1.0, constant: -1)
            
            var leftConstraint = NSLayoutConstraint(item: rowView, attribute: .Left, relatedBy: .Equal, toItem: inputView, attribute: .Left, multiplier: 1.0, constant: 1)
            
            inputView.addConstraints([leftConstraint, rightSideConstraint])
            
            var topConstraint: NSLayoutConstraint
            
            if index == 0 {
                topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: inputView, attribute: .Top, multiplier: 1.0, constant: 0)
                
            }else{
                
                let prevRow = rowViews[index-1]
                topConstraint = NSLayoutConstraint(item: rowView, attribute: .Top, relatedBy: .Equal, toItem: prevRow, attribute: .Bottom, multiplier: 1.0, constant: 0)
                
                let firstRow = rowViews[0]
                var heightConstraint = NSLayoutConstraint(item: firstRow, attribute: .Height, relatedBy: .Equal, toItem: rowView, attribute: .Height, multiplier: 1.0, constant: 0)
                
                inputView.addConstraint(heightConstraint)
            }
            inputView.addConstraint(topConstraint)
            
            var bottomConstraint: NSLayoutConstraint
            
            if index == rowViews.count - 1 {
                bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: inputView, attribute: .Bottom, multiplier: 1.0, constant: 0)
                
            }else{
                
                let nextRow = rowViews[index+1]
                bottomConstraint = NSLayoutConstraint(item: rowView, attribute: .Bottom, relatedBy: .Equal, toItem: nextRow, attribute: .Top, multiplier: 1.0, constant: 0) 
            } 
            
            inputView.addConstraint(bottomConstraint) 
        } 
    }
    //--------------------------------------------------
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated
    }
    
    override func textWillChange(textInput: UITextInput?) {
        // The app is about to change the document's contents. Perform any preparation here.
    }
    
    override func textDidChange(textInput: UITextInput?) {
        // The app has just changed the document's contents, the document context has been updated.
        
        
    }
    

}

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326448796&siteId=291194637