Forbid user interaction with UI except the keyboard while editing text field

1.4k views Asked by At

New to Swift.

When a user clicks on a text field to begin typing, is there a way to disable all user interaction other than typing into that text field? I want it so that the user is unable to click outside the text field, and should press the return key to dismiss keyboard, where user interaction is enabled again (I have the dismiss keyboard part set up).

I know that the isUserInteractionEnabled function can stop all interaction, but I still want the user to be able to interact with the keyboard.

Thanks

2

There are 2 answers

0
NonCreature0714 On

The behavior you describe is can be accomplished by manipulating the Boolean isEnabled value if UI elements. There are a couple of steps to make this smooth and user friendly.

Just good practice, and not required for this example, but I like to make extension of the MyViewController, to subscribe/unsubscribe to keyboard notifications and so on in it, like so:

import Foundation
import UIKit

extension MyViewController {            

    //MARK: Subscribing/Unsubribing to NotificationCenter.
    func subcribeToKeyboardNotifications(){    
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    func unsubscribeToKeyboardNotifications(){
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }

    //MARK: Screen manipulation methods, to move the UIView up when the bottom text field is editing.
    func keyboardWillShow(_ notification: Notification) {
        if bottomTextField.isEditing {
            view.frame.origin.y =  getKeyboardHeight(notification) * -1
        }
    }

    func keyboardWillHide(_ notification: Notification) {
        if bottomTextField.isEditing {
            view.frame.origin.y = 0
        }
    }

    //MARK: Value returning method to calculate keyboard height.
    private func getKeyboardHeight(_ notification: Notification) -> CGFloat {
        let userInfo = (notification as NSNotification).userInfo!
        let keyboardSize = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue
        return keyboardSize.cgRectValue.height
    } 
}

Next, in MyViewController, I set the keyboard to resignToFirstResponder() when the return key is tapped, and I make sure to set my UITextFieldDelegates.

import Foundation
import UIKit

class MyViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var someButton: UIButton!
    @IBOutlet weak var topTextField: UITextField!
    @IBOutlet weak var bottomTextField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        //Set the delegates to self, it this example.
        topTextField.delegate = self
        bottomTextField.delegate = self
        enableUIElements(true)
    }

    //Delegate function to type stuff into the text field.
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        enableUIElements(false)
        var newText = textField.text! as NSString
        newText = newText.replacingCharacters(in: range, with: string) as NSString
        let textSize: CGSize = newText.size(attributes: [NSFontAttributeName: textField.font!])
        return textSize.width < textField.bounds.size.width
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder() //Dismiss the keyboard.
        enableUIElements(true) //enable the UI elements.
        return true
    }

    //Create a function to enable/disable UI elements.
    func enableUIElements(_ enable: Bool){
        someButton.isEnabled = enable
        //TODO: add other elements here.
    }
}

Note that last function!!! Use it to enable/disable other elements while the keyboard is active, as I've shown.

That should get you where you need to go :)

1
hamsternik On

So, the first idea, which came up to me is disabling every UI element into your view while you're focused on the UITextField instance.

Create a function, which will obtain a UI element as an argument (UITextField for ex). In the function's body start to check every UI element and cyclically disable everyone, which will not equal to the passed argument. You can verify your text field for the type and text additionally.

Here's draft of the function's body:

func disableAll(exceptInput element: UITextField) {
    for item in self.view.subviews {
        if item != element {
            item.isUserInteractionEnabled = false
        } else {
            item.isUserInteractionEnabled = true
        }
    }
}

And perform this method in the text field action, for ex. in the onDidStart.

And, obviously, don't forget about enabling all elements for user interaction. The code is simple:

func enableAll() {
    for item in self.view.subviews {
        item.isUserInteractionEnabled = true
    }
} 

This method you can perform on the onDidEnd IBAction for every UITextField. The last step is necessary if you want to run the normal application behavior.