简体   繁体   English

如何在 SwiftUI 中使用 NSAttributedString 和 ScrollView?

[英]How to use an NSAttributedString with a ScrollView in SwiftUI?

I've been able to render NSAttributedString s via UIViewRepresentable which works great until I wrap the view in a ScrollView .我已经能够通过UIViewRepresentable渲染NSAttributedString ,这在我将视图包装在ScrollView之前效果很好。

When placed inside the ScrollView , the NSAttributedString view stops rendering.当放置在ScrollView中时, NSAttributedString视图停止渲染。

I've tried some other methods that replace an NSAttributedString with adding multiple Text() views together to get formatting which works inside the ScrollView and supports italics and monospace font .我尝试了一些其他方法,将NSAttributedString替换为将多个Text()视图一起添加以获取在ScrollView内工作并支持italicsmonospace font的格式。 Unfortunately this doesn't work for links inside text blocks, which means I still need an NSAttributedString .不幸的是,这不适用于文本块内的链接,这意味着我仍然需要一个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)
    }
}

Edit: I tried using the wrapped UITextView with the text property set instead of the attributeText property, but this also fails to render in the ScrollView , so the issue seems to be the UITextView , not the NSAttributedString .编辑:我尝试使用带有text属性集而不是attributeText属性的包装UITextView ,但这也无法在ScrollView呈现,所以问题似乎是UITextView ,而不是NSAttributedString

So the question is, how do we get the UITextView to work in a ScrollView ?所以问题是,我们如何让UITextViewScrollView中工作?

The reason is that SwiftUI ScrollView requires defined content size, but used UITextView is itself a UIScrollView and detects content based on available space in parent view.原因是 SwiftUI ScrollView需要定义内容大小,但使用的UITextView本身就是一个UIScrollView并根据父视图中的可用空间检测内容。 Thus it happens cycle of undefined sizes.因此,它发生了未定义大小的循环。

Here is a simplified demo of possible approach how to solve this.这是如何解决此问题的可能方法的简化演示。 The idea is to calculate content size of UITextView and pass it to SwiftUI...这个想法是计算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 !
        }
    }
}

backup 备份

If you want to use Asperi's TextWithAttributedString as a child view, replace如果要使用Asperi 的 TextWithAttributedString TextWithAttributedString子视图,请替换

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

by经过

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM