根据SwiftUI中的内容调整大小

根据SwiftUI中的内容调整大小

本文介绍了UITextView-根据SwiftUI中的内容调整大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图弄清楚如何使UITextView的大小取决于它在SwiftUI中的内容.我将UITextView包裹在 UIViewRepresentable 中,如下所示:

I am trying to figure out how to make UITextView size dependent on it's contents in SwiftUI. I wrapped the UITextView in UIViewRepresentable as following:

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的内容大小更改其帧大小,但没有任何效果.我想在这个阶段,视图甚至没有布局,并且其框架在其他地方被覆盖.如果有人能指出我正确的方向,我将不胜感激.

Further, I tried to change the frame size in updateUIView based on it's content size, but it did not have any effect. I suppose that at this stage, the view is not even layout and its frame is being overridden somewhere else. I would really appreciate if someone could point me in a correct direction.

推荐答案

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

I was able to get this to work by binding a variable in the TextView that is used by the encapsulating view to set the frame height. Here is the minimal TextView implementation:

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在视图更新期间修改状态"错误.

The key is the binding of desiredHeight, which is computed in updateUIView using the UIView sizeThatFits method. Note that this is wrapped in a DispatchQueue.main.async block to avoid the SwiftUI "Modifying state during view update" error.

我现在可以在ContentView中使用此视图:

I can now use this view in my 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的实现中,只有在文本视图上的编辑结束时才会发生这种情况.

Here I have a couple of notes in a String array, along with an array of desiredHeight values to bind to the TextView. The height of the TextView is set in the frame modifier on the TextView. In this example, I also set a minimum height to give some space for the intial edit. The frame height only updates when one of the State values (in this case the notes) changes. In the implementation of the TextView here, this only occurs when editing ends on the text view.

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

I tried updating the text in the textViewDidChange delegate function in the Coordinator. This updates the frame height as you add text, but makes it so that you can only ever type text at the end of the TextView since updating the view resets the insertion point to the end!

这篇关于UITextView-根据SwiftUI中的内容调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-04 21:30