【问题标题】:How do I highlight text in a string that contains emojis in Swift?如何在 Swift 中突出显示包含表情符号的字符串中的文本?
【发布时间】:2026-02-24 03:35:01
【问题描述】:

我有以下功能可以在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)
            }
        }
    }

如果我传递一个主题标签 (#)(\\w+) 或提及 (@)(\\w+) 模式,则代码可以完美运行,但如果文本包含表情符号,则范围会被其前面的表情符号数量所抵消:

我知道 Swift 处理字符串的方式与 Objective-C 不同,因为 count(string)count(string.utf16) 给我的结果不同,但我不知道在使用正则表达式时如何解释这一点。

我可以检查两个计数之间的差异并偏移范围,但这对我来说似乎是错误的和 hacky。一定有别的办法。

【问题讨论】:

    标签: ios regex swift emoji


    【解决方案1】:

    Swift extract regex matches 类似,一种可能的解决方案是将给定的Swift StringNSString 并应用由返回的NSRanges enumerateMatchesInString() 到那个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)
            }
        }
    }
    

    (替代解决方案。) 可以将NSRange 转换为Range&lt;String.Index&gt;,而无需中间转换为NSString。 与

    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
        }
    }
    

    来自https://*.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)
                }
            }
        }
    }
    

    这也应该适用于各种扩展字形 集群(表情符号、区域指标等)

    【讨论】:

    • 完美运行!谢谢你。我确信有一种方法可以在不依赖 NSString 的情况下做到这一点,但这是可行的,我认为 NSString 不会很快出现。
    • @MichaelGaylord:你激起了我的野心,看到更新的答案:)
    • @MartinR 太棒了!感谢分享!像魅力一样工作!