Extension Approach to Keeping UITextFields in Sight
It’s inevitable — you design a great UI and then run it in a simulator or on a device. You tap a text field, the keyboard swoops up from the bottom, and you can’t see anything that you’re typing. There are frameworks out there to handle this situation and various techniques for one to “roll their own” — here’s one more for the collection.
Extensions are a great way to extend classes, structs, enums, and protocols whether or not you have access to the source code. It’s great for segregating functional blocks of protocol implementations (e.g. UITableViewDelegate, CLLocationDelegate) view controllers implement. I’ve personally adopted this practice as a means for keeping the source code clean and readable.
Extensions are even better for situations when you don’t have access to the original code. For example, let’s say you wanted the add a wizbang capability to transform any String in your code without having to subclass, force cast, everytime you wanted to make use of this new fangled feature. Extensions make doing this super simple
Normally, if I’m using extensiosn for something like UITableDelegate, I’ll just add the extensions in the same the source file as the UIViewController. When an extension is more general and will be applicable to all UIViewControllers, I add put the extension code in a separate file.
Identifying Active Text Fields
We’ll know add some code to the extension so that we can keep track of the the active text field. To do this we’ll make the extension implement the UITextFieldDelegate protocol and add some of the delegate methods to the file.
Implementing the UITextFieldDelegate protocol allows us to know when a text field (and which one it is) is being edited by the user. This will come into play a bit more when integrate the keyboard events described below. By default the
textFieldDidEndEditing callbacks do nothing be set and unset the activeTextField variable. If you want to do something special for these events, you can override these in your view controller code, but be sure to call the super of the methods (e.g.
super.textFieldDidBeginEditing) before adding your code.
textFieldShouldReturn callback resigns the first responder and returns false, which dismisses the keyboard. Like above, you can override this method if needed. You don’t need to call the super for this one.
You’ll notice a few things in the code snippet above — the main thing being
AssociatedKeys. This is a workaround I learned from a Medium article linked below for mimicing stored variables in extensions.
One of the main limitations of Swift Extensions is the impossibility to use stored properties. Let’s see a workaround…medium.com
Adding Keyboard Events
The keyboard size varies based on the different options one has (e.g. spellcheck, suggestions, layout, etc.) and the device being used. Luckily Apple provides some system notifications through NotificationCenter that allows an app to know when the keyboard will be shown and how big it is. To the code above we’ll add some additional lines code to register and de-register for the system notifications about the keyboard and use the
AssociatedKeys method for storing the height of the keyboard. Having this information will be essential in the next section when we implement the logic for making sure a text field is not occluded.
Keeping the Text Field in View
Now that we know which text field is being edited and we know the details about the keyboard, we can add the logic for moving the view to ensure the text field is not occluded by the keyboard. The meat of this is in the
checkForOcclusion method. We check to see if the bottom of the active text field is lower than the top of the keyboard and, if it is, move the root view of the view controller to bring it up above the keyboard.
We also add in some logic to the text field delegate and keyboard notification selectors to be smart about when we call
checkForOcclusion and reseting the view’s frame.
Using the Extension
Now with the extension all coded up, we can go ahead and implement it in a view controller. I have a UIViewController in my storyboard and have connected the delegate outlet for a UITextField to the ViewController. I find it easy to do it here instead of creating a reference and having to do it in the ViewController code, but it can be done there too using the
textfield.delegate = self syntax.
In addition to connecting the texfield delegate output to the view controller we also need to add two methods to our view controller class. These methods enable the view controller to register and deregister for the keyboard events based ont he view controller’s lifecycle. Two lines of code is all it takes!
The Finished Product
With the code added to the view controller class, go ahead and run the app. When a text field is clicked, the extension will check if the text field is occluded by the presenting keyboard, and if so, will move the view up over the keyboard. As the focused view changes, the extension will slide the view accordingly. Finally, when the keyboard is dismissed, the view returns to its original frame location.