如何在SwiftUI中将NSAttributedString与ScrollView结合使用?

时间:2020-01-24 00:43:04

标签: swift uikit swiftui nsattributedstring

我已经能够通过NSAttributedString来渲染UIViewRepresentable,直到将视图包装在ScrollView中之前,效果都很好。

ScrollView视图放置在NSAttributedString内时,将停止渲染。

我尝试了一些其他方法,这些方法通过将多个NSAttributedString视图加在一起来替换Text(),以获得在ScrollView内部有效并支持斜体的格式和monospace font。不幸的是,这不适用于文本块内的links,这意味着我仍然需要NSAttributedString

import SwiftUI

struct TextWithAttributedString: UIViewRepresentable {

    var attributedString: NSAttributedString

    init(_ attributedString: NSAttributedString) {
        self.attributedString = attributedString
    }

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView(frame: .zero)
        textView.attributedText = self.attributedString
        textView.isEditable = false
        return textView
    }

    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = self.attributedString
    }
}


let exampleText = """
Fugiat id blanditiis et est culpa voluptas. Vivamus aliquet enim eu blandit blandit. Sit eget praesentium maxime sit molestiae et alias aut.
"""

struct NSAttributedStringView: View {
    var body: some View {
// Note: when uncommented, the view breaks
//    ScrollView {
        TextWithAttributedString(NSAttributedString(string: exampleText))
//    }
    }
}

struct NSAttributedStringView_Previews: PreviewProvider {
    static var previews: some View {
        NSAttributedStringView()
            .previewLayout(.sizeThatFits)
    }
}

编辑:我尝试使用设置了UITextView属性而不是text属性的包装的attributeText,但这也无法在{ {1}},因此问题似乎是ScrollView,而不是UITextView

问题是,我们如何让NSAttributedStringUITextView中工作?

2 个答案:

答案 0 :(得分:5)

原因是SwiftUI ScrollView需要定义的内容大小,但是使用的UITextView本身是UIScrollView并根据父视图中的可用空间来检测内容。因此,它发生了不确定大小的循环。

这里是解决方法的简化演示。这个想法是计算UITextView的内容大小并将其传递给SwiftUI ...

struct TextWithAttributedString: UIViewRepresentable {
    @Binding var height: CGFloat
    var attributedString: NSAttributedString

    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView(frame: .zero)
        textView.isEditable = false
        return textView
    }

    func updateUIView(_ textView: UITextView, context: Context) {
        textView.attributedText = self.attributedString

        // calculate height based on main screen, but this might be 
        // improved for more generic cases
        DispatchQueue.main.async { // << fixed 
            height = textView.sizeThatFits(UIScreen.main.bounds.size).height
        }
    }
}


struct NSAttributedStringView: View {
    @State private var textHeight: CGFloat = .zero
    var body: some View {
        ScrollView {
            TextWithAttributedString(height: $textHeight, attributedString: NSAttributedString(string: exampleText))
                .frame(height: textHeight) // << specify height explicitly !
        }
    }
}

答案 1 :(得分:1)

如果要将Asperi's TextWithAttributedString用作子视图,请替换

height = textView.sizeThatFits(UIScreen.main.bounds.size).height

作者

DispatchQueue.main.async {
    height = textView.sizeThatFits(textView.visibleSize).height
}