Search code examples
iosswiftxibcustom-keyboardinputview

Load a nib file to act as a custom keyboard when a UITextField is tapped


I'm trying to load a .xib file to act as a custom keyboard when a textField is tapped. I'm able to show the .xib file (view) when the textField is tapped but I'm not sure how to communicate the buttons in the .xib file with the a textField in the ViewController.swift.

This is what I have done so far.

  1. Created a single project.

  2. Added a UITextField and created an outlet for it.

    @IBOutlet weak var myField: MyTextField!
    
  3. Created a new .xib file and called it CustomView.xib.

  4. Created a new class and called it CustomView.swift.

  5. Assigned class CustomView.swift to the CustomView.xib file.

  6. Added some UIButtons in the CustomView.xib file. These will be acting as a custom-keyboard, they will show when the textField is tapped and hide when the resignFirstResponder method is called.

  7. In the viewDidLoad method of the ViewController.swift I assigned inputView as follow.

     import UIKit
    
     class ViewController: UIViewController {
    
      @IBOutlet weak var myField: MyTextField!
    
      override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        let myView = NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil).first as? CustomView
        myField.inputView = myView
        }
    }
    
  8. Done

When I run the app and tap on the textField the .xib file shows (see picture below), my problem is, how do I communicate the buttons with the textField in the ViewController.swif. In other words what I want is to show the numbers in the inputField when they are tapped.

Any suggestions? Is this how this is usually done.

Here is an image that shows the view shown when the inputField was tapped.

enter image description here


Solution

  • Let's say you have 9 UIButtons, then consider ctrl + dragging an IBAction from all these buttons in storyboard to a single method such as:

    - (IBAction)tapped:(id)sender
    {
        textView.text = [NSString stringWithFormat:@"%@%li", textView.text, (long)((UIButton*)sender).tag];
        NSLog(@"%@", textView.text);
    }
    

    given that textField is your text field, you could then append each corresponding number from your keypad (that is to say, the buttons) using the tag property of these buttons and the aforementioned method.

    You could set the tag number for each single button in storyboard (I.e., 1 - 9, respectively).

    I didn't test it using 9 buttons but did with only 2, with tag numbers 1 and 2, respectively for each buttons. The result was displaying fine in UITextField (I.e., 1, 12, 122 and so forth).

    Update (Post-comment):

    I was able to re-create this using a nib file containing a few buttons and a UITextField in storyboard.

    The process proceeds as follows:

    1. Create a nib with all the buttons (which you already have done).

    2. Create a view; and under "Custom Class", re-name the class to this view (I.e., "Custom Class" -> "Class" -> "view-that-holds-the-buttons").

    3. Wire the IBActions (a total of 9 corresponding to the number of your buttons) to one single method as described above.

    4. In your other view controller whose view hold the UITextField, load the nib using your existing method.

    5. Add this view (from nib) as subview.

    The following concerns with communication between the view (that holds the buttons along with the IBAction method) and the controller in which you load the nib:

    6. create a delegate (weak) property.

    7. Before you add the view (from nib), assign this delegate with the view controller (the control that loads the nib).

    8. Create a protocol:

    For instance:

    protocol keypadProtocol : class
    {
        func displayKeys(keystrokeObject: AnyObject)
    }
    

    Have the view controller that loads the nib conform to this protocol and implement the required method (displayKeys):

    //The one that loads the nib, which also holds the UITextField
    class mainViewController: UIViewController, keypadProtocol
    

    So, once the buttons are tapped, the IBAction would be called; but instead of displaying it, we send the sender to our delegate, which is the view controller that implements the displayKeys method and holds the UITextField.

    The IBAction would be implemented as follows:

    @IBAction func tapped(sender: AnyObject)
    {
         delegate!.displayKeys(sender)
    }
    

    displayKeys would be implemented like the following:

    func displayKeys(keystrokeObject: AnyObject)
    {
        textView.text = NSString(format: "%@%li", textView.text! ?? "", (keystrokeObject as! UIButton).tag) as String
    }
    

    Declaration of the delegate in the controller where you load the nib file:

    weak var delegate : keypadProtocol?
    

    Assigning the delegate from within the view controller where you load the nib:

    keyPadView.delegate = self //keyPadView is the nib file loaded
    

    In reply to your second comment:

    Assumption:

    We have 2 classes.

    The first one is a subclass of UIView, which is the xib, that holds the buttons. Let’s call this „KeypadView“.

    The second one is the main view controller, which is associated to the controller that holds the UITextField in your storyboard. Let’s call this „MainViewController“.

    Step 2:

    Firstly, please create a new UIView and name it, for the sake of consistency, „KeypadView“.
    Then, click on your .xib file; on the right panel, click on the third tab from the left, which is called „Identity Inspector“; you would see „Custom Class -> Class“, where you would associate this xib to the class you created (you need this class, in order to connect the IBAction for the buttons from the xib file to it). It would be the „KeypadView“, which is a subclass of UIView.

    Step 6:

    You declare this in the class („KeypadView“) that holds the buttons.

    Step 8:

    You connect this method (IBAction) to the aforementioned class (I.e., „KeypadView“).

    Once you load the xib („KeypadView“) from within the „mainViewController“, set the delegate in „KeypadView“ to self (self is the „MainViewController“):

    let keyPadView = NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil).first as? KeyPadView
    
    keyPadView.delegate = self
    self.view.addSubview(keypadView)
    
    //you may need to re-adjust the position of the views; I will leave that to you
    

    In your „KeyPadView“ class, there should be an IBAction that gets called from each of the buttons:

    I.e.,

    @IBAction func tapped(sender: AnyObject)
    {
        delegate!.displayKeys(sender)
    }
    

    Our delegate is the „mainViewController“, if you recall.

    Since displayKeys is implemented in „mainViewController“, the following method would be called therein:

    func displayKeys(keystrokeObject: AnyObject)
    {
        textView.text = NSString(format: "%@%li", textView.text! ?? "", (keystrokeObject as! UIButton).tag) as String
    }
    

    „mainViewController“ would then display the keystrokes in its UITextField (I.e., textView).