Search code examples
iosswiftios-keyboard-extension

Slow Buttons with Custom Keyboard Extension


We made a custom keyboard and uploaded to the App Store, the design and the keyboard works fine. However we get a lot 1-star reviews due to slowness of the buttons. The question is. Is there a way to speed UIButtons up?

UIControl's touchUpInside works once the user release their finger off the button, in some cases people write so fast and touchUpInside isn't the "Fast" way do do it

TouchDown will fire before TouchUpInside because touch down is the action of your finger touching down on the phone.

Please advice on this, What and Which way do you generally prefer for a production keyboard

This is my current method of handling a tap: which's too slow!

func buttonAction(_ sender:UIButton!) {
        print("Button tapped")
       self.textDocumentProxy.insert("B")
}

Solution

  • The speed of TouchDown vs. TouchUpInside isn't really the issue. The problem comes when users are typing with both thumbs and the key strokes are interpreted in the wrong order.

    Apple’s default keyboard registers a key on touch up. But, on the iPhone, if a second key is pressed while the first is down, then the first key is registered then and doesn’t wait for its touch up. This keeps the input in the touch down order (for two thumb typing), but still reflects the touch up behavior.

    To implement this, you need to observe both TouchDown and TouchUpInside events.

    Here is one way you could do it. Create a property called pendingButton to track the last button pressed down, and process that button either when it is let up or another button is pressed down.

    You'll need to connect buttonDown to TouchDown events and buttonUp to TouchUpInside events.

    // last button pressed down
    var pendingButton: String?
    
    // connect button TouchDown events here    
    @IBAction func buttonDown(_ sender: UIButton) {
        // If we already have a pending button, process it before storing
        // the next one
        if let pending = self.pendingButton {
            self.textDocumentProxy.insert(pending)
        }
        self.pendingButton = sender.currentTitle
    }
    
    // connect button TouchUpInside events here   
    @IBAction func buttonUp(_ sender: UIButton) {
        // If the button being let up is the latest pending button,
        // then process it, otherwise ignore it
        if sender.currentTitle == self.pendingButton {
            self.textDocumentProxy.insert(self.currentTitle!)
            self.pendingButton = nil
        }
    }
    

    Note: You might also want to consider carefully other events such as TouchUpOutside. That should probably be connected to buttonUp as well depending on the desired behavior of your keyboard.

    If instead, dragging outside of a button cancels the button, then you should implement a function to observe TouchDragExit and then cancel the pending button if that is the pending button.

    // connect button TouchDragExit events here
    @IBAction func dragExit(_ sender: UIButton) {
        if sender.currentTitle == self.pendingButton {
            self.pendingButton = nil
        }
    }