SwiftUI · · 2 min read

Using SwiftUI TextEditor for Multiline Text Input

Using SwiftUI TextEditor for Multiline Text Input

The first version of SwiftUI doesn’t come with a native UI component for multiline text field. For multiline input, you can wrap a UITextView from the UIKit framework and make it available to your SwiftUI project by adopting the UIViewRepresentable protocol. We have written a detailed tutorial on the implementationg, showing you how to create a multiline text field in SwiftUI.

In iOS 14, which is expected to be released in late Sep, Apple introduced a new component called TextEditor for the SwiftUI framework. This TextEditor enables developers to display and edit multiline text in your apps. In this tutorial, we will show you how to use TextEditor for handling multiline input.

To follow this tutorial, please make sure you download and install Xcode 12 beta.

Using TextEditor

It is very easy to use TextEditor. You just need to have a state variable to hold the input text. Then create a TextEditor instance in the body of your view like this:

struct TextView: View {
    @State private var inputText = ""

    var body: some View {
        TextEditor(text: $inputText)
    }
}

To instantiate the text editor, you pass the binding of inputText so that the state variable can store the user input.

You can customize the editor like any SwiftUI view. For example, the below code changes the font type and adjust the line spacing of the text editor:

TextEditor(text: $inputText)
    .font(.title)
    .lineSpacing(20)
    .autocapitalization(.words)
    .disableAutocorrection(true)
    .padding()

Optionally, you can enable/disable the auto-capitalization and auto-correction features.

Using the onChange() Modifier to Detect Text Input Change

For UITextView of the UIKit framework, it works with the UITextViewDelegate protocol to handle the editing changes. So, how about TextEditor? How can we detect the change of user input and perform further processing?

The new version of SwiftUI introduces an onChange() modifier which can be attached to TextEditor or any other view. Let’s say, if you are building a note application using TextEditor and need to display a word count in real time, you can attach the onChange() modifier to TextEditor like this:

struct ContentView: View {
    @State private var inputText = ""
    @State private var wordCount: Int = 0

    var body: some View {
        ZStack(alignment: .topTrailing) {
            TextEditor(text: $inputText)
                .font(.body)
                .padding()
                .padding(.top, 20)
                .onChange(of: inputText) { value in
                    let words = inputText.split { $0 == " " || $0.isNewline }
                    self.wordCount = words.count
                }

            Text("\(wordCount) words")
                .font(.headline)
                .foregroundColor(.secondary)
                .padding(.trailing)
        }
    }
}

In the code above, we declare a state property to store the word count. And, we specify in the onChange() modifier to monitor the change of inputText. So, whenever a user types a character, the code inside the onChange() modifier will be invoked. In the closure, we compute the total number of words in inputText and update the wordCount variable accordingly.

If you run the code in a simulator, you should see a plain text editor and it displays the word count in real time.

swiftui-texteditor-demo

With the upcoming release of iOS 14, we will cover the new API of SwiftUI in our Mastering SwiftUI book. If you want to dive deeper into this UI framework, please check out the book here.

Read next