#swift #nsattributedstring
Вопрос:
Я нахожусь в процессе написания кода для отображения упоминаний в an NSAttributedString
, которые необходимо связать с профилем пользователя. Формат упоминаний таков @username[userid]
, что их нужно было бы отображать как простые @username
, которые можно использовать.
У меня есть код, работающий до сих пор, так что имя пользователя становится доступным для кликабельности, но теперь мне нужно удалить [userid]
часть, которая, конечно, изменяет длину строки, чтобы диапазоны больше не совпадали и т. Д. Не знаю, как я могу это решить.
import Foundation
import UIKit
let comment = "Hello @kevin[1], @john and @andrew[2]!"
let wholeRange = NSRange(comment.startIndex..<comment.endIndex, in: comment)
let regex = try NSRegularExpression(pattern: #"(@[w.-@] )[(d )]"#)
let attributedString = NSMutableAttributedString(string: comment)
regex.enumerateMatches(in: comment, options: [], range: wholeRange) { match, _, _ in
guard let match = match else {
return
}
let userIdRange = Range(match.range(at: 2), in: comment)!
let userId = comment[userIdRange]
let usernameRange = match.range(at: 1)
attributedString.addAttribute(NSAttributedString.Key.link, value: URL(string: "test://profile/(userId)")!, range: usernameRange)
}
print(attributedString)
Результат прямо сейчас может быть представлен следующим образом при печати:
Hello {
}@kevin{
NSLink = "test://profile/1";
}[1], @john and {
}@andrew{
NSLink = "test://profile/2";
}[2]!{
}
Так @kevin
и @andrew
есть ссылки, @john
их нет (чего и следовало ожидать!), но идентификаторы пользователей все равно видны. Конечно, это проблема, которая была решена раньше, но я не могу найти никаких примеров, не знаю, какие ключевые слова искать. Есть много вопросов об обнаружении имен пользователей/упоминаний в строках и еще больше о создании ссылок NSAttributedString
, но это не та проблема , которую я пытаюсь решить.
Как бы я превратил @username[userid]
упоминания в кликабельные @username
ссылки, чтобы [userid]
часть была скрыта?
Ответ №1:
Вам просто нужно получить все соответствующие диапазоны, повторить их в обратном порядке, добавить ссылку на него, а затем заменить весь диапазон именем. Что-то вроде:
let comment = "Hello @kevin[1], @john and @andrew[2]!"
let attributedString = NSMutableAttributedString(string: comment)
let regex = try NSRegularExpression(pattern: #"(@[w.-@] )[(d )]"#)
var ranges: [(NSRange,NSRange,NSRange)] = []
regex.enumerateMatches(in: comment, range: NSRange(comment.startIndex..., in: comment)) { match, _, _ in
guard let match = match else {
return
}
ranges.append((match.range(at: 0),
match.range(at: 1),
match.range(at: 2)))
}
ranges.reversed().forEach {
let userId = attributedString.attributedSubstring(from: $0.2).string
let username = attributedString.attributedSubstring(from: $0.1).string
attributedString.addAttribute(.link, value: URL(string: "test://profile/(userId)")!, range: $0.0)
attributedString.replaceCharacters(in: $0.0, with: username)
}
print(attributedString)
Это приведет к печати
Привет {
}@кевин{
NSLink = «тест://профиль/1»;
}, @Джон и {
}@Эндрю{
NSLink = «тест://профиль/2»;
}!{
}
Ответ №2:
Быстро сделано:
let comment = "Hello @kevin[1], @john and @andrew[2]!"
let attributedString = NSMutableAttributedString(string: comment)
let wholeRange = NSRange(attributedString.string.startIndex..<attributedString.string.endIndex, in: attributedString.string)
let regex = try NSRegularExpression(pattern: #"(@[w.-@] )[(d )]"#)
let matches = regex.matches(in: attributedString.string, options: [], range: wholeRange)
matches.reversed().forEach { aResult in
let fullMatchRange = Range(aResult.range(at: 0), in: attributedString.string)! //@kevin[1]
let replacementRange = Range(aResult.range(at: 1), in: attributedString.string)! //@kevin
let userIdRange = Range(aResult.range(at: 2), in: attributedString.string)! // 1
let atAuthor = String(attributedString.string[replacementRange])
attributedString.addAttribute(.link,
value: URL(string: "test://profile/(attributedString.string[userIdRange])")!,
range: NSRange(fullMatchRange, in: attributedString.string))
attributedString.replaceCharacters(in: NSRange(fullMatchRange, in: attributedString.string),
with: atAuthor)
}
print(attributedString)
Выход:
Hello {
}@kevin{
NSLink = "test://profile/1";
}, @john and {
}@andrew{
NSLink = "test://profile/2";
}!{
}
Что тут можно посмотреть:
- Я изменил шаблон, для удобства захвата. Смотрите пример в комментарии в разделе
forEach()
. - Я использовал совпадения в обратном порядке, иначе диапазоны больше не будут точными!
- Я продолжал играть
attributedString.string
вместоcomment
того, чтобы на случай, если это «несинхронно».
Комментарии:
1. Ах, использование спичек в обратном порядке было моментом, когда я пропустил лампочку. Так просто, как только он щелкнет! 😅 Я получал сбои, когда делал их в обычном порядке.
2. О, действительно, это был предыдущий тест в моем коде, и я подумал, что сначала вы хотели
@john
перейти по ссылкеtest://profile/john
, поэтому я тоже создал дополнительные группы/диапазоны. Но сейчас там должно быть чище.