如何在 SwiftUI 中使用 NSAttributedString 和 ScrollView?

Posted

技术标签:

【中文标题】如何在 SwiftUI 中使用 NSAttributedString 和 ScrollView?【英文标题】:How to use an NSAttributedString with a ScrollView in SwiftUI? 【发布时间】:2020-01-24 00:43:04 【问题描述】:

我已经能够通过UIViewRepresentable 渲染NSAttributedStrings,在我将视图包裹在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)
    

编辑:我尝试使用包装的UITextViewtext 属性集而不是attributeText 属性,但这也无法在ScrollView 中呈现,所以问题似乎是UITextView,而不是NSAttributedString

所以问题是,我们如何让UITextViewScrollView 中工作?

【问题讨论】:

【参考方案1】:

原因是 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 !
        
    

【讨论】:

效果很好!我需要的一个小补充是将height 赋值包装在DispatchQueue.main.async 调用中,否则我会收到关于在渲染中设置状态的SwiftUI 警告,结果视图为空。【参考方案2】:

如果你想使用Asperi'sTextWithAttributedString作为子视图,替换

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

通过

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

【讨论】:

以上是关于如何在 SwiftUI 中使用 NSAttributedString 和 ScrollView?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 UIkit 中添加 SwiftUI 对象

如何在 SwiftUI 中使用 SFSafariViewController?

如何在 swiftUI 视图中使用按钮? [关闭]

如何在 SwiftUI 中使用 .fileimporter 保存音频文件并检索文件并在 SwiftUI 列表中显示它们的文件名?

如何在 Xcode Playground 中使用 SwiftUI?

如何在 SwiftUI 中使用渐变填充形状