简体   繁体   English

如何在Swift中包含emojis的字符串中突出显示文本?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM