[英]How do I highlight text in a string that contains emojis in Swift?
I have the following function to find and highlight hashtags or mentions (@ or #) in a UILabel
: 我有以下功能来查找和突出显示
UILabel
主题标签或提及(@或#):
class func addLinkAttribute(pattern: String,
toText text: String,
withAttributeName attributeName : String,
toAttributedString attributedString :NSMutableAttributedString,
withLinkAttributes linkAttributes: [NSObject : AnyObject]) {
var error: NSError?
if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) {
regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, count(text))) { result, flags, stop in
let range = result.range
let start = advance(text.startIndex, range.location)
let end = advance(start, range.length)
let foundText = text.substringWithRange(Range<String.Index>(start: start,end: end))
var linkAttributesWithName = linkAttributes
linkAttributesWithName[attributeName] = foundText
attributedString.addAttributes(linkAttributesWithName, range: range)
}
}
}
If I pass a hashtag (#)(\\\\w+)
or mention (@)(\\\\w+)
pattern the code works perfectly but if the text contains an Emoji the range is offset by the number of emojis preceding it: 如果我传递一个
(#)(\\\\w+)
标签(#)(\\\\w+)
或提及(@)(\\\\w+)
模式,则代码可以正常工作,但如果文本包含表情符号,则范围会被前面的表情符号数偏移:
I know Swift treats strings differently to Objective-C, since count(string)
and count(string.utf16)
give me different results, but I am stumped as to how to account for this when using a regular expression. 我知道Swift对字符串的处理方式不同于Objective-C,因为
count(string)
和count(string.utf16)
给了我不同的结果,但是在使用正则表达式时我对如何解释这个问题感到count(string.utf16)
。
I could just check the difference between the 2 counts and offset the range, but this seems wrong and hacky to me. 我可以检查两个计数之间的差异并抵消范围,但这对我来说似乎是错误的和黑客的。 There must be another way.
必须有另一种方式。
Similarly as in Swift extract regex matches , a possible solution is to convert the given Swift String
to an NSString
and apply the NSRange
s returned by enumerateMatchesInString()
to that NSString
: 与Swift提取正则表达式匹配类似,一种可能的解决方案是将给定的Swift
String
转换为NSString
并将enumerateMatchesInString()
返回的NSRange
应用于该NSString
:
class func addLinkAttribute(pattern: String,
toText text: String,
withAttributeName attributeName : String,
toAttributedString attributedString :NSMutableAttributedString,
withLinkAttributes linkAttributes: [NSObject : AnyObject]) {
let nsText = text as NSString
var error: NSError?
if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) {
regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, nsText.length)) {
result, _, _ in
let range = result.range
let foundText = nsText.substringWithRange(range)
var linkAttributesWithName = linkAttributes
linkAttributesWithName[attributeName] = foundText
attributedString.addAttributes(linkAttributesWithName, range: range)
}
}
}
(Alternative solution.) It is possible to convert an NSRange
to Range<String.Index>
without intermediate conversion to an NSString
. (替代解决方案。)可以将
NSRange
转换为Range<String.Index>
而无需中间转换为NSString
。 With 同
extension String {
func rangeFromNSRange(nsRange : NSRange) -> Range<String.Index>? {
let utf16start = self.utf16.startIndex
if let from = String.Index(self.utf16.startIndex + nsRange.location, within: self),
let to = String.Index(self.utf16.startIndex + nsRange.location + nsRange.length, within: self) {
return from ..< to
}
return nil
}
}
from https://stackoverflow.com/a/30404532/1187415 , your code can be written as 从https://stackoverflow.com/a/30404532/1187415 ,您的代码可以写成
class func addLinkAttribute(pattern: String,
toText text: String,
withAttributeName attributeName : String,
toAttributedString attributedString :NSMutableAttributedString,
withLinkAttributes linkAttributes: [NSObject : AnyObject]) {
var error: NSError?
if let regex = NSRegularExpression(pattern: pattern, options:.CaseInsensitive, error: &error) {
regex.enumerateMatchesInString(text, options: .allZeros, range: NSMakeRange(0, count(text.utf16))) {
result, _, _ in
let nsRange = result.range
if let strRange = text.rangeFromNSRange(nsRange) {
let foundText = text.substringWithRange(strRange)
var linkAttributesWithName = linkAttributes
linkAttributesWithName[attributeName] = foundText
attributedString.addAttributes(linkAttributesWithName, range: nsRange)
}
}
}
}
and that should also work correctly for all kinds of extended grapheme clusters (Emojis, Regional Indicators etc...) 这也应该适用于各种扩展的字形集群(Emojis,区域指标等......)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.