Search code examples
iosswiftxcodebuttonkeyboard

How to add a minus sign to the .DecimalPad iOS keyboard?


How can I add a minus sign to the .DecimalPad type iOS keyboard like the app from the link below? Correct me if I'm wrong, but that doesn't seem like a custom keyboard to me, it looks like the default decimal keypad from Apple to me...

Decimal keypad with a minus sign


Solution

  • To add some keys to the keyboard, open KeyboardViewController.swift and make the following changes.

    In viewDidLoad() add the following at the bottom of the function right after the code for the next keyboard button.

    let buttonTitles = ["-"]
    var buttons = createButtons(buttonTitles)
    var topRow = UIView(frame: CGRectMake(0, 0, 320, 40))
    
    for button in buttons {
        topRow.addSubview(button)
    }
    
    self.view.addSubview(topRow)
    
    addConstraints(buttons, containingView: topRow)
    

    Next add the function below which will create buttons with titles of the strings passed into it.

    func createButtons(titles: [String]) -> [UIButton] {
    
        var buttons = [UIButton]()
    
        for title in titles {
            let button = UIButton.buttonWithType(.System) as UIButton
            button.setTitle(title, forState: .Normal)
            button.setTranslatesAutoresizingMaskIntoConstraints(false)
            button.backgroundColor = UIColor(white: 1.0, alpha: 1.0)
            button.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
            button.addTarget(self, action: "keyPressed:", forControlEvents: .TouchUpInside)
            buttons.append(button)
        }
    
        return buttons
    }
    

    This function iterates through an array of Strings and creates buttons with the respective titles. It also adds a target to each button so that when it is tapped, the function keyPressed() will be called. Next add this function.

    func keyPressed(sender: AnyObject?) {
        let button = sender as UIButton
        let title = button.titleForState(.Normal)
        (textDocumentProxy as UIKeyInput).insertText(title!)
    }
    

    Here you get the title of the tapped button and insert it at the insertion point of the current text input object via the textDocumentProperty. This is an object conforming to the UITextDocumentProxy protocol, which acts as a proxy between the keyboard and the text input object that summoned it.

    Next we add the addConstraints() method which will add constraints to the buttons and the containingView.

    func addConstraints(buttons: [UIButton], containingView: UIView){
    
            for (index, button) in enumerate(buttons) {
    
                var topConstraint = NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: containingView, attribute: .Top, multiplier: 1.0, constant: 1)
    
                var bottomConstraint = NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .Equal, toItem: containingView, attribute: .Bottom, multiplier: 1.0, constant: -1)
    
                var leftConstraint : NSLayoutConstraint!
    
                if index == 0 {
    
                    leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: containingView, attribute: .Left, multiplier: 1.0, constant: 1)
    
                }else{
    
                    leftConstraint = NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: buttons[index-1], attribute: .Right, multiplier: 1.0, constant: 1)
    
                    var widthConstraint = NSLayoutConstraint(item: buttons[0], attribute: .Width, relatedBy: .Equal, toItem: button, attribute: .Width, multiplier: 1.0, constant: 0)
    
                    containingView.addConstraint(widthConstraint)
                }
    
                var rightConstraint : NSLayoutConstraint!
    
                if index == buttons.count - 1 {
    
                    rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: containingView, attribute: .Right, multiplier: 1.0, constant: -1)
    
                }else{
    
                    rightConstraint = NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .Equal, toItem: buttons[index+1], attribute: .Left, multiplier: 1.0, constant: -1)
                }
    
                containingView.addConstraints([topConstraint, bottomConstraint, rightConstraint, leftConstraint])
            }
        }
    

    Run the app and you should see a similar keyboard as shown below

    As you can see, adding views programmatically requires you to set up constraints in code and if your keyboard’s UI is complicated and has many keys, this can get complicated and hard to debug. Next we will look at using the Interface Builder to create buttons.

    Create a nib file by navigating to File > New > File > iOS > User Interface > View. Name it KeyboardView and make sure it is under the AC Keyboard target.

    Select the nib’s view and in the Attributes Inspector, set its Size as Freeform and Status Bar to None. Then go to the Size Inspector and set its width to 320 and height to 220.

    In KeyboardViewController.swift add the following in viewDidLoad() after the call to super.viewDidLoad(). This sets the nib file as the view of the view controller.

    let nib = UINib(nibName: "KeyboardView", bundle: nil)
    let objects = nib.instantiateWithOwner(self, options: nil)
    view = objects[0] as UIView;
    

    Add a view to the nib’s main view. Click on the Pin menu at the bottom of the Interface Builder interface and give it the following constraints – Height of 40, Trailing space of 0, Leading space of 0 and Top space to container of 42. Make sure Constrain to margin is not checked.

    add_constraints
    

    Add a button to the view. Set the text color to Dark Gray and change the title. Now for the constraints.

    With each button, select the Align menu and select Vertical Center in Container.

    alignment auto layout
    

    Select the '-' button and add a Leading space to Superview for 5.

    Select '-' and in the Size Inspector, make sure that its Leading Space to S ie set to a constant of 5.

    Select the main view then Editor > Resolve Auto Layout Issues > All Views > Update Frames.

    Run the application and you should see the keys you just added set up below the ones you added in code.

    interface_builder_ui
    

    To create outlets and actions for the keys, select the File’s Owner from the Document Outline, then in the Identity Inspector, set the class as KeyboardViewController. You can then create actions and outlets like you usually do in storyboard files by Control-dragging from a control to the view controller class. (You’ll see more of this in the example that follows).

    Now that we have seen how to create a keyboard’s UI programmatically and with a nib file, let us add some functionality to it. For this, I have a starter project that we will be using. The project is a of a simple keyboard that we will add some functionality to. It is shown below. You should note that to keep it simple, I didn’t design for all size classes so, while it looks nice on the iPhone 5S, it doesn’t look quite the same for bigger screens. You can download the code here. Also note that the keyboard’s name is Appcoda Keyboard and not the AC Keyboard we had earlier.

    keyboard_starter

    I have already set up the actions and outlets we’ll require, but haven’t written the code for them (except for the Next Keyboard key).

    First, you’ll notice that the Next Keyboard key has been replaced with a key titled KB. The action method for this can be seen it the view controller file as shown below.

    @IBAction func nextKeyboardPressed(button: UIButton) {
        advanceToNextInputMode()
    }
    

    We are first going to set up the actions for keys with letters and symbols i.e. any key you tap and see its title as the typed text. I created an action method for all these keys called keyPressed(). Modify this method as shown.

    @IBAction func keyPressed(button: UIButton) {
        var string = button.titleLabel!.text
        (textDocumentProxy as UIKeyInput).insertText("\(string!)")
    }
    

    This is similar to what we had before. The title of the button is inserted at the insertion point of the current text input object via the textDocumentProperty. All the letters we type will be in Caps, but we will fix this shortly. Next modify the following functions to set up actions for the backspace(BS), space(SPACE) and return(RTN) keys respectively.

    @IBAction func backSpacePressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).deleteBackward()
    }
    
    @IBAction func spacePressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).insertText(" ")
    }
    
    @IBAction func returnPressed(button: UIButton) {
        (textDocumentProxy as UIKeyInput).insertText("\n")
    }
    

    Run the app and test the keys.

    In the view file, you will notice two views labelled Char Set 1 and Char Set 2. These are on the same row with one on top of the other. In viewDidLoad() the second view is hidden. Modify the charSetPressed() function as shown so that when the user presses the key labelled 1/2, the key’s text will change to 2/2 and a new set of characters will appear on the first row of the keyboard.

    @IBAction func charSetPressed(button: UIButton) {
        if button.titleLabel!.text == "1/2" {
            charSet1.hidden = true
            charSet2.hidden = false
            button.setTitle("2/2", forState: .Normal)
        } else if button.titleLabel!.text == "2/2" {
            charSet1.hidden = false
            charSet2.hidden = true
            button.setTitle("1/2", forState: .Normal)
        }
    }
    

    charset If you look at the system keyboard, there is usually an indication, in the form of a brief animation when you tap a key. We should add some sort of feedback so the user knows that they tapped the right key. Add the following at the end of the keyPressed() method.

    UIView.animateWithDuration(0.2, animations: {
            button.transform = CGAffineTransformScale(CGAffineTransformIdentity, 2.0, 2.0)
            }, completion: {(_) -> Void in
                button.transform =
                    CGAffineTransformScale(CGAffineTransformIdentity, 1, 1)
        })
    

    This makes a key scale up briefly when tapped before going back to its original size.

    Lastly we’ll implement the Capslock key(CL). Modify the capsLockPressed() function as follows.

    @IBAction func capsLockPressed(button: UIButton) {
        capsLockOn = !capsLockOn
    
        changeCaps(row1)
        changeCaps(row2)
        changeCaps(row3)
        changeCaps(row4)
    }
    

    Reference