Multiline Editable Text Field in Swiftui

How do I create a multiline TextField in SwiftUI?

Update: While Xcode11 beta 4 now does support TextView, I've found that wrapping a UITextView is still be best way to get editable multiline text to work. For instance, TextView has display glitches where text does not appear properly inside the view.

Original (beta 1) answer:

For now, you could wrap a UITextView to create a composable View:

import SwiftUI
import Combine

final class UserData: BindableObject {
let didChange = PassthroughSubject<UserData, Never>()

var text = "" {
didSet {
didChange.send(self)
}
}

init(text: String) {
self.text = text
}
}

struct MultilineTextView: UIViewRepresentable {
@Binding var text: String

func makeUIView(context: Context) -> UITextView {
let view = UITextView()
view.isScrollEnabled = true
view.isEditable = true
view.isUserInteractionEnabled = true
return view
}

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

struct ContentView : View {
@State private var selection = 0
@EnvironmentObject var userData: UserData

var body: some View {
TabbedView(selection: $selection){
MultilineTextView(text: $userData.text)
.tabItemLabel(Image("first"))
.tag(0)
Text("Second View")
.font(.title)
.tabItemLabel(Image("second"))
.tag(1)
}
}
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(UserData(
text: """
Some longer text here
that spans a few lines
and runs on.
"""
))

}
}
#endif

Sample Image

Make the TextField Multiline - SwiftUI

Since iOS 16

The lineLimit modifier works as expected if you choose .vertical value for the axis parameter. And it also supports range now:

TextField("Title", text: $text, axis: .vertical)
.lineLimit(5...10)

Demo



Since iOS 14

Form iOS 14 and with Xcode 12, it's available as TextEditor

struct ContentView: View {
@State var text: String = "Multiline \ntext \nis called \nTextEditor"

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


iOS 13

Also, if you want to support iOS 13, you can take at look this answer to port UITextField inside SwiftUI with full access to all of its properties.

How do I create a Multi Line Text Field in SwiftUI for MacOS?

Here is some initial demo of component like iOS14 TextEditor.

Demo prepared & tested with Xcode 11.7 / macOS 10.15.6

demo

struct TestTextArea: View {
@State private var text = "Placeholder: Enter some text"

var body: some View {
VStack {
TextArea(text: $text)
.border(Color.black)
// Text(text) // uncomment to see mirror of enterred text
}.padding()
}
}

struct TextArea: NSViewRepresentable {
@Binding var text: String

func makeNSView(context: Context) -> NSScrollView {
context.coordinator.createTextViewStack()
}

func updateNSView(_ nsView: NSScrollView, context: Context) {
if let textArea = nsView.documentView as? NSTextView, textArea.string != self.text {
textArea.string = self.text
}
}

func makeCoordinator() -> Coordinator {
Coordinator(text: $text)
}

class Coordinator: NSObject, NSTextViewDelegate {
var text: Binding<String>

init(text: Binding<String>) {
self.text = text
}

func textView(_ textView: NSTextView, shouldChangeTextIn range: NSRange, replacementString text: String?) -> Bool {
defer {
self.text.wrappedValue = (textView.string as NSString).replacingCharacters(in: range, with: text!)
}
return true
}

fileprivate lazy var textStorage = NSTextStorage()
fileprivate lazy var layoutManager = NSLayoutManager()
fileprivate lazy var textContainer = NSTextContainer()
fileprivate lazy var textView: NSTextView = NSTextView(frame: CGRect(), textContainer: textContainer)
fileprivate lazy var scrollview = NSScrollView()

func createTextViewStack() -> NSScrollView {
let contentSize = scrollview.contentSize

textContainer.containerSize = CGSize(width: contentSize.width, height: CGFloat.greatestFiniteMagnitude)
textContainer.widthTracksTextView = true

textView.minSize = CGSize(width: 0, height: 0)
textView.maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
textView.isVerticallyResizable = true
textView.frame = CGRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)
textView.autoresizingMask = [.width]
textView.delegate = self

scrollview.borderType = .noBorder
scrollview.hasVerticalScroller = true
scrollview.documentView = textView

textStorage.addLayoutManager(layoutManager)
layoutManager.addTextContainer(textContainer)

return scrollview
}
}
}

SwiftUI: How do I make TextField fit multi-line content?

It seems there's no direct argument to manage multiline padding correctly. They are maybe underdevelopping. But the following will give you a straight workaround solution to what you are expecting.

extension String{
var extraLines : String{ get{
return self + String(repeating:"\n", count: self.components(separatedBy: "\n").count - 1)
}}
}

