When you just want to display text, use a label. When you need the user to input text, then you need to use a text field. The key to using a text field is that your app must display a virtual keyboard on the screen. When using the virtual keyboard, you need to be aware of its position on the screen and the text fields.
If text fields appear near the top of the screen, then the virtual keyboard can slide up without risk of covering up the text fields. However, if the text fields appear too close to the bottom of the screen, the virtual keyboard will slide up and cover the text fields that the user wants to type in.
If this occurs, your app needs to slide its view up to keep the text field visible while allowing the virtual keyboard to fill the bottom of the screen. After the user gets done typing text in a text field, then the virtual keyboard needs to disappear and the text field needs to slide back down again.
Dismissing the keyboard by resigning the first responder
When a text field or text view becomes first responder, the keyboard automatically appears. Conversely, when they’re no longer the first responder, the keyboard automatically disappears. If you want to dismiss the keyboard, you need the relevant text field or text view to no longer be first responder, and you can do that by calling their resignFirstResponder
method:
textField.resignFirstResponder()
Detecting the return key tap to dismiss the keyboard
You can detect that the Return key was tapped on a text field by using the delegation pattern. The text field’s delegate has a textFieldShouldReturn
method that's called when the Return key is tapped, that has a reference to the text field itself. Follow the three steps for the delegation pattern:
UITextFieldDelegate
protocol on an extension of the ViewController.Implement any required methods on the protocol. You want to implement the textFieldShouldReturn
method:
extension MyViewController: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool { textField.resignFirstResponder() return true } }
Detecting touches to dismiss the keyboard
You can detect touches using the touchesEnded
method. You can use this method to dismiss the keyboard when the user touches anywhere on the screen.
To resign the first responder, instead of calling resignFirstResponder
directly on the view, you can also use the more generic method endEditing
on the root view of the scene. The endEditing
method goes on a hunt through its subview hierarchy until it finds the first responder, and then it asks it politely to resign. If its force parameter is set to true
, it’s slightly less polite with the first responder, forcing it to resign.
Add the endEditing
method to a touchesEnded
method:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) view.endEditing(true) }
Slide the view up by the size of the virtual keyboard’s height
To make the screen slide up and down to accommodate the appearance of the virtual keyboard, every app needs to use the iOS notification center, which detects when events will occur. When the user clicks in a text field, this sends a notification that the virtual keyboard needs to appear. When the user is done editing text in a text field, this sends another notification that the virtual keyboard needs to go away.
To receive notifications, an app needs to use the addObserver
method that defines an object (the view) to receive notifications along with a method to run when it receives that specific notification. To detect when the virtual keyboard should appear and disappear, we’ll need to detect two notifications:
Next, we’ll need to write two functions to display the virtual keyboard and hide it. To show the virtual keyboard, we need to slide the view up by the size of the virtual keyboard’s height, which varies depending on the type of iOS device it runs on.
To hide the virtual keyboard, we need to return the view back to its original Y position that determines its vertical position on the screen. However, the virtual keyboard will never go away as long as the cursor appears in a text field.
To tell our app to make the virtual keyboard go away, we also need to detect if the user taps anywhere outside of a text field.
To see how to make the virtual keyboard appear and disappear with text fields, follow these steps.
Click the Main.storyboard file in the Navigator pane. Xcode displays the single view. Click the Library icon to display the Object Library. Drag and drop two text fields in the bottom one-third of the view, so when the virtual keyboard slides up from the bottom, it will cover the text fields
The entire ViewController.swift file should look like this:
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder. keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder. keyboardWillHideNotification, object: nil) let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard)) view.addGestureRecognizer(tap) } @objc func dismissKeyboard() { view.endEditing(true) } @objc func keyboardWillShow(notification: NSNotification) { if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { if self.view.frame.origin.y == 0 { self.view.frame.origin.y -= keyboardSize.height } } } @objc func keyboardWillHide(notification: NSNotification) { if ((notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil { if self.view.frame.origin.y != 0 { self.view.frame.origin.y = 0 } } } }
Scroll view using ScrollView
Sometimes, the content that you want on a view doesn’t fit in the view, such as when the keyboard appears over the top of the form. Scroll views make it possible for the user to scroll around a view to explore its content.
Scroll views have advantages related to managing the keyboard:
scrollRectToVisible
to request that a specific area of content be visible.Follow the steps to set up the form to use scroll views:
If you run your app now, you’ll notice that the form still doesn’t scroll, even if you select a text field, causing the keyboard to appear. Because the scrollable content is the same size as the scroll view, scrolling isn’t necessary, and the keyboard showing doesn’t automatically make any adjustments to the scrollable area - you need to do this part manually.
The best approach to make this adjustment is to create a bottom margin for the scrollable content with the scroll view’s contentInset
property.
You’ll need to determine the amount to offset the scrollable content. The keyboard height itself doesn’t change as it shows, so you’ll need to calculate this by subtracting the keyboard y position from the height of the root view.
Add the following to the keyboardFrameChanges
method:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(keyboardFrameChanges), name: UIResponder.keyboardWillChangeFrameNotification, object: nil) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) NotificationCenter.default.removeObserver(self) } @objc func keyboardFrameChanges(notification:Notification) { //get keyboard height guard let userInfo = notification.userInfo, var keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } keyboardFrame = self.view.convert(keyboardFrame, from: nil) let offset = self.view.frame.height - keyboardFrame.origin.y scrollView.contentInset.bottom = offset scrollView.scrollIndicatorInsets.bottom = offset }
Create an outlet for the scroll view and call it scrollView
.
You can now set the contentInset
property on the scroll view:
scrollView.contentInset.bottom = offset
Run the app, tap on a text field, and you should find that your form is now scrollable! One thing will appear a little strange though—the scroll indicator on the right isn’t right. Set a bottom margin for the scroll indicator as well to resolve this:
scrollView.scrollIndicatorInsets.bottom = offset