How to Create a Multiline Textfield in Swiftui? Like the Notes App

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

How do I create a multiline Textfiled in SwiftUI?

Here is a 90% solution. It deletes further text after the 3rd line break, but it cannot detect line wraps in long texts.

struct ContentView: View {

@State private var notes = ""

var body: some View {

VStack (alignment: .leading) {
Text("Notes")
.fontWeight(.bold)

CustomTextEditor("Enter your note", text: $notes)
}
.padding()

}
}

struct CustomTextEditor: View {

init(_ prompt: LocalizedStringKey, text: Binding<String>) {
self.prompt = prompt
self._text = Binding(projectedValue: text)
}
let prompt: LocalizedStringKey
@Binding var text: String

var body: some View {
ZStack(alignment: .topLeading ) {

Text(prompt)
.foregroundColor(text == "" ? .secondary : .clear)
.padding(EdgeInsets(top: 7, leading: 3, bottom: 0, trailing: 0))

TextEditor(text: $text)

// deleting everything after the 3rd linebreak
.onChange(of: text) { _ in
let stringArray = text.map { $0 }
let pos = stringArray.indices.filter { stringArray[$0] == "\n"}
if pos.count > 2 {
text = String(stringArray.prefix(upTo: pos[2]))
}
}
}
.frame(height: 82)
.padding(10)
.background(Color(.systemGray5))
.cornerRadius(20)

// getting rid of TextEditor standard background
.onAppear {
UITextView.appearance().backgroundColor = .clear
}
.onDisappear {
UITextView.appearance().backgroundColor = .systemGray5
}
}
}

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.

Rich TextView in SwiftUI

There is a well documented library that for SwiftUI text editor. it supports iOS 13.0+ and macOS 10.15+, here is the link for it!
HighlightedTextEditor

It's also very simple to use.

SwiftUI Wrapper for UITextView not updating ObservedObject

Your multiline text view needs a coordinator to observe the text updates from UITextView

struct MultilineTextView: UIViewRepresentable {
@ObservedObject var myOText: MyOText

func makeUIView(context: Context) -> UITextView {
let view = UITextView()
view.isScrollEnabled = true
view.isEditable = true
view.isUserInteractionEnabled = true
view.textAlignment = .center
view.font = UIFont(name: "Times New Roman", size: 20)
view.delegate = context.coordinator
return view
}

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

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

class Coordinator: NSObject, UITextViewDelegate {
var control: MultilineTextView

init(_ control: MultilineTextView) {
self.control = control
}

func textViewDidChange(_ textView: UITextView) {
control.myOText.inTheCourse = textView.text
}
}
}


Related Topics



Leave a reply



Submit