0

I am looking to enable the return key of a UITextField keyboard if (and only if) the user inputs a valid email address.

I have tried a solution that dates back from 2009 which uses the private method setReturnKeyEnabled. Unfortunately, this method does not seem to exist anymore:

No known instance method for selector 'setReturnKeyEnabled:'

How can I programmatically enable and disable the return key of a keyboard?

Community
  • 1
  • 1
Randomblue
  • 112,777
  • 145
  • 353
  • 547
  • you should say so in your question if you want the return key to be greyed out. because "disable" can mean many things. If the return key does not work, it is disabled as well, I could argue. And it doesn't seem to be possible to grey the return key out, at least not with documented techniques. – Michael May 24 '15 at 16:07

4 Answers4

1

All the other solutions do not answer the question. OP wants to "gray" out the return button on the keyboard as a visual signal to the user.

Here is my solution, working on iOS 13. You may have to modify the solution slightly for other iOS versions.

First, I extend UITextFieldDelegate.

func getKeyboard() -> UIView?
    {
        for window in UIApplication.shared.windows.reversed()
        {
            if window.debugDescription.contains("UIRemoteKeyboardWindow") {
                if let inputView = window.subviews
                    .first? // UIInputSetContainerView
                    .subviews
                    .first // UIInputSetHostView
                {
                    for view in inputView.subviews {
                        if view.debugDescription.contains("_UIKBCompatInputView"), let keyboard = view.subviews.first, keyboard.debugDescription.contains( "UIKeyboardAutomatic") {
                            return keyboard
                        }
                    }
                }
                
            }
        }
        return nil
    }

Then, whenever I need to disable the "return" key, we can do (replace delegate with the variable name of your delegate object):

if let keyboard = delegate.getKeyboard(){
    keyboard.setValue(text == nil, forKey: "returnKeyEnabled")
}
Winston Du
  • 340
  • 2
  • 9
  • This seems to be a really good answer to the question. Why is this not getting more votes? Does this affect other things that is not obvious just from looking at this code? – daniel Sep 30 '22 at 20:28
  • 1
    @paul-b's answer, which came after mine, is better in that it also addresses OP's need to verify email. It also is a bit smarter on the keypath. But overall, I think it's just that few folks ask this question. – Winston Du Oct 03 '22 at 00:31
  • Ok. I did use part of his code. I hope using this "back door" is reliable. It appears as though Apple doesn't intend for "returnKeyEnabled" to be used anymore, if they removed the method. I could be wrong. Maybe Apple deliberately allowed key-value-coding to access "returnKeyEnabled". Thanks. – daniel Oct 04 '22 at 01:52
0

Extend UITextFieldDelegate.

extension UITextFieldDelegate {
    func setReturnKeyState(for textField: UITextField, isEnabled: Bool, delay: Double? = nil) {
        textField.enablesReturnKeyAutomatically = false
        if textField.delegate != nil {
            if let delay = delay {
                DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
                    textField.setValue(isEnabled, forKeyPath: "inputDelegate.returnKeyEnabled")
                }
            } else {
                textField.setValue(isEnabled, forKeyPath: "inputDelegate.returnKeyEnabled")
            }
        }
    }
}

Define validation rule.

private func isValidEmail(_ input: String?) -> Bool {
    guard let input = input else { return false }
    let emailRegex = try! NSRegularExpression(pattern: ".+@.+\\..+")
    let fullNSRange = NSRange(input.startIndex..., in: input)
    return emailRegex.rangeOfFirstMatch(in: input, options: .anchored, range: fullNSRange) == fullNSRange
}

In UITextFieldDelegate's methods use setReturnKeyState(for textField: ...).

func textFieldDidBeginEditing(_ textField: UITextField) {
    setReturnKeyState(for: textField, isEnabled: isValidEmail(textField.text), delay: 0.1) // A bit hacky it needs delay here
}

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if var text = textField.text, let range = Range(range, in: text) {
        text.replaceSubrange(range, with: string)
        setReturnKeyState(for: textField, isEnabled: isValidEmail(text))
    }
    return true
}
Paul B
  • 3,989
  • 33
  • 46
-2

You don't really enable or disable the return key. Instead, if the user presses the return key, the delegate method -textFieldShouldReturn: gets called. If you want to hide the keyboard in that case, you have to call -resignFirstResponder on the text field, in your delegate method.

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextFieldDelegate_Protocol/index.html

Michael
  • 6,451
  • 5
  • 31
  • 53
-2
  1. Set the delegate on your UITextField
  2. For your delegate instance (the one you just set that conforms to UITextFieldDelegate), implement: - (BOOL)textFieldShouldReturn:(UITextField *)textField.

There are other callbacks in the delegate, such as textField:shouldChangeCharactersInRange:replacementString: that may be of interest to you, if you're trying to keep the textField always "correct" and not allow the user to continue typing after the textfield has become erroneous wrt to your preferred formatting / validation. Definitely worth taking a look at the documentation for UITextFieldDelegate for all your possible callbacks.

greymouser
  • 3,133
  • 19
  • 22