8

I've made an in-app custom keyboard that replaces the system keyboard and pops up when I tap inside a UITextField.

enter image description here

Here is my code:

class ViewController: UIViewController {
    
    var myCustomKeyboard: UIView!
    @IBOutlet weak var textField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let keyboardNib = UINib(nibName: "Keyboard", bundle: nil)
        myCustomKeyboard = keyboardNib.instantiateWithOwner(self, options: nil)[0] as! UIView
        
        textField.inputView = myCustomKeyboard
    }

}

The keyboard layout is loaded from an xib file.

Question

How do I get the button text into the text field?

Notes:

  • There are many tutorials about making a custom system keyboard (which needs to be installed), but I only want an in-app keyboard. The tutorials use a special view controller just for the keyboard, but here it seems that I am just setting the keyboard view.
  • I have read the Custom Views for Data Input documentation.
  • This is the closest Stack Overflow question I could find, but it doesn't go as far as describing how to get the text from the buttons.

Update

  • This tutorial seems to indicate that there is a view controller for the custom input view. However, I am getting lost in the Objective-C code. What would be the process in Swift?
  • This answer mentions the UIKeyInput protocol that UITextField conforms to, but how do I use it?
  • If there is any built in way too make a custom in-app keyboard, I would really prefer that to making a normal custom view.
Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • Just append button title to the text field : `textField.text = textField.text+button.title` ? – ryancrunchi Nov 03 '15 at 11:20
  • Since the buttons are stored in an xib file, how do I get a reference to them? – Suragch Nov 03 '15 at 11:26
  • 1
    You can make a custom view subclass for your keyboard and make the buttons public, or better implement a delegate. Or if you want to keep your xib, give your buttons a tag in you xib. Then access them with `myCustomKeyboard.viewWithTag(tag)` – ryancrunchi Nov 03 '15 at 11:42
  • Thanks for those ideas. I may end up going with something like that. For the system keyboards, though, the view controller doesn't need to know anything about the buttons on the keyboard. It just accepts text from it. Isn't there anything similar for an in-app custom keyboard? – Suragch Nov 03 '15 at 11:49
  • I do not think so. You have to manage the keyboard behavior yourself as it is just a custom view. – ryancrunchi Nov 03 '15 at 11:52
  • Another way could be to iterate over the subviews of your custom keyboard (`myCustomKeyboard`). And for each button you add a target/action for the event `UIControlEventTouchUpInside`. In this function you just update your textField with the text of the sender (the touched button) – awph Nov 05 '15 at 18:51

2 Answers2

4

I imagine something like this:

A new function to handle button event

func updateTextfield(sender: UIButton) {
    textField.text = (textField.text ?? "") + (sender.titleForState(.Normal) ?? "")
}

And after init your custom keyboard, register the buttons:

myCustomKeyboard.subviews
    .filter { $0 as? UIButton != nil } // Keep the buttons only
    .forEach { ($0 as! UIButton).addTarget(self, action: "updateTextfield", forControlEvents: .TouchUpInside)}
awph
  • 566
  • 6
  • 15
4

Setup

  • Make an xib file that includes all your keys
  • Use Autolayout so that the keys will resize to the correct proportions no matter how big the keyboard is set to later.
  • Create a swift file with the same name as the xib file and set it as the file owner in the xib file setting.

    enter image description here

    enter image description here enter image description here

  • Hook up all the key buttons to an IBAction method in the swift file. (See the code below.)

Code

I'm using the delegate pattern to communicate between the custom keyboard view and the main view controller. This allows them to be decoupled. Multiple different custom keyboards could be swapped in and out without needing to change the detailed implementation code in the main view controller.

Keyboard.swift file

import UIKit

protocol KeyboardDelegate {
    func keyWasTapped(character: String)
}

class Keyboard: UIView {

    var delegate: KeyboardDelegate?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initializeSubviews()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initializeSubviews()
    }

    func initializeSubviews() {
        let xibFileName = "Keyboard" // xib extention not needed
        let view = NSBundle.mainBundle().loadNibNamed(xibFileName, owner: self, options: nil)[0] as! UIView
        self.addSubview(view)
        view.frame = self.bounds
    }

    @IBAction func keyTapped(sender: UIButton) {
        self.delegate?.keyWasTapped(sender.titleLabel!.text!)
    }

}

Main view controller

Note that the ViewController conforms to the KeyboardDelegate protocol that we created. Also, when creating an instance of the keyboard view, the height needs to be set but the width doesn't. Apparently setting the inputView of the text field updates the keyboard view width to the screen width, which is convenient.

class ViewController: UIViewController, KeyboardDelegate {

    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        // get an instance of the Keyboard (only the height is important)
        let keyboardView = Keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: 300))

        // use the delegate to communicate
        keyboardView.delegate = self

        // replace the system keyboard with the custom keyboard
        textField.inputView = keyboardView
    }

    // required method for keyboard delegate protocol
    func keyWasTapped(character: String) {
        textField.insertText(character)
    }

}

Sources

Related

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • You are totally right with this solution :). Just a little thing : `Set up the xib metrics as follows` will not affect how the view render when you run the app. It is just simulated in interface builder to see how it `would` render if the metrics were set as you set them. – ryancrunchi Nov 13 '15 at 07:39
  • Ah, somehow I had missed the significance of the "simulated" word. Thanks for pointing that out. I removed that step from my answer. – Suragch Nov 13 '15 at 08:50
  • Implementation is done. But when I click button, it is not insert the text in the `textfiled`. It didn't come to keyWasTapped – Piraba Sep 22 '16 at 07:14
  • @Piraba, I can only guess that your delegate isn't configured correctly or maybe you forgot to hook up your IBActions to the key buttons. Add some `print` statements or step through everything in debug mode to see exactly where the problem is. See [this answer](http://stackoverflow.com/a/33692231/3681880) also. If it is none of that, then I recommend you ask a new question and provide more details. – Suragch Sep 22 '16 at 07:22
  • @Suragch When I click numbers, It printed some text inside "keyTapped" method. But in the viewController class not.I implemented keyboarddelegate protocol also – Piraba Sep 22 '16 at 07:56