Override UITextField Touch Interaction

Override UITextField Touch Interaction

Eliminate the editing functionality of UITextField using the UITextFieldDelegate protocol.

Go to the profile of  Jonathan Banks
Jonathan Banks
5 min read
Time
1hour
Platforms
iOS
Difficulty
Easy
Technologies
Swift

This article provides step-by-step guidance to override the standard touch interaction of a UITextField. It builds upon code for a custom control named SelectorTextField, detailed in a previous article located here

Overview

An interesting issue arose while writing a guide on creating a custom dropdown selector control. That SelectorTextField contains a UITextField, one of several built-in iOS controls used in its construction. The enhanced UITextField uses a UITapGestureRecognizer to trigger the dropdown menu.

The user-created UITapGestureRecognizer intercepts tap touch events and prevents the first responder from triggering the keyboard. The embedded UITextField recognizes other gestures, such as long-presses. This is expected standard behavior. Taps are overridden but no other code added to intercept long-presses, swipes, or other gestures.

Screen-Shot-2019-07-21-at-6.28.48-PM

The control should not display the edit options if it is long-pressed by the user. User interaction should only result in displaying the drop-down choices. What are some options to correct this behavior? Some possibilities could include:

  • Add new gesture recognizers onto the custom control that consume the long-press but perform no action. The downside is adding confusing and unnecessary code to the project.
  • Replace the UITextField with a UILabel or UIButton, set the isUserInteractionEnabled attribute to true.

The superior choice is to modify the behavior of the UITextField for educational as well as design and coding purposes.

Setup

The final code for this tutorial is located here. The two tutorials and links to download the SelectorTextField code are found in the links below:

The initial part of the tutorial is a false start that leads to a dead end. It's there to show the initial thought process taken to overcome this problem. If the only desire is to get right to the solution, click here

False Start: canPerformAction

Among the methods of UITextField is canPerformAction that returns true for allowed actions and false for disallowed. The signature of the function is:

func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool

According to Apple's documentation the parameters of the method are as follows:

action
A selector that identifies a method associated with a command. For the editing menu, this is one of the editing methods declared by the UIResponderStandardEditActions informal protocol (for example, copy:).

sender
The object calling this method. For the editing menu commands, this is the shared UIApplication object. Depending on the context, you can query the sender for information to help you determine whether a command should be enabled.

The target issue to resolve is the edit options popping up when the user long-presses the UITextField. The documentation mentions the method UIResponderStandardEditActions that seems promising.

Examining it further, UIResponderStandardEditActions is a protocol that provides some interesting options for customization. According to the docs:

Responder objects can implement the methods of this protocol to handle standard editing-related actions. For example, a UIMenuController object reports editing actions using these methods. The menu controller then asks UIKit to search the responder chain for an object that implements the appropriate method, calling the method on the first object that implements it.

The documentation indicates the methods include:

  • cut
  • copy
  • paste
  • delete
  • select
  • selectAll
  • toggleBoldface
  • toggleItalics
  • toggleUnderline
  • makeTextWritingDirectionLeftToRight
  • makeTextWritingDirectionRightToLeft
  • increaseSize
  • decreaseSize

Subclassing UITextField to override the canPerformAction function is one option. It may prevent the SelectorTextField from allowing those methods to activate the popup.

Write a class that extends UITextField and overrides canPerformAction. Refer to the sample code below:

import UIKit

class EmbeddedTextField: UITextField {

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        
    if (action == #selector(UIResponderStandardEditActions.paste(_:)) ||
        action == #selector(UIResponderStandardEditActions.copy(_:)) ||
        action == #selector(UIResponderStandardEditActions.select(_:)) ||
        action == #selector(UIResponderStandardEditActions.selectAll(_:)) ||
        action == #selector(UIResponderStandardEditActions.cut(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleBoldface(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleItalics(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleUnderline(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionLeftToRight(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionRightToLeft(_:)) ||
        action == #selector(UIResponderStandardEditActions.increaseSize(_:)) ||
        action == #selector(UIResponderStandardEditActions.decreaseSize(_:)) ||
        action == #selector(UIResponderStandardEditActions.delete(_:)) 
        )  {

        return false
    }

    return super.canPerformAction(action, withSender: sender)
        
    }

}

The overridden canPerformAction method returns false for every available option in UIResponderStandardEditActions. Eliminating the call to super.canPerformAction and returning false without specifying options would result in the same outcome.

Returning true for some options and false for others allows exacting specification of how the UITextField reacts to inputs.

Go back to the SelectorTextField class and change the textField variable from a type of UITextField to EmbeddedTextField. Change the declaration and the instance in initializeComponents.

Execute the Code

Run the project with the modifications made thus far. Long-press the control and there are still pop-up edit functions showing. Refer to the screenshot:

Screen-Shot-2019-07-21-at-10.57.20-PM-1

The documentation does not contain information for the "Look up" and "Share" functions. Therefore, they are not exposed publicly by the UIResponderStandardEditActions protocol and are not accessible in a legitimate way unless accessing private APIs. The problem is Apple started rejecting apps using private APIs years ago.

Unfortunately, this means that the path was a dead-end. The search for a viable solution continues.

Solution: Implement the textFieldShouldBeginEditing function of UITextFieldDelegate

What about investigating the functions of UITextFieldDelegate? There may be an exposed method that can intercept and reject user input. Looking at the documentation for the UITextFieldDelegate we find the following:

You use these methods to validate text that was typed by the user, to respond to specific interactions with the keyboard, and to control the overall editing process. Editing begins shortly before the text field becomes the first responder and displays the keyboard (or its assigned input view).

Controlling the overall editing process sounds promising. Continuing on in the Apple documentation:

Before becoming the first responder, the text field calls its delegate’s textFieldShouldBeginEditing(_:) method. Use that method to allow or prevent the editing of the text field’s contents.

Allowing or preventing editing of the text field content is desired to resolve the problem on hand. The textFieldShouldBeginEditing method of the UITextFieldDelegate needs implementation. Refer to the code below:

extension SelectorTextField:UITextFieldDelegate{

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        return false
    }
}

Returning false from textFieldShouldBeginEditing prevents all editing.

Next, make the UITextField delegate the SelectorTextField upon instantiation in the initializeComponents function.

textField.delegate = self

Run the application now. The SelectorTextField does not permit editing of the embedded UITextField, none of the pop-up editing functions work anymore, and the user can still tap it to trigger the dropdown animation.

Final Thoughts

Learning to navigate the official Apple documentation to parse out relevant data is a very important skill to develop when creating iOS applications.

This tutorial included an examination of two distinct sets of Apple's documentation. The final solution involved the discovery of the textFieldShouldBeginEditing method of UITextFieldDelegate. That came solely from examining the documentation.

Apple's docs are extremely well-written and possess a wealth of knowledge around features, functions, and capabilities of components. Relying solely on the documentation will sometimes result in taking wrong paths. It may seem like wasting time but incorrect paths can be sources of important tangential discoveries. Use Stack Overflow if speed is critical. Otherwise, invest the time into Apple's documentation and build a much deeper well of knowledge.