Конвертировать HTML в NSAttributedString в фоновом режиме? - PullRequest
0 голосов
/ 03 сентября 2018

Я работаю над приложением, которое будет извлекать сообщения из WordPress и позволит пользователю просматривать каждое сообщение отдельно, подробно. WordPress API возвращает содержимое сообщения, которое является HTML-кодом сообщения. (Примечание: теги img ссылаются на URL-адрес WordPress загруженного изображения)

Изначально я использовал WebView и загружал извлеченный контент прямо в него. Это сработало отлично; однако изображения, казалось, загружались в основной поток, поскольку это вызывало задержку и иногда приводило к зависанию пользовательского интерфейса до завершения загрузки изображения. Мне предложили проверить библиотеку Aztec Editor из WordPress; Однако я не мог понять, как его использовать (не мог найти много документации).

Мой текущий маршрут - анализ содержимого HTML и создание списка словарей (ключи типа [изображение или текст] и содержимое). После анализа я создаю сообщение, динамически добавляя представления «Метки» и «Изображения» (что позволяет загружать изображения в фоновом режиме). Хотя это кажется чрезмерно сложным и, вероятно, неправильным маршрутом, он работает хорошо (хотя он будет открыт для любых других решений!). В настоящее время моей единственной проблемой является задержка преобразования строки HTML в NSAttributedText. Перед добавлением текстового содержимого в словарь я преобразую его из строки в NSAttributedString. Я заметил задержку в несколько секунд, и процессор симулятора в течение нескольких секунд поднялся до 50-60%, а затем упал. Есть ли в любом случае я мог бы сделать это преобразование в фоновом потоке (ы) и отображать анимацию загрузки в течение этого времени?

Пожалуйста, дайте мне знать, если у вас есть идеи или предложения для лучшего решения. Большое спасибо!

Код:

let postCache = NSCache<NSString, AnyObject>()
var yPos = CGFloat(20)
let screenWidth = UIScreen.main.bounds.width

...

 func parsePost() -> [[String:Any]]? {
    if let postFromCache = postCache.object(forKey: postToView.id as NSString) as? [[String:Any]] {
        return postFromCache
    } else {
        var content = [[String:Any]]()

        do {
            let doc: Document = try SwiftSoup.parse(postToView.postContent)
            if let elements = try doc.body()?.children() {
                for elem in elements {
                    if(elem.hasText()) {
                        do {
                            let html = try elem.html()
                            if let validHtmlString = html.htmlToAttributedString {
                                content.append(["text" : validHtmlString])
                            }
                        } 
                    } else {
                        let imageElements = try elem.getElementsByTag("img")
                        if(imageElements.size() > 0) {
                            for image in imageElements {
                                var imageDictionary = [String:Any]()
                                let width = (image.getAttributes()?.get(key: "width"))!
                                let height = (image.getAttributes()?.get(key: "height"))!
                                let ratio = CGFloat(Float(height)!/Float(width)!)
                                imageDictionary["ratio"] = ratio
                                imageDictionary["image"] = (image.getAttributes()?.get(key: "src"))!

                                content.append(imageDictionary)
                            }
                        }
                    }
                }
            }
        } catch {
            print("error")
        }

        if(content.count > 0) {
            postCache.setObject(content as AnyObject, forKey: postToView.id as NSString)
        }

        return content
    }
}

func buildUi(content: [[String:Any]]) {
    for dict in content {
        if let attributedText = dict["text"] as? NSAttributedString {
            let labelToAdd = UILabel()
            labelToAdd.attributedText = attributedText
            labelToAdd.numberOfLines = 0
            labelToAdd.frame = CGRect(x:0, y:yPos, width: 200, height: 0)
            labelToAdd.sizeToFit()
            yPos += labelToAdd.frame.height + 5
            self.scrollView.addSubview(labelToAdd)
        } else if let imageName = dict["image"] as? String {
            let ratio = dict["ratio"] as! CGFloat
            let imageToAdd = UIImageView()
            let url = URL(string: imageName)
            Nuke.loadImage(with: url!, into: imageToAdd)
            imageToAdd.frame = CGRect(x:0, y:yPos, width: screenWidth, height: screenWidth*ratio)
            yPos += imageToAdd.frame.height + 5
            self.scrollView.addSubview(imageToAdd)
        }
    }
    self.scrollView.contentSize = CGSize(width: self.scrollView.contentSize.width, height: yPos)
}

extension String {
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)
    } catch {
        return NSAttributedString()
    }
}
var htmlToString: String {
    return htmlToAttributedString?.string ?? ""
}

}

(Простите за не очень чистый код! Я просто хочу убедиться, что смогу достичь желаемого результата, прежде чем начать рефакторинг. Еще раз спасибо!)

...