@tiger_13

Как вывести правильный NSRange с NSAttributedString если NSAttributedString инициализирован с Data(HTML)?

Всем привет!
Есть UITextView, в котором текст присваивается через .attributedText.
NSAttributedString инициализируется с Data (берем с чтения файла, в котором html). При инициализации NSAttributedString указываем documentType - html.

var HTMLdata:Data?{
        didSet{
            guard let data = self.HTMLdata else {return}
            do{
                let attributed = try NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html], documentAttributes: nil)
                textView.attributedText = attributed
            }catch{
                return
            }
        }
    }


На этом этапе все работает хорошо, NSAttributedString распознает html теги, применяет к ним стили, записанные в html.
Но, следующий этап - найти ссылку, и проскролить UITextView к ссылке. Ссылка (тэг a пустой)
Пример куска HTML:
<p class=main-text><a name="link5"></a>
<span class=main-text-span>Название</span> абзаца</p>

Нужно найти a с name "link5" и проскролить к ней.
Так как NSAttributedString уже прочитал HTML, уже отсутствует в attributed.string, и соответственно результатом выполнения:
let range = attributed.string.range(of: #"<a name="link5"></a>"#) // -> nil

будет nil
Если же инициализировать String с Data(HTML), то range не будет nil, но при textView.scrollRangeToVisible() будет отображаться вообще левый кусок, так как в String будут "не отрендеренные" теги, соответственно length и location в NSRange будут намного больше чем в attributed.string.
Как мне найти правильный NSRange?

  • Вопрос задан
  • 180 просмотров
Пригласить эксперта
Ответы на вопрос 1
@tiger_13 Автор вопроса
Как вариант - использовать regex, с помощью которого найти что написано в <p>из отображаемого текста (ищем в String, инициализированным с data(html), то есть видим все теги), и искать этот текст в attributed.string:
//1
    private func getRangeFor(attributedString:String,aValue:String) -> NSRange?{
        guard let aWord = self.findAWords(aValue: aValue),let foundRange = attributedString.range(of: aWord) else {return nil}//ищем aWord - то, что записано в <p>, затем ищем уже range в attributedString(отрендеренный HTML)
        return NSRange(foundRange, in: attributedString)
    }
    
    //2
    private func findAWords(aValue:String) -> String?{
        guard let data = self.HTMLdata,let str = String(data: data, encoding: .utf8) else {return nil}
        
        
        let pattern = #"<p class=main-text><a name="\#(aValue)"></a>\n<span class=main-text-span>(?<paragraph>.+)</span>"#
        let regexRange = NSRange(location: 0, length: str.utf16.count)
        let regex = try! NSRegularExpression(pattern: pattern)
        let regexResult = regex.firstMatch(in: str, options: [], range: regexRange)
        guard let matchedWordRange = regexResult?.range(withName: "paragraph"), let wordRange = Range(matchedWordRange, in: str) else {return nil}
        return String(str[wordRange]) //вернет слово (Название), нужно дописать regex, чтобы была вторая группа - слово ( абзаца)
    }
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы