heron
heron

Reputation: 3661

Custom keyboard for iOS

I've built keyboard this way. (code below)

But, there are some problems like:

What I want to do is, to modify original iOS keyboard and add some other buttons.

Is it possible? Any suggestions?

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.setTranslatesAutoresizingMaskIntoConstraints(false)
        row2.setTranslatesAutoresizingMaskIntoConstraints(false)
        row3.setTranslatesAutoresizingMaskIntoConstraints(false)
        row4.setTranslatesAutoresizingMaskIntoConstraints(false)

        addConstraintsToInputView(self.view, rowViews: [row1, row2, row3, row4])
    }

    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)
            buttons.append(button)
            keyboardRowView.addSubview(button)
        }

        addIndividualButtonConstraints(buttons, mainView: keyboardRowView)

        return keyboardRowView
    }

    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.

        var textColor: UIColor
        var proxy = self.textDocumentProxy as UITextDocumentProxy
        if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
            textColor = UIColor.whiteColor()
        } else {
            textColor = UIColor.blackColor()
        }
    }



    func createButtonWithTitle(title: String) -> UIButton {

        let button = UIButton.buttonWithType(.System) as UIButton
        button.frame = CGRectMake(0, 0, 20, 20)
        button.setTitle(title, forState: .Normal)
        button.sizeToFit()
        button.titleLabel?.font = UIFont.systemFontOfSize(15)
        button.setTranslatesAutoresizingMaskIntoConstraints(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
    }

    func didTapButton(sender: AnyObject?) {

        let button = sender as UIButton
        var proxy = textDocumentProxy as UITextDocumentProxy

        if let title = button.titleForState(.Normal) {
            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 enumerate(buttons) {

            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)

                widthConstraint.priority = 800
                mainView.addConstraint(widthConstraint)
            }

            mainView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint])
        }
    }


    func addConstraintsToInputView(inputView: UIView, rowViews: [UIView]){

        for (index, rowView) in enumerate(rowViews) {
            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)

                heightConstraint.priority = 800
                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)
        }

    }
}

Upvotes: 2

Views: 4395

Answers (3)

Andrew
Andrew

Reputation: 2468

when you click on some button, it's click animation taking long to get back.

Your keys are nothing more than UIButtons. The fade-out animation you're seeing is the normal animation for a UIButton—you can see it for yourself on most buttons across the system. Instead of initializing it with UIButtonType.System, use .Custom and set your own appearance for the desired states.

As an example (there are many possibilities):

let button = UIButton(.Custom)
button.setTitleColor(UIColor.redColor(), forState: .Highlighted)

Note that you don't even have to restrict yourself to using UIButton—custom keyboards are a blank slate.

there is no way to place some common keys as globe symbol for language changing or caps lock

From the Custom Keyboard Guide:

The system picks the appropriate “next” keyboard; there is no API to obtain a list of enabled keyboards or for picking a particular keyboard to switch to.

So these "special" keys are also up to you to provide, most likely with your own custom icons. If you're using UIButton, that probably means calling setImage:forState:. It looks like most third-party keyboards use a globe icon almost identicial to the system one for the "next" key.

There is no API to mod the system keyboard—you must build one yourself from the ground-up.

Upvotes: 3

Tural Ali
Tural Ali

Reputation: 23230

Use the github repo below. It's nearly 1:1 immitation of iOS 8 original keyboard and works like a charm

https://github.com/archagon/tasty-imitation-keyboard

Upvotes: 4

ytbryan
ytbryan

Reputation: 2694

For slow animation, you can study these github repo on how they do their animation:

  1. https://github.com/YuAo/WUEmoticonsKeyboard (See the popview when the button gets pressed.)
  2. https://github.com/ayushgoel/AGEmojiKeyboard
  3. https://github.com/kulpreetchilana/Custom-iOS-Keyboards
  4. or search github:https://github.com/search?utf8=✓&q=iOS+keyboard

Note: They are mostly objective-c repo.

Lastly, it might be cheaper to install a custom keyboard than to make one: http://www.imore.com/best-custom-keyboards-ios-8

Upvotes: 2

Related Questions