UITextView - 根据 SwiftUI 中的内容调整大小

use*_*787 9 uitextview ios swift swiftui

我想弄清楚如何使 UITextView 大小依赖于它在 SwiftUI 中的内容。我将 UITextView 包装UIViewRepresentable如下:

struct TextView: UIViewRepresentable {

    @Binding var showActionSheet: Bool

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextView {

        let uiTextView = UITextView()
        uiTextView.delegate = context.coordinator

        uiTextView.font = UIFont(name: "HelveticaNeue", size: 15)
        uiTextView.isScrollEnabled = true
        uiTextView.isEditable = true
        uiTextView.isUserInteractionEnabled = true
        uiTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
        uiTextView.isEditable = false

        return uiTextView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = prepareText(question: question)

        var frame = uiView.frame
        frame.size.height = uiView.contentSize.height
        uiView.frame = frame
    }

    func prepareText(text: string) -> NSMutableAttributedString {
        ...................
        return attributedText
    }

    class Coordinator : NSObject, UITextViewDelegate {

        var parent: TextView

        init(_ view: TextView) {
            self.parent = view
        }

        func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
            parent.showActionSheet = true
            return false
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,我尝试根据内容大小更改 updateUIView 中的帧大小,但没有任何效果。我想在这个阶段,视图甚至没有布局,它的框架在其他地方被覆盖。如果有人能指出我正确的方向,我将不胜感激。

小智 9

我能够通过在封装视图使用的 TextView 中绑定一个变量来设置框架高度来使其工作。这是最小的 TextView 实现:

struct TextView: UIViewRepresentable {

    @Binding var text: String?
    @Binding var attributedText: NSAttributedString?
    @Binding var desiredHeight: CGFloat

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextView {

        let uiTextView = UITextView()
        uiTextView.delegate = context.coordinator

        // Configure text view as desired...
        uiTextView.font = UIFont(name: "HelveticaNeue", size: 15)

        return uiTextView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        if self.attributedText != nil {
            uiView.attributedText = self.attributedText
        } else {
            uiView.text = self.attributedText
        }

        // Compute the desired height for the content
        let fixedWidth = uiView.frame.size.width
        let newSize = uiView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

        DispatchQueue.main.async {
            self.desiredHeight = newSize.height
        }
    }

    class Coordinator : NSObject, UITextViewDelegate {

        var parent: TextView

        init(_ view: TextView) {
            self.parent = view
        }

        func textViewDidEndEditing(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.text = textView.text
                self.parent.attributedText = textView.attributedText
            }
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

关键是所需高度的绑定,它是在 updateUIView 中使用 UIView sizeThatFits 方法计算的。请注意,这包含在 DispatchQueue.main.async 块中以避免 SwiftUI“在视图更新期间修改状态”错误。

我现在可以在我的 ContentView 中使用这个视图:

struct ContentView: View {

    @State private var notes: [String?] = [
        "This is a short Note",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet consectetur. Morbi enim nunc faucibus a. Nunc pulvinar sapien et ligula ullamcorper malesuada proin libero.",
    ]

    @State private var desiredHeight: [CGFloat] = [0, 0]

    var body: some View {
        List {
            ForEach(0..<notes.count, id: \.self) { index in
                TextView(
                    desiredHeight: self.$desiredHeight[index],
                    text: self.$notes[index],
                    attributedText: .constant(nil)
                )
                .frame(height: max(self.desiredHeight[index], 100))
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我在 String 数组中有几个注释,以及要绑定到 TextView 的一组所需高度值。TextView 的高度在 TextView 上的框架修改器中设置。在这个例子中,我还设置了一个最小高度,为初始编辑留出一些空间。框架高度仅在状态值之一(在本例中为注释)更改时更新。在此处的 TextView 的实现中,这仅在文本视图上结束编辑时发生。

我尝试更新协调器中 textViewDidChange 委托函数中的文本。这会在您添加文本时更新框架高度,但使您只能在 TextView 的末尾键入文本,因为更新视图会将插入点重置到末尾!

  • 如果你的 UITextView 是可滚动的,那么这工作正常。但是,如果您想要带有属性文本的静态多行文本视图,则它不起作用。 (2认同)