NSAttributedString:将(多个)@username[userid] 提及转化为可点击的@username 链接
NSAttributedString: turn (multiple) @username[userid] mentions into clickable @username links
我正在编写代码以在 NSAttributedString
中显示提及,这需要 link 输出到用户个人资料。提及的格式是 @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
是 link,@john
不是(这是预期的!),但用户 ID 仍然可见。当然这是一个以前已经解决的问题,但我找不到任何例子,不知道要搜索什么关键字。有很多关于在字符串中检测 usernames/mentions 的问题,甚至更多关于在 NSAttributedString
中制作 link 的问题,但这不是我要解决的问题。
如何将 @username[userid]
提及变成可点击的 @username
link,从而隐藏 [userid]
部分?
你只需要获取所有匹配的范围,以相反的顺序迭代它们,添加 link 然后用名称替换整个范围。类似于:
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: [=10=].2).string
let username = attributedString.attributedSubstring(from: [=10=].1).string
attributedString.addAttribute(.link, value: URL(string: "test://profile/\(userId)")!, range: [=10=].0)
attributedString.replaceCharacters(in: [=10=].0, with: username)
}
print(attributedString)
这将打印
Hello {
}@kevin{
NSLink = "test://profile/1";
}, @john and {
}@andrew{
NSLink = "test://profile/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
以防它“不同步”。
我正在编写代码以在 NSAttributedString
中显示提及,这需要 link 输出到用户个人资料。提及的格式是 @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
是 link,@john
不是(这是预期的!),但用户 ID 仍然可见。当然这是一个以前已经解决的问题,但我找不到任何例子,不知道要搜索什么关键字。有很多关于在字符串中检测 usernames/mentions 的问题,甚至更多关于在 NSAttributedString
中制作 link 的问题,但这不是我要解决的问题。
如何将 @username[userid]
提及变成可点击的 @username
link,从而隐藏 [userid]
部分?
你只需要获取所有匹配的范围,以相反的顺序迭代它们,添加 link 然后用名称替换整个范围。类似于:
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: [=10=].2).string
let username = attributedString.attributedSubstring(from: [=10=].1).string
attributedString.addAttribute(.link, value: URL(string: "test://profile/\(userId)")!, range: [=10=].0)
attributedString.replaceCharacters(in: [=10=].0, with: username)
}
print(attributedString)
这将打印
Hello {
}@kevin{
NSLink = "test://profile/1";
}, @john and {
}@andrew{
NSLink = "test://profile/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
以防它“不同步”。