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
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
}
}
}
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
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)
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.
Handling shift+enter for newline, enter to send in TextEditor SwiftUI
The correct shortcut for new line is actually option+return and TextField
supports it, e.g.
TextField("type something...", text: $text, onEditingChanged: { _ in
print("changed")
}, onCommit: {
print("commit")
})
How to use bold and normal text in same line in SwiftUI
Here a simple example for you, with my example code you can code for iOS 13 or 14 as well:
struct ContentView: View {
var body: some View {
Text("Hello, World!") + Text(" Hello, World!").bold() + Text(" Hello, World!").italic()
}
}
output:
SwiftUI TextField or UITextField with tag in MacOs
Anyways, had a bit of play with this, and found a way (thanks to this StackOverflow question
Basically, I have a TextField
that has a ViewModifier
attached to it. Looking like this:
TextField(
"Placeholder",
text: .constant("Text")
)
.modifier(TextFieldModifier(text: "TextOverlayWithExtra"))
And the modifier:
struct TextFieldModifier: ViewModifier {
var text: String
func body(content: Content) -> some View {
content
// Clear color for the TextField
.foregroundColor(.clear)
// Overlay with text and extra
.overlay(
HStack(spacing: 0.0) {
// This Swift View splits the text and adds what I need
TextFieldHighlightedVariables(text)
Spacer()
}
.padding(.top, 2)
.padding(.leading, 4)
,
alignment: .topLeading
)
}
}
How can I put multiple lines (breaking lines) in a TextField and Text components on Swift UI?
You can use .lineLimit(nil)
to your Text views. It supports multi-line text and allocates space as needed.
struct StackOverFlow : View {
var body: some View {
Text("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.")
.lineLimit(nil)
}
}
Without linelimit
With linelimit
SwiftUI: Different Widths for Multiline Text Views in Boxes
There are two issues at play here:
You've got two modifiers swapped (
.frame(...)
and.unoBoxRoundPad()
); you want the roundedness to apply to the entire stretched thing. By putting.unoBoxRoundPad()
first, you're saying "pad this thing" and then "place that rounded thing inside an infinitely-wide box"; you want the reverse: your thing should be placed inside an infinitely-wide box, and it's the infinitely wide box that should have the rounded corners and padding.You need to specify an
alignment:
when using that.frame()
modifier; when the inner view is placed inside an infinitely-wide box, it's going to default to being centered vertically and horizontally inside it. Based on your screenshots, you probably want to use.topLeading
so that the content ("Support", "Help", etc) start in the top-left corner (or top-right in RTL languages).
Related Topics
Programmatically Disabling Screenshot in App
Why Swift Closure Not Capture Self
Lazy Property Initialization in Swift
Add Skreferencenode/Skscene to Another Skscene in Spritekit
Fatal Error: Array Index Out of Range in Swift Xcode6
How to Request a Desktop Version of a Webpage Using Uiwebview in Swift 3.0
Declaring and Using Custom Attributes in Swift
Swift 2: !, ? -" Value of Optional Type "..." Not Unwrapped"
How to Get Iobluetoothdevice's Battery Level, Using Swift and Appkit (Xcode for MACos)
iOS 13: Threading Violation: Expected the Main Thread
Mutating Function Inside Class
Show Nsmenu Only on Nsstatusbarbutton Right Click
Why Does Swiftui Uihostingcontroller Have Extra Spacing
Using Compiler Variables in Swift
Split Uint32 into [Uint8] in Swift
No Value Associated with Key Codingkeys While Trying to Get Data from Github API in Xcode App