UITextView - 根据 SwiftUI 中的内容调整大小 [英] UITextView - adjust size based on the content in SwiftUI

查看:19
本文介绍了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
            }
        }
    }
}

关键是desiredHeight的绑定,它是在updateUIView中使用UIView的sizeThatFits方法计算出来的.请注意,这包含在 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 的一组所需高度值.TextView 的高度在 TextView 上的框架修改器中设置.在这个例子中,我还设置了一个最小高度,为初始编辑留出一些空间.框架高度仅在状态值之一(在本例中为注释)更改时更新.这里的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.

我尝试更新协调器中 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 中的内容调整大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