struct TextFieldDemo: View {
var content: Binding<String>

init(content: Binding<String>) {
self.content = content
}

@State var height : CGFloat? //current height

let constHeightRatio : CGFloat = 0.55 //use for assembly with other fonts.
let defaultHeight : CGFloat = 250 //use for assembly with other views.

var body: some View {
TextField("Custom placeholder", text: content).environment(\.multilineTextAlignment, .center).alignmentGuide(.bottom) { (ViewDimensions) -> CGFloat in
if self.height == nil {self.height = ViewDimensions.height}
return ViewDimensions.height
}.frame( height: (height ?? defaultHeight) * constHeightRatio, alignment: .bottom).background(Color.yellow)
}
}

#if DEBUG
struct TextInputRowPreviews: PreviewProvider {

static var previews: some View {
let content = "content\ncontent\ncontent".extraLines
return
TextFieldDemo(content: .constant(content))
}
}
#endif

This works fine for single view. If view assembly is required (with other stacking views, etc), you may adjust defaultHeight and/or constHeightRatio to achieve what you want. Hopefully it works for you too.

SwiftUI - Is there a way to create a read-only TextEditor?

Very basic example of scrolling text:

struct ContentView: View {
@State var content = "Long text here..."

var body: some View {
ScrollView {
VStack {
Text(content)
.lineLimit(nil)
}.frame(maxWidth: .infinity)
}
}
}

Here's an example with some long text:

import SwiftUI

struct ContentView: View {
@State var content =
"""
Label

A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.

Button

You can set the title, image, and other appearance properties of a button. In addition, you can specify a different appearance for each button state.

Segmented Control

The segments can represent single or multiple selection, or a list of commands.

Each segment can display text or an image, but not both.

Text Field

Displays a rounded rectangle that can contain editable text. When a user taps a text field, a keyboard appears; when a user taps Return in the keyboard, the keyboard disappears and the text field can handle the input in an application-specific way. UITextField supports overlay views to display additional information, such as a bookmarks icon. UITextField also provides a clear text control a user taps to erase the contents of the text field.

Slider

UISlider displays a horizontal bar, called a track, that represents a range of values. The current value is shown by the position of an indicator, or thumb. A user selects a value by sliding the thumb along the track. You can customize the appearance of both the track and the thumb.

Switch

Displays an element that shows the user the boolean state of a given value. By tapping the control, the state can be toggled.

Activity Indicator View

Used to indicate processing for a task with unknown completion percentage.

Progress View

Shows that a lengthy task is underway, and indicates the percentage of the task that has been completed.

Page Control

UIPageControl indicates the number of open pages in an application by displaying a dot for each open page. The dot that corresponds to the currently viewed page is highlighted. UIPageControl supports navigation by sending the delegate an event when a user taps to the right or to the left of the currently highlighted dot.

Stepper

Often combined with a label or text field to show the value being incremented.

Horizontal Stack View

An UIStackView creates and manages the constraints necessary to create horizontal or vertical stacks of views. It will dynamically add and remove its constraints to react to views being removed or added to its stack. With customization it can also react and influence the layout around it.

Vertical Stack View

An UIStackView creates and manages the constraints necessary to create horizontal or vertical stacks of views. It will dynamically add and remove its constraints to react to views being removed or added to its stack. With customization it can also react and influence the layout around it.

Table View

Coordinates with a data source and delegate to display a scrollable list of rows. Each row in a table view is a UITableViewCell object.

The rows can be grouped into sections, and the sections can optionally have headers and footers.

The user can edit a table by inserting, deleting, and reordering table cells.

Table View Cell

Defines the attributes and behavior of cells in a table view. You can set a table cell's selected-state appearance, support editing functionality, display accessory views (such as a switch control), and specify background appearance and content indentation.

Image View

Shows an image, or series of images as an animation.

Collection View

Coordinates with a data source and delegate to display a scrollable collection of cells. Each cell in a collection view is a UICollectionViewCell object.

Collection views support flow layout as well a custom layouts, and cells can be grouped into sections, and the sections and cells can optionally have supplementary views.
"""

var body: some View {
ScrollView {
VStack {
Text(content)
.lineLimit(nil)
}.frame(maxWidth: .infinity)
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How to create a multiline UITextfield?

UITextField is specifically one-line only.

Your Google search is correct, you need to use UITextView instead of UITextField for display and editing of multiline text.

In Interface Builder, add a UITextView where you want it and select the "editable" box. It will be multiline by default.

Adding unlimited lines in a Text (SwiftUI)

For wrapping Text in a Form .lineLimit(Int.max) did not work for me. It seems there needs to be some width for it to know when to wrap. I believe the official way is with .fixedSize:

Text(message)
.fixedSize(horizontal: false, vertical: true)

The documentation states:

This example shows the effect of fixedSize(horizontal:vertical:) on a text view that is wider than its parent, preserving the ideal, untruncated width of the text view.

https://developer.apple.com/documentation/swiftui/view/fixedsize(horizontal:vertical:)



Related Topics



Leave a reply



Submit