SwiftUI:如何将 TextField 高度动态更改为阈值,然后允许滚动?

Posted

技术标签:

【中文标题】SwiftUI:如何将 TextField 高度动态更改为阈值,然后允许滚动?【英文标题】:SwiftUI: How to change the TextField height dynamicly to a threshold and then allow scrolling? 【发布时间】:2021-09-04 17:42:48 【问题描述】:

LineLimit 不起作用:

TextField("Text", text: $model.commitDescr)
     .multilineTextAlignment(.leading)
     .lineLimit(5)

LineLimit 没有任何改变 - 我可以显示 10-20-30 行。

与 scrollView 类似的方式总是有 maxHeight(400) 而不是动态高度:

ScrollView() 
     TextField("Text", text: $model.commitDescr)
         .multilineTextAlignment(.leading)
         .lineLimit(5)

.frame(minHeight: 20, maxHeight: 400)

以下也不能正常工作:

TextField("Text", text: $model.commitDescr)
     .multilineTextAlignment(.leading)
     .lineLimit(5)
     .frame(minHeight: 20, maxHeight: 400)

【问题讨论】:

【参考方案1】:

这是解决此问题的一种方法:

struct ContentView: View 
    
    @State private var commitDescr: String = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

    var body: some View 

        TextEditorView(string: $commitDescr)
  
    
    



struct TextEditorView: View 
    
    @Binding var string: String
    @State private var textEditorHeight : CGFloat = CGFloat()

    var body: some View 
        
        ZStack(alignment: .leading) 

            Text(string)
                .lineLimit(5)
                .foregroundColor(.clear)
                .padding(.top, 5.0)
                .padding(.bottom, 7.0)
                .background(GeometryReader 
                    Color.clear.preference(key: ViewHeightKey.self,
                                           value: $0.frame(in: .local).size.height)
                )
            
            TextEditor(text: $string)
                .frame(height: textEditorHeight)
               
        
        .onPreferenceChange(ViewHeightKey.self)  textEditorHeight = $0 
        
    
    



struct ViewHeightKey: PreferenceKey 
    static var defaultValue: CGFloat  0 
    static func reduce(value: inout Value, nextValue: () -> Value) 
        value = value + nextValue()
    

【讨论】:

我发现它是一个相当麻烦的解决方案,它可以在 Swift 中本地完成但仍然有效。 @Björn 这是原生 Swift...?【参考方案2】:

@available(iOS 14.0, macOS 11.0, *)

使用 TextEditor 代替 TextField

TextEditor(text: $text)
      .fixedSize(horizontal: false, vertical: true)
      .multilineTextAlignment(.leading)
 

使用示例:

 ScrollView 
    VStack
        TextEditor(text: $text)
            .frame(minHeight: 40, alignment: .leading)
            .frame(maxHeight: MAX_HEIGHT)
            .cornerRadius(10, antialiased: true)
            .foregroundColor(.black)
            .font(.body)
            .padding()
            .fixedSize(horizontal: false, vertical: true)
            .multilineTextAlignment(.leading)
     
     .background(Color.red)

 

【讨论】:

我不需要固定大小。我需要具有 maxHeight 阈值的动态尺寸! fixedSized 行用于告诉 TextEditor 动态更改 heightmaintaining 它的 width 我已经检查了你的代码。这没用。 i.stack.imgur.com/v7NJ0.png 另一个样本:i.stack.imgur.com/YiFDR.jpg 是的。但是 SwiftPunk 的代码有更好的逻辑。但即便如此 - 您的代码也几乎可以按预期工作。非常感谢您的帮助!【参考方案3】:

固定高度,允许滚动

使用TextEditor,而不是TextField。然后,您可以将 maxHeight 设置为您想要的任何值。

struct ContentView: View 
    @State private var text = ""

    var body: some View 
        TextEditor(text: $text)
            .frame(maxHeight: 300)
    

结果:

固定行数,不滚动

如果您想限制使用TextEditor 可以显示的行数,可以使用SwiftUI-Introspect 来做到这一点。

例子:

struct ContentView: View 
    @State private var text = ""

    var body: some View 
        TextEditor(text: $text)
            .introspectTextView  textView in
                textView.textContainer.maximumNumberOfLines = 5
                textView.textContainer.lineBreakMode = .byTruncatingTail
            
    

结果:

【讨论】:

我想要一些 MaxHeight 限制。并且在达到限制的情况下,需要显示滚动来滚动文本。 @Andrew 第一个例子就是这样做的。只有几行,它不会滚动。当您达到最大高度时,您可以看到滚动然后启用。您的意思是您希望TextEditor 高度从小且居中开始,然后扩展到最大高度? TextEditor 的起始高度必须为 1 行。如果$text 有 5 行 - 必须是 5 行高度。如果 $text 有 6 行或更多 - TextEditor 必须有 5 行的高度 + 显示滚动。【参考方案4】:

通常,TextField 仅用于一行文本。

对于多行输入,TextEditor 可能是原生 SwiftUI 中更好的解决方案。您也可以为此设置.frame(maxHeight: XX, alignment: .leading)

为了清楚起见,下面的示例将确保编辑器始终保持 200 点高,即使没有输入文本:

TextEditor(text: $variable)
     .frame(maxHeight: 200, alignment: .center)

根据提问者的要求,提供一个额外的示例,其中编辑器很小(50 点高)并且仅在输入文本时扩展至 maxHeight:

TextEditor(text: $variable)
     .frame(minHeight: 50, maxHeight: 200, alignment: .center)

概念证明:https://imgur.com/a/kmu1gIe

【讨论】:

框架不起作用。我在我的问题中写了这个。框架在 TextField 包装的情况下不起作用,而不是在 ScrollView 包装的情况下。请在你身边尝试一下。 您在问题中使用了 TextField 。我写了关于 .frame 属性在我这边工作得很好的 TextEditor。请在你身边试试。下次请仔细阅读答案后再发表评论。 您的代码没有正确的行为。证明:imgur.com/a/6oJVdUd SwiftPunk 的代码符合预期。证明:i.imgur.com/ES0IsgI.png 啊,您希望您的编辑器在没有输入文本时尽可能小吗?也许您应该将其添加到您的问题中。正如现在阅读您的问题一样,它声明您想给它一个 maxHeight 并且该字段不会无限增长。只需将 minHeight 添加到您的框架中,如下所示:.frame(minHeight: 50, maxHeight: 200, alignment: .center)。不要告诉我它不起作用,因为它起作用了。祝你好运! @Björn:“TextEditor 可能是原生 SwiftUI 中更好的解决方案”,你确定 TextEditor 是原生的 SwiftUI 还是来自 UIKit?

以上是关于SwiftUI:如何将 TextField 高度动态更改为阈值,然后允许滚动?的主要内容,如果未能解决你的问题,请参考以下文章

如何将可观察对象发布的整数变量绑定到 SwiftUI 中的 textField?

如何防止 TextField 在 SwiftUI 列表中消失?

SwiftUI:如何使 TextField 适合多行内容?

SwiftUI:如何使 TextField 适合多行内容?

SwiftUI - 如何在 TextField 上设置最大数量?

SwiftUI TextField 数字输入