如何在SwiftUI中创建多行文本框?

195

我一直在尝试在SwiftUI中创建一个多行文本字段,但我无法弄清楚如何实现。

这是我目前的代码:

我一直在尝试在SwiftUI中创建一个多行TextField,但是我无法找到方法。

这是我当前的代码:

struct EditorTextView : View {
    @Binding var text: String
    
    var body: some View {
        TextField($text)
            .lineLimit(4)
            .multilineTextAlignment(.leading)
            .frame(minWidth: 100, maxWidth: 200, minHeight: 100, maxHeight: .infinity, alignment: .topLeading)
    }
}

#if DEBUG
let sampleText = """
Very long line 1
Very long line 2
Very long line 3
Very long line 4
"""

struct EditorTextView_Previews : PreviewProvider {
    static var previews: some View {
        EditorTextView(text: .constant(sampleText))
            .previewLayout(.fixed(width: 200, height: 200))
    }
}
#endif

但这是输出结果:

enter image description here


2
我刚刚尝试在Xcode版本11.0(11A419c)中使用SwiftUI创建多行文本字段,并使用lineLimit()方法,但仍然无法正常工作。我无法相信苹果公司还没有修复这个问题。多行文本字段在移动应用程序中非常常见。 - e987
17个回答

4

MacOS 实现

struct MultilineTextField: NSViewRepresentable {
    
    typealias NSViewType = NSTextView
    private let textView = NSTextView()
    @Binding var text: String
    
    func makeNSView(context: Context) -> NSTextView {
        textView.delegate = context.coordinator
        return textView
    }
    func updateNSView(_ nsView: NSTextView, context: Context) {
        nsView.string = text
    }
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    class Coordinator: NSObject, NSTextViewDelegate {
        let parent: MultilineTextField
        init(_ textView: MultilineTextField) {
            parent = textView
        }
        func textDidChange(_ notification: Notification) {
            guard let textView = notification.object as? NSTextView else { return }
            self.parent.text = textView.string
        }
    }
}

以及如何使用

struct ContentView: View {

    @State var someString = ""

    var body: some View {
         MultilineTextField(text: $someString)
    }
}

4

适用于 Xcode 12iOS14,非常容易。

import SwiftUI

struct ContentView: View {
    
    @State private var text = "Hello world"
    
    var body: some View {
        TextEditor(text: $text)
    }
}

4
这是只有在使用iOS14系统时才会出现的情况,如果用户仍在使用iOS13系统呢? - Di Nerd Apps
同时,TextEditor 缺少许多功能,而它实现的一些功能也存在问题。 - jalone

3

根据Asperi的回答,这是我想出来的解决方案。这个解决方案不需要计算和传播大小。它使用TextView内部的contentSizeintrinsicContentSize

resizable text view

struct TextView: UIViewRepresentable {
    @Binding var text: String
    
    func makeUIView(context: UIViewRepresentableContext<TextView>) -> UITextView {
        let textView = UIKitTextView()
        
        textView.delegate = context.coordinator
        
        return textView
    }
    
    func updateUIView(_ textView: UITextView, context: UIViewRepresentableContext<TextView>) {
        if textView.text != self.text {
            textView.text = self.text
        }
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator(text: $text)
    }
    
    final private class UIKitTextView: UITextView {
        override var contentSize: CGSize {
            didSet {
                invalidateIntrinsicContentSize()
            }
        }
        
        override var intrinsicContentSize: CGSize {
            // Or use e.g. `min(contentSize.height, 150)` if you want to restrict max height
            CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
        }
    }
    
    final class Coordinator: NSObject, UITextViewDelegate {
        var text: Binding<String>
        
        init(text: Binding<String>) {
            self.text = text
        }
        
        func textViewDidChange(_ textView: UITextView) {
            text.wrappedValue = textView.text
        }
    }
}

2
你的解决方案对于一行(短)的“text”内容运行良好。但是,如果你使用非常长的“text”内容初始化TextView,则无法正确计算contentSize.hight。这个初始的多行内容显示在一个高度不足的TextView中。只有在通过添加新行编辑文本内容后,大小才会重新计算,TextView才能正确地适应其高度。 - J.E.K

2

我使用文本编辑器

TextEditor(text: $text)
    .multilineTextAlignment(.leading)
    .cornerRadius(25)
    .font(Font.custom("AvenirNext-Regular", size: 20, relativeTo: .body))
    //.autocapitalization(.words)
    .disableAutocorrection(true)
    .border(Color.gray, width: 3)
    .padding([.leading, .bottom, .trailing])

2
你可以只使用TextEditor(text: $text),然后添加任何修饰符,如高度。

0

我想分享我的代码,因为其他答案没有正确使用Coordinator

struct UITextViewTest: View {
    @State var text = "Hello, World!"
    var body: some View {
        VStack {
            TextField("", text: $text)
            MultilineTextField(text: $text)
        }
    }
}


struct MultilineTextField: UIViewRepresentable {
    @Binding var text: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    func makeUIView(context: Context) -> UITextView {
        context.coordinator.textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        // update in case the text value has changed, we assume the UIView checks if the value is different before doing any actual work.
        // fortunately UITextView doesn't call its delegate when setting this property (in case of MKMapView, we would need to set our did change closures to nil to prevent infinite loop).
        uiView.text = text

        // since the binding passed in may have changed we need to give a new closure to the coordinator.
        context.coordinator.textDidChange = { newText in
            text = newText
        }
    }
    
    class Coordinator: NSObject, UITextViewDelegate {
        lazy var textView: UITextView = {
            let textView = UITextView()
            textView.font = .preferredFont(forTextStyle: .body)
            textView.delegate = self
            return textView
        }()
        
        var textDidChange: ((String) -> Void)?
        
        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            return true
        }
        
        func textViewDidChange(_ textView: UITextView) {
            textDidChange?(textView.text)
        }
    }
}

-1
// You can use the .multiLineTextAlignment modifier

TextField("Random text", text: $text)
    .multiLineTextAlignment(.leading)

// This aligns the text to the left
// There are more properties beside '.leading', more can be found at the source

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接