我需要能够使用 SwiftUI 文本视图显示 HTML 标记文本。 根据教程,我使用
UIViewRepresentable
找到了这个 NSAttributedString
解决方案。
struct HTMLText: UIViewRepresentable {
var attributedText: NSAttributedString
init(text: String) {
self.attributedText = text.htmlToAttributedString
}
func makeUIView(context: Context) -> UITextView {
let uiTextView = UITextView()
uiTextView.backgroundColor = .red
uiTextView.textContainerInset = .zero
uiTextView.isEditable = false
uiTextView.isScrollEnabled = false
uiTextView.textContainer.lineFragmentPadding = 0
return uiTextView
}
func updateUIView(_ uiTextView: UITextView, context: Context) {
uiTextView.attributedText = attributedText
}
}
extension String {
var htmlToAttributedString: NSAttributedString {
guard let data = data(using: .utf8) else {
return NSAttributedString(string: self) }
do {
return try NSAttributedString(
data: data,
options: [.documentType: NSAttributedString.DocumentType.html,
.characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)
} catch {
return NSAttributedString(string: self)
}
}
}
虽然这对于简单视图效果很好,但我在将其合并到我的应用程序中时遇到了困难。与
NavigationStack
有关的某些事情会导致属性图中出现循环:=== AttributeGraph: cycle detected through attribute 96280 ===
。在我的应用程序中,记录了几十条这样的消息,并且没有显示文本字段。更致命的是,直接用作父视图似乎会导致 GeometryReader
,日志记录为 SIGABRT
。此复制会导致循环,尽管只有少数并且显示文本:
precondition failure: setting value during update: 768
这会导致完全崩溃。
struct ContentView: View {
@State var showView: Bool = false
var body: some View {
NavigationStack {
Button(action: {
showView.toggle()
}, label: {
Text("Show view")
})
.navigationDestination(isPresented: $showView, destination: {
ScrollableView() // Putting the scroll view here directly seems to be fine
})
}
}
}
struct ScrollableView: View {
var body: some View {
ScrollView {
HTMLText(text: "hello <em> world </em>")
}
}
}
出问题的确切点是尝试将字符串数据编码到属性字符串中,即使它被包装在 try 块中,但这里似乎根本没有处理错误。我无法找到其他方式来显示 HTML 文本,我还有其他选择吗?我可以采取哪些步骤来尝试调试属性图?
struct Crash: View {
var body: some View {
GeometryReader { _ in
HTMLText(text: "hello <em> world </em>")
}
}
}
时,你可以看到内部函数
htmlToAttributedString
调用。根据它的名字,我认为它的工作是强制 UI 立即重新加载。这将导致 _UIHostingView.requestImmediateUpdate
,如该日志记录 cycle
所示。正如您所看到的,将调试断点放在 AttributeGraph: cycle detected through attribute
的 init
中:HTMLText
将导致另一个
ScrollableView.body.getter
。ScrollableView.body.getter
放入
htmlToAttributedString
:DispatchQueue.main.async