In this tutorial, we’ll be discussing the UITextView element and implement the various forms of it in our application.
iOS UITextView
Unlike its name, a UITextView is not just a text view. You can edit it, type in it, scroll it.
A UITextView is a multiline text region. It has a built-in UIScrollView.
By default a UITextView is editable. To disabled editing you need to set the property isEditable
as false
.
To create it programmatically you need to create a rectangle with a width and height specified:
let uiTextView = UITextView()
uiTextView.frame = CGRect(x: 0, y: 0, width: 200, height: 150)
UITextView is for multi-line input whereas UITextField by default is for a single line only.UITextView does not provide a placeholder/hint text by default.
UITextViewDelegate
The UITextViewDelegate protocol defines a set of optional methods that gets triggered when the text is being edited. All the protocol methods are optional.
Following are some of the methods present in the UITextViewDelegate.
textViewDidBeginEditing
/textViewWillBeginEditing
textViewDidEndEditing
/textViewWillEndEditing
textViewDidChange
– Gets called when the text is editted.
textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String)
– This returns a boolean which asks if the text should be replaced or not.
An interesting use case would be to set the character limit on the UITextView:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText newText: String) -> Bool {
return textView.text.count + (newText.count - range.length) <= 140
}
This sets the character limit to 140. Just like twitter!
Enough talk. Lets code!
UITextView Implementation
Create a new XCode project and let’s get started:
In the Main.storyboard drag the UITextView onto the ViewController and do the following instructions:
-
- Set auto-layout constraints on the UITextView. We’ve set it to the bottom of the screen with a fixed height.
- Link the UITextView to the ViewController.swift file using the IBOutlet via Assistant Editor.
.
On running the application on the simulator and device we get:
That’s WEIRD!
The keyboard pops up over the UITextView.
On clicking return, the keyboard is getting dismissed.
We need to fix these.
Add the following code in your ViewController.swift file:
import UIKit
class ViewController: UIViewController, UITextViewDelegate {
@IBOutlet weak var bottomTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
bottomTextView.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.updateTextView(notification:)), name: Notification.Name.UIKeyboardWillChangeFrame, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.updateTextView(notification:)), name: Notification.Name.UIKeyboardWillHide, object: nil)
}
func textViewDidBeginEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.lightGray
}
func textViewDidEndEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.white
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "n" {
textView.resignFirstResponder()
return false
}
return true
}
@objc func updateTextView(notification: Notification)
{
if let userInfo = notification.userInfo
{
let keyboardFrameScreenCoordinates = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
let keyboardFrame = self.view.convert(keyboardFrameScreenCoordinates, to: view.window)
if notification.name == Notification.Name.UIKeyboardWillHide{
view.frame.origin.y = 0
}
else{
view.frame.origin.y = -keyboardFrame.height
}
}
}
}
In the above code, we’ve implemented the UITextViewDelegate Protocol, implemented keyboard dismiss logic and moved the UITextView above the keyboard.
- To set it on the UITextView we do:
bottomTextView.delegate = self
- We’ve implemented three functions of the UITextViewDelegate –
textViewDidBeginEditing
,textViewDidEndEditing
,textView(shouldChangeTextIn:)
. We change the background color of the UITextView when editting begins and ends.resignFirstResponder()
is used to dismiss the Keyboard. To do so on a UITextView we do:func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { if text == "n" { textView.resignFirstResponder() return false } return true }
- In the viewDidLoad, we’ve added two Notification observers which detect changes in Keyboard and trigger the function
updateTextView
. - Inside the
updateTextView
we change the position of the UITextView depending on the notification name. - To move the UITextView above the keyboard, we calculate the keyboard height programmatically and shift the whole view upwards by that height.
The output when the application is now run is:
Next, we’ll look into Auto-Sizing UITextViews
Auto-Size UITextView
UITextViews size can be changed depending on the content size of it.
Let’s create another ViewController in our storyboard and connect it via a Button segue.
- Add the Button to the current ViewController and set the constraints.
- Create another ViewController Scene and drag the Button using Ctrl+ Click to create a segue. Create a new Swift File SecondViewController.swift and set the name in the right pane
In the SecondViewController.swift, we’ll add the UITextView programmatically and set the constraints through Swift Code as well.
In the below code, we’ve:
- Added Placeholders on the UITextView
- Auto-increased the height of the UITextView when more content is typed.
- Set font size on the UITextView
import UIKit
class SecondViewController: UIViewController, UITextViewDelegate {
let topTextView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
addAnotherTextView()
}
func addAnotherTextView() {
topTextView.delegate = self
topTextView.text = "Enter your notes here"
topTextView.frame = CGRect(x: 0, y: 0, width: 200, height: 150)
topTextView.font = .systemFont(ofSize: 20)
view.addSubview(topTextView)
topTextView.translatesAutoresizingMaskIntoConstraints = false
[
topTextView.topAnchor.constraint(equalTo: view!.safeAreaLayoutGuide.topAnchor),
topTextView.leadingAnchor.constraint(equalTo: view!.leadingAnchor),
topTextView.trailingAnchor.constraint(equalTo: view!.trailingAnchor),
topTextView.heightAnchor.constraint(equalToConstant: 40)
].forEach{
$0.isActive = true
}
}
func textViewDidBeginEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.lightGray
if (textView.text == "Enter your notes here")
{
textView.text = ""
textView.textColor = .black
}
}
func textViewDidEndEditing(_ textView: UITextView) {
textView.backgroundColor = UIColor.white
if (textView.text == "")
{
textView.text = "Enter your notes here"
textView.textColor = .lightGray
}
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "n" {
textView.resignFirstResponder()
return false
}
return true
}
func textViewDidChange(_ textView: UITextView) {
let size = CGSize(width: view.frame.width, height: .infinity)
let approxSize = textView.sizeThatFits(size)
textView.constraints.forEach{(constraint) in
if constraint.firstAttribute == .height
{
constraint.constant = approxSize.height
}
}
}
}
textViewDidChange is a part of the UITextViewDelegate protocol. This gets triggered whenever text is added.
Each time we calculate the size of the text and increase the height constraint of the UITextView.
The output of the application in action is:
As you can see the height of the UITextView is increased. But the UITextView keeps pushing the top line above.
This is because of the ScrollBars. We can disable them by adding the following line inside the addAnotherTextView() function:
topTextView.isScrollEnabled = false
Now the top line doesn’t keep going out of the screen.
Let’s look at another use case.
Auto Sizing the UITextView till a specific height
We can size the UITextView such that it can expand only to a specific height.
Change your textViewDidChange function to:
func textViewDidChange(_ textView: UITextView)
{
if topTextView.contentSize.height >= 120.0
{
topTextView.isScrollEnabled = true
}
else
{
let size = CGSize(width: view.frame.width, height: .infinity)
let approxSize = textView.sizeThatFits(size)
textView.constraints.forEach {(constraint) in
if constraint.firstAttribute == .height{
constraint.constant = approxSize.height
}
}
topTextView.isScrollEnabled = false
}
}
The logic is pretty simple:
If the max height isn’t reached, keep the scrollbars disabled.
The output of the application in action is:
This brings an end to this tutorial on UITextView. We implement some practical scenarios that might help you in your iOS Development by using UITextViewDelegate and also Notification Observers.
You can download the project from the link below: