控制 SwiftUI 中 TextEditor 的大小

use*_*ser 20 swift swiftui

我想TextEditor在我的 ForEach 中有一些,我在 down 中制作了这个示例代码!正如您在 Image 中看到的代码和结果,它TextEditor就像一个贪婪的视图,并占用所有可用空间!至少对我来说有很多缺点!

如果我将高度限制为自定义值,那么我将失去查看 TextEditor 本身的所有字符串和字符串行的可能性,并且我必须向上或向下滚动才能看到其他行,这不是我的设计!

我的目标是占用TextEditor所需的空间,如果我输入新的字符串行,那么它的高度可以增加,或者如果我删除字符串行,它的高度可以缩小到至少 1 行!

我想知道我该怎么做?

struct ContentView: View {
    
    @StateObject var textEditorReferenceType: TextEditorReferenceType = TextEditorReferenceType()
    
    var body: some View {
        
        Text("TextEditorView").bold().padding()
        
        VStack {
            
            ForEach(textEditorReferenceType.arrayOfString.indices, id: \.self, content: { index in
                
                TextEditorView(string: $textEditorReferenceType.arrayOfString[index])

            })
            
        }
        .padding()
        
    }
}


struct TextEditorView: View {
    
    @Binding var string: String
    
    var body: some View {
        
        TextEditor(text: $string)
            .cornerRadius(10.0)
            .shadow(radius: 1.0)
        
    }
    
}

class TextEditorReferenceType: ObservableObject {
    
    @Published var arrayOfString: [String] = ["Hello, World!", "Hello, World!", "Hello, World!"]
    
}
Run Code Online (Sandbox Code Playgroud)

当前代码的结果:

在此输入图像描述

我的目标的结果:

在此输入图像描述

jn_*_*pdx 28

您可以使用PreferenceKey和 不可见的Text覆盖层来测量字符串尺寸并设置 的TextEditor框架:


struct TextEditorView: View {
    
    @Binding var string: String
    @State var textEditorHeight : CGFloat = 20
    
    var body: some View {
        
        ZStack(alignment: .leading) {
            Text(string)
                .font(.system(.body))
                .foregroundColor(.clear)
                .padding(14)
                .background(GeometryReader {
                    Color.clear.preference(key: ViewHeightKey.self,
                                           value: $0.frame(in: .local).size.height)
                })
            
            TextEditor(text: $string)
                .font(.system(.body))
                .frame(height: max(40,textEditorHeight))
                .cornerRadius(10.0)
                            .shadow(radius: 1.0)
        }.onPreferenceChange(ViewHeightKey.self) { textEditorHeight = $0 }
        
    }
    
}


struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = value + nextValue()
    }
}
Run Code Online (Sandbox Code Playgroud)

改编自我的其他答案:Mimicing behavior of iMessage with TextEditor for text input

  • 你认为苹果为什么要把 SwiftUI 做得如此危险?为什么你不能只应用“scaledToFit()”并且它就可以工作?SwiftUI 感觉就像是一个做得很糟糕的 API……它在很多方面都非常有限和糟糕,这真的让我很受伤。我不明白为什么苹果会做出这样的东西......然后把它做得这么糟糕...... (3认同)
  • 这将是一个很好的补充,但请记住,它是一个相对年轻的框架。不过,这可能不是进行价值判断的正确论坛。 (2认同)

wil*_*ard 25

iOS 16 - 原生 SwiftUI

在 iOS 16 中,现在可以通过textField添加axis: .vertical.lineLimit()

linelimit 定义文本字段扩展之前的行数。添加开始范围以定义文本字段内的行范围将开始和停止延伸。

在此输入图像描述

WWDC22 会议“SwiftUI 的新增功能”17:10 左右

  • 这不适用于文本编辑器,它允许换行符等 (4认同)
  • 这个问题是关于“TextEditor”,而不是“TextField” (3认同)

el3*_*oot 7

添加.fixedSize(horizontal: false, vertical: true)最小高度为我解决了这个问题。

例子 :

TextEditor(text: $myBinding)
           .frame(minHeight: 50)
           .fixedSize(horizontal: false, vertical: true)
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。它很简单并且运行完美。 (2认同)

Mar*_*eIV 7

纯粹而简单的 SwiftUI 方法(*...实际上使用TextEditor,而不是TextField!)

与这里的一些答案相反,正如其他人指出的那样,OP 特别询问了TextEditor,而不是TextFieldTextEditor如果您想支持在文本输入期间显式添加换行符,而不仅仅是换行以跨越多行,则需要。

实现这一目标的最简单的解决方案TextEditor需要两件事:

  1. 使用.fixedSize(horizontal: false, vertical: true)on TextEditor (规定绝对最小高度)
  2. 在同一个容器中拥有另一个具有更高布局优先级的贪婪控件(将其平滑到那个高度!)

这是一个例子,我在Button自动增长的正下方有一个TextEditor. 我使用Color.clear无限框架使其贪婪,然后.layoutPriority(1)使其压倒控件的贪婪TextEditor。上面fixedSize写着TextEditor“哟,你不能折叠超过我的文字,布拉!!”,因此这就是它的作用。如果没有fixedSize,它会塌陷到零高度。

struct TestApp: App {

    static private let initialText = """
        He: Tell me a joke!

        She: Ok... what do you call two crows sitting on a branch?

        He: I dunno, what?

        She: Attempted murder!
        """

    @State private var text: String = Self.initialText

    var body: some Scene {
        WindowGroup {

            VStack(alignment: .leading) {

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

                Button("Test Me") {
                    text = Self.initialText
                }

                Color.clear
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .layoutPriority(1)
            }
            .padding()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

旁注:如上所述,layoutPriority仅影响当前容器中的布局,因此请确保它所应用的贪婪控件是您的直接兄弟TextEditor(即它们具有相同/直接父级)或位于TextEditor布局的更下方,而不是多于。

这也意味着,如果您将下面的控件嵌套VStack在另一个VStack带有其他控件的控件中,则该外部控件的子控件VStack(包括您的内部控件VStack)将再次在其中均匀分布(当然,除非您layoutPriority也应用到这些控件中的任何一个。)

如上所述,我在那里使用了显式贪婪间隔控件:Color.clear与框架一起使用。但是,从技术上讲,这是不需要的,因为您可以直接向按钮添加框架来实现相同的效果。您只需要还指定适当的alignment值来说明您希望该按钮在框架的贪婪区域中结束的位置。在这里,.topLeading按钮最终位于TextEditor左侧的正下方。如果不添加alignment,按钮将最终位于贪婪区域的中间,这.center是默认对齐方式。

也就是说,我个人更喜欢显式布局,因此间隔符是我的选择,但其他人可能更喜欢不需要专用控件的简单性。*

// `alignment: topLeading` in the frame puts the button in the top-left of the 'greedy' area that the frame creates
Button("Test Me") {
    text = Self.initialText
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.layoutPriority(1)

Run Code Online (Sandbox Code Playgroud)

这是结果的示例......

在此输入图像描述
在此输入图像描述
在此输入图像描述