ios - SwiftUI TextEditor 初始内容大小错误

标签 ios swiftui swiftui-texteditor

iOS 14.4 + Xcode 12.4

我想在 iOS 上的 SwiftUI 中制作一个简单的 list ,其中每个项目的文本都是 TextEditor

enter image description here

首先,我创建基本的应用程序结构并填充一些演示内容:

import SwiftUI

@main
struct TestApp: App {
  @State var alpha = "Alpha"
  @State var bravo = "Bravo is a really long one that should wrap to multiple lines."
  @State var charlie = "Charlie"
  
  init(){
    //Remove the default background of the TextEditor/UITextView
    UITextView.appearance().backgroundColor = .clear
  }
  
  var body: some Scene {
    WindowGroup {
      ScrollView{
        VStack(spacing: 7){
          TaskView(text: $alpha)
          TaskView(text: $bravo)
          TaskView(text: $charlie)
        }
        .padding(20)
      }
      .background(Color.gray)
    }
  }
}

然后每个TaskView代表列表中的一个任务(白框):

struct TaskView:View{
  @Binding var text:String
  
  var body: some View{
    HStack(alignment:.top, spacing:8){
      Button(action: {
        print("Test")
      }){
        Circle()
          .strokeBorder(Color.gray,lineWidth: 1)
          .background(Circle().foregroundColor(Color.white))
          .frame(width:22, height: 22)
      }
      .buttonStyle(PlainButtonStyle())
      
      FieldView(name: $text)
       
    }
    .frame(maxWidth: .infinity, alignment: .leading)
    .padding(EdgeInsets(top:10, leading:10, bottom: 10, trailing: 30))
    .background(Color.white)
    .cornerRadius(5)
  }
}

最后,每个 TextEditor 都位于 FieldView 中,如下所示:

struct FieldView: View{
  @Binding var name: String
  var body: some View{
    ZStack{
      Text(name)
        .padding(EdgeInsets(top: -7, leading: -3, bottom: -5, trailing: -3))
        .opacity(0)
      TextEditor(text: $name)
        .fixedSize(horizontal: false, vertical: true)
        .padding(EdgeInsets(top: -7, leading: -3, bottom: -5, trailing: -3))
    }
  }
}

正如您在上面的屏幕截图中看到的,TextEditor 的初始高度不会自动调整大小以适应文本。但一旦我输入它,它就会适当调整大小。下面的视频显示了这一点:

enter image description here

如何使 View 具有正确的初始高度?在我输入之前,TextEditor 会垂直滚动,因此它似乎具有错误的内在内容大小。

最佳答案

注意: View 保持半透明并带有边框,以便您可以查看/调试正在发生的情况。

struct FieldView: View{
    @Binding var name: String
    @State private var textEditorHeight : CGFloat = 100
    var body: some View{
        ZStack(alignment: .topLeading) {
            Text(name)
                .background(GeometryReader {
                    Color.clear
                        .preference(key: ViewHeightKey.self,
                                           value: $0.frame(in: .local).size.height)

                })
                //.opacity(0)
                .border(Color.pink)
                .foregroundColor(Color.red)
                
            TextEditor(text: $name)
                .padding(EdgeInsets(top: -7, leading: -3, bottom: -5, trailing: -7))
                .frame(height: textEditorHeight + 12)
                .border(Color.green)
                .opacity(0.4)
        }
        .onPreferenceChange(ViewHeightKey.self) { textEditorHeight = $0 }
    }
}

struct ViewHeightKey: PreferenceKey {
    static var defaultValue: CGFloat { 0 }
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = value + nextValue()
        print("Reporting height: \(value)")
    }
}

首先,我使用 PreferenceKey 将“不可见” TextView 的高度传递到 View 层次结构。然后,我使用该值设置 TextEditor 框架的高度。

请注意, View 现在已与 topLeading 对齐 - 在最初的示例中,不可见文本居中对齐。

我不喜欢的一件事是边缘插入的使用——这些感觉就像神奇的数字(嗯,它们是......),我宁愿有一个没有它们的解决方案,仍然保留TextTextEditor 完全对齐。但是,这暂时有效。

更新,将 UIViewRepresentable 与 UITextView 结合使用

这似乎有效并避免了滚动问题:


struct TaskView:View{
    @Binding var text:String
    @State private var textHeight : CGFloat = 40
    
    var body: some View{
        HStack(alignment:.top, spacing:8){
            Button(action: {
                print("Test")
            }){
                Circle()
                    .strokeBorder(Color.gray,lineWidth: 1)
                    .background(Circle().foregroundColor(Color.white))
                    .frame(width:22, height: 22)
            }
            .buttonStyle(PlainButtonStyle())
            
            FieldView(text: $text, heightToTransmit: $textHeight)
                .frame(height: textHeight)
                .border(Color.red)
            
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(EdgeInsets(top:10, leading:10, bottom: 10, trailing: 30))
        .background(Color.white)
        .cornerRadius(5)
    }
}

struct FieldView : UIViewRepresentable {
    @Binding var text : String
    @Binding var heightToTransmit: CGFloat
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        let textView = UITextView(frame: .zero, textContainer: nil)
        textView.delegate = context.coordinator
        textView.backgroundColor = .yellow // visual debugging
        textView.isScrollEnabled = false   // causes expanding height
        context.coordinator.textView = textView
        textView.text = text
        view.addSubview(textView)

        // Auto Layout
        textView.translatesAutoresizingMaskIntoConstraints = false
        let safeArea = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            textView.topAnchor.constraint(equalTo: safeArea.topAnchor),
            textView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
            textView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor)
        ])
        
        return view
    }
    
    func updateUIView(_ view: UIView, context: Context) {
        context.coordinator.heightBinding = $heightToTransmit
        context.coordinator.textBinding = $text
        DispatchQueue.main.async {
            context.coordinator.runSizing()
        }
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    
    class Coordinator : NSObject, UITextViewDelegate {
        var textBinding : Binding<String>?
        var heightBinding : Binding<CGFloat>?
        var textView : UITextView?
        
        func runSizing() {
            guard let textView = textView else { return }
            textView.sizeToFit()
            self.textBinding?.wrappedValue = textView.text
            self.heightBinding?.wrappedValue = textView.frame.size.height
        }
        
        func textViewDidChange(_ textView: UITextView) {
            runSizing()
        }
    }
}

关于ios - SwiftUI TextEditor 初始内容大小错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66500441/

相关文章:

ios - 在 Swift 中获取两个日期(月/日/小时/分钟/秒)之间的差异

ios - SceneKit自定义几何纹理错误

ios - SwiftUI : [Unknown process name] CGAffineTransformInvert: singular matrix ERROR

swift - 如何在swiftUI中每天查看日期

ios - 如何更改 SwiftUI Divider 上方和下方的填充?

swift - 当键盘出现在 SwiftUI 中时,向上移动文本编辑器(在列表中)

ios - 每个随机数调用事件

iphone - CATransform3DTranslate和CATransform3DScale之间的区别