首页 > 解决方案 > 尝试将 HTML 转换为属性字符串,但遇到运行时崩溃或长期警告

问题描述

目标:将传入的 HMTL 翻译成 AttributedString。
两者都通过 UIViewRepresentable 视图: 两种方法:

  1. 通过 UITextView;或者
  2. 通过 WebKit。

#1:通过 UIView
我最初选择这条路线是为了避免 Webkit 的麻烦。这是代码:

import SwiftUI

struct VaccineDetailView: View {
    @State var htmlText = htmlString

    @State var text = htmlString
    var body: some View {
        TextView(htmlText: $text)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}

// =====================================================================================================

struct TextView: UIViewRepresentable {
    @Binding var htmlText: String

    func makeUIView(context: Context) -> UITextView {
        UITextView()
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        let htmlData = NSString(string: htmlText).data(using: String.Encoding.unicode.rawValue)
        let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
        do {
            let attributedString = try NSAttributedString(data: htmlData!, options: options, documentAttributes: nil)
            uiView.attributedText = attributedString
        } catch {
             print("Unexpected error: \(error).")

        }
    }
}

但是,当我在模拟器中关闭并重新打开应用程序时遇到了以下运行时错误:

在此处输入图像描述

这是控制台消息的一部分:

...[plugin] AddInstanceForFactory: No factory registered for id <CFUUID 0x600001c81300> F8BB1C28-BAE8-11D6-9C31-00039315CD46
=== AttributeGraph: cycle detected through attribute 93896 ===
=== AttributeGraph: cycle detected through attribute 93432 ===
=== AttributeGraph: cycle detected through attribute 93432 ===
*** Assertion failure in void _UIApplicationDrainManagedAutoreleasePool()(), UIApplication+AutoreleasePool.m:171
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unexpected start state'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff2041faf6 __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x00007fff20177e78 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff2041f91f +[NSException raise:format:] + 0
    3   Foundation                          0x00007fff2076d633 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 166
...
...

2) 通过 WebKit:
我没有使用 WebKit 导致运行时崩溃:

import SwiftUI
import WebKit

struct VaccineDetailView: View {
    @State var htmlText = htmlString

    @State var text = htmlString
    var body: some View {
        TextView(htmlText: $text)
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}

// =====================================================================================================

struct TextView: UIViewRepresentable {
    @Binding var htmlText: String

    func makeUIView(context: Context) -> UITextView {
        UITextView()
    }

    // Converting HTML to String:
    func updateUIView(_ uiView: UITextView, context: Context) {
        NSAttributedString.loadFromHTML(string: htmlString) { attr, _, _ in
            uiView.attributedText = attr
        }
    }
}

但是我确实得到以下信息:

Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
Could not signal service com.apple.WebKit.Networking: 113: Could not find specified service

选项 #2 似乎是更安全的方法,但我必须处理 WebKit 可以找到指定服务的 *repeated* 警告。
注意:我只是使用 WebKit 来翻译 HTML。我在这里没有使用 URLSession(通过在其他地方发布/订阅接受)。这可能是问题所在……WebKit 不理解“不需要服务”。

我不明白为什么选项 #1 会崩溃。

问题:

  1. 为什么选项 #1 在重新打开应用程序时总是崩溃?
  2. 我如何告诉 WebKit 不要担心任何“指定服务”?
    2a)我可以将其合并到与 URLSession.publisher 关联的订阅者语法中...以避免警告吗?如何?

这是翻译后的输出:

在此处输入图像描述

标签: htmlswiftui

解决方案


我仅在 ios14 中遇到与 TTTAttributeLabel 相同的崩溃。事实证明 DispatchQueue.main.async 解决了崩溃。

struct TextView: UIViewRepresentable {
    @Binding var htmlText: String

    func makeUIView(context: Context) -> UITextView {
        UITextView()
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        let htmlData = NSString(string: htmlText).data(using: String.Encoding.unicode.rawValue)
        let options = [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html]
        do {
            DispatchQueue.main.async {
                let attributedString = try NSAttributedString(data: htmlData!, options: options, documentAttributes: nil)
                uiView.attributedText = attributedString
            }
        } catch {
             print("Unexpected error: \(error).")

        }
    }
}

推荐阅读