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

时间:2019-11-02 10:27:18

标签: ios swift uitextview 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
        }
    }
}

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

1 个答案:

答案 0 :(得分:0)

我能够通过在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
            }
        }
    }
}

键是所需的高度的绑定,该绑定是使用UIView sizeThatFits方法在updateUIView中计算的。请注意,这被包装在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))
            }
        }
    }
}

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

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