在UITextView中显示HTML文本崩溃发生在swift 4中?

问题描述 投票:7回答:5

我正在使用此代码段

Code to show HTML Text in TextView

var htmlToAttributedString: NSAttributedString? {
    guard let data = data(using: .utf8) else { return NSAttributedString() }
    do {
        return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil) // Get crash on this line
    } catch let error {
        print(error.localizedDescription)
        return NSAttributedString()
    }
}

var htmlToString: String {
    return htmlToAttributedString?.string ?? ""
}

HTML显示UITableViewCell文本

cell.textViewMessage.attributedText = msg.htmlToAttributedString

启动第一次没有崩溃,但在那之后,当我运行代码时崩溃并且之后没有工作。

线程1:EXC_BAD_ACCESS(代码= 1,地址= 0x10)

#Edit HTML要在单元格中显示的字符串

<p>Here\'s a short video tour.  Press play to start.</p><br><iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://www.youtube.com\"></iframe><br>

#Edit 1 - 我试图在Playground中运行此代码并且它正常工作,除非它现在显示错误。请参阅附图

Playground output

html ios swift uitextview swift4
5个回答
5
投票

看起来问题是标记,而不是每个标记都可以在uitextview中显示。您可以在uiwebview中更好地显示这些标记

我认为问题是iframe标签。

要显示iFrame,请使用uiwebview或wkwebview。

谢谢


1
投票

这是一个受this repo启发的解决方案。基本上我们删除iframe标签,并用可点击的img替换它:

let msg = "<p>Here\'s a short video tour. Press play to start.</p><br><iframe class=\"ql-video\" frameborder=\"0\" allowfullscreen=\"true\" src=\"https://www.youtube.com/embed/wJcOvdkI7mU\"></iframe><br>"

//Let's get the video id
let range = NSRange(location: 0, length: msg.utf16.count)
let regex = try! NSRegularExpression(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)")
guard let match = regex.firstMatch(in: msg, options: [], range: range) else {
    fatalError("Couldn't find the video ID")
}
let videoId: String = String(msg[Range(match.range, in: msg)!])

//Let's replace the the opening iframe tag
let regex2 = try! NSRegularExpression(pattern:
    "<[\\s]*iframe[\\s]+.*src=")

let str2 = regex2.stringByReplacingMatches(in: msg, options: [], range: range, withTemplate: "<a href=")

//And then replace the closing tag
let regex3 = try! NSRegularExpression(pattern:
    "><\\/iframe>")
let range2 = NSRange(location: 0, length: str2.utf16.count)
let str3 = regex3.stringByReplacingMatches(in: str2, options: [], range: range2, withTemplate: "><img src=\"https://img.youtube.com/vi/" + videoId + "/0.jpg\" alt=\"\" width=\"\(textView.frame.width)\" /></a>")         // You could adjust the width and height to your liking

//Set the text of the textView
textView.attributedText = str3.htmlToAttributedString
textView.delegate = self

要在用户点按并按住图像时打开Youtube应用,请实现此委托方法:

extension NameOfYourViewController: UITextViewDelegate {
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        UIApplication.shared.open(URL, options: [:])
        return true
    }
}

如果未安装youtube应用,则视频将在Safari中播放。

结果如下:

Youtube in a UITextField


0
投票

将您的选项更改为:

let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
                NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
                NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
            ]

0
投票

试试这个UITextView:

let string = "<h2>The bedding was hardly able to cover it.</h2>"
if !string.isEmpty{
    if let htmlData = string.data(using:String.Encoding.unicode) {
    do {
          let attributedText = try NSAttributedString(data: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
           cell.textViewMessage.attributedText = attributedText
       } catch let e as NSError {
          print("Couldn't translate \(string): \(e.localizedDescription) ")
       }
 }

尝试使用UILabel将html文本设置为带有html标签的uilabel:

extension String {
     var withoutHtmlTags: String {
            let a = self.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil)

            return a.replacingOccurrences(of: "&[^;]+;", with: "", options: String.CompareOptions.regularExpression, range: nil)
        }
    }

let string = "<h2>The bedding was hardly able to cover it.</h2>"

textUIlabel.text = string.withoutHtmlTags

0
投票

这个问题的原因是表视图,这个错误将非常随机地发生并且难以重现,因为它更具体到设备内存和UI绘制过程,这可能导致在后台线程中执行该方法。在重新分配和释放表格单元格时,在某些地方,表格单元格可能会在后台线程上调用此方法,而HTML导入程序使用非线程安全的WebKit导入程序并期望位于主线程上。

如何重现此错误:使用UITest运行代码并且它会更频繁地崩溃,因为单元测试会显着减慢UI绘制过程

解决方案:解码HTML到String应该在主线程上,但是在主线程的模型层中执行此操作,而不是在创建单元格时执行此操作。这将使UI更加流畅。

为什么没有在catch块中捕获崩溃:您的应用程序因为未处理的语言异常而崩溃,正如Objective-C的异常处理基础结构所使用的那样。 SWIFT就像Cocoa的NSError一个很好的包装器。 Swift无法处理Objective-C异常,因此框架中的Objective-C代码的异常不会由Swift错误处理程序引起。

© www.soinside.com 2019 - 2024. All rights reserved.