Input/Display Multiline TextField

Published by @SoNiceInfo at 6/24/2020


iOS 14 Support

For inputting multiple lines text, TextEditor has been added in the new SwiftUI in WWDC20.
We don't need to use UITextView wrapped by SwiftUI any more. Using Text and lineLimit(nil) modifier to get multiple lines to display.

import SwiftUI

struct ContentView: View {
    @State var text: String = ""

    var body: some View {
        VStack {
            // Input
            TextEditor(text: $text)
                .frame(width: UIScreen.main.bounds.width * 0.8, height: 200)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.blue, lineWidth: 5)
                )
            // Display
            Text(text)
                .foregroundColor(.yellow)
                .lineLimit(nil)
                .padding(5)
                .frame(width: UIScreen.main.bounds.width * 0.8, height: 200, alignment: .topLeading)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.green, lineWidth: 5)
                )
        }
    }
}

iOS 13 Support

To display more than one text, use the Text and lineLimit(nil) modifier to get multiple lines.
But we can't make TextField of multiple lines with them.
So we use UIViewRepresentable to arrange UITextView so that we can use it in SwiftUI.

Prepare MultilineTextField Struct

struct MultilineTextField: UIViewRepresentable {
    @Binding var text: String
}

Creating a MultilineTextField that supports multi-line text input.
Conforms to the UIViewRepresentable protocol.
We can now use UITextView.
The MultilineTextField also takes the binding variable text and makes it available for reference and modification.

Creating a Text Field with UITextView

struct MultilineTextField: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.isScrollEnabled = true
        view.isEditable = true
        view.font = UIFont.systemFont(ofSize: 18)
        return view
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        if uiView.text != text {
            uiView.text = text
        }
    }
}

Next, implementing makeUIView and updateUIView.
Creating UITextView instance at makeUIView.
Apply view.isScrollEnabled = true for Scrollability, view.isEditable = true for editable.

In updateUIView, if the text variable is changed, the contents of the MultilineTextField are also changed.

Notify Parent View for Text Changing

struct MultilineTextField: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UITextView {
        view.delegate = context.coordinator
        return view
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator : NSObject, UITextViewDelegate {

        var parent: MultilineTextField

        init(_ textView: MultilineTextField) {
            self.parent = textView
        }

        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            return true
        }

        func textViewDidChange(_ textView: UITextView) {
            self.parent.text = textView.text
        }
    }
}

A change notification of the variable text modified is not sent to the parent View by default.
Use Coordinator to notify the parent view of changes from the child view.
Don't forget to add delegate = context.coordinator in makeUIView.

Completion

MultilineTextField are called like TextField() with MultilineTextField(text: $text).
View modifier is also available.
Blue area is MultilineTextField(text: $text), result is displayed in green area.

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @State var text: String = ""

    var body: some View {
        VStack {
            // Input
            MultilineTextField(text: $text)
                .frame(width: UIScreen.main.bounds.width * 0.8, height: 200)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.blue, lineWidth: 5)
                )
            // Display
            Text(text)
                .foregroundColor(.yellow)
                .lineLimit(nil)
                .padding(5)
                .frame(width: UIScreen.main.bounds.width * 0.8, height: 200, alignment: .topLeading)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.green, lineWidth: 5)
                )
        }
    }
}

// TextFiled for multi line supported.
struct MultilineTextField: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.delegate = context.coordinator
        view.isScrollEnabled = true
        view.isEditable = true
        view.isUserInteractionEnabled = true
        view.font = UIFont.systemFont(ofSize: 18)
        return view
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        if uiView.text != text {
            uiView.text = text
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator : NSObject, UITextViewDelegate {

        var parent: MultilineTextField

        init(_ textView: MultilineTextField) {
            self.parent = textView
        }

        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            return true
        }

        func textViewDidChange(_ textView: UITextView) {
            self.parent.text = textView.text
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
An image of input/display multi-line text.

Please see how to close keyboard here.
Close Keyboard

References

ios - How do I create a multiline TextField in SwiftUI? - Stack Overflow
SwiftUI Multiline Text - Swiftly Dierkes

    I released iOS App!

    ToDo App

    Visualize Activity, Data sharing with iCloud, Dark mode supported.

    リリースしたToDoアプリのスクリーンショット

    IP Address bookmark.

    Check and bookamrk IP address of all interfaces with geolocation.

    リリースしたIPアドレス保存アプリのスクリーンショット