【问题标题】:Find out if Character in String is emoji?找出字符串中的字符是否是表情符号?
【发布时间】:2015-08-25 18:08:12
【问题描述】:

我需要确定字符串中的字符是否是表情符号。

比如我有这个角色:

let string = "????"
let character = Array(string)[0]

我需要确定该字符是否是表情符号。

【问题讨论】:

  • 我很好奇:你为什么需要这些信息?
  • @EricD.:有 许多 个 Unicode 字符采用多个 UTF-8 代码点(例如“€” = E2 82 AC)或多个 UTF- 16 个代码点(例如“????”=D834 DD1E)。
  • 希望你能从这个 obj-c 版本的代码 stackoverflow.com/questions/19886642/… 中有所启发
  • 字符串有自己的索引,这是使用它们的首选方式。要获取特定字符(或字素簇),您可以:let character = string[string.index(after: string.startIndex)]let secondCharacter = string[string.index(string.startIndex, offsetBy: 1)]

标签: ios string swift character emoji


【解决方案1】:

我偶然发现了字符、Unicode 标量和字形之间的区别。

例如,字形?‍?‍?‍?由7个unicode标量组成:

  • 四个表情符号:????
  • 每个表情符号之间都有一个特殊字符,其作用类似于字符胶;见the specs for more info

另一个例子,字形 ?? 由 2 个 unicode 标量组成:

  • 常规表情符号:?
  • 肤色修饰符:?

最后一个,字形 1️⃣ 包含三个 unicode 字符:

所以在渲染字符时,生成的字形真的很重要。

Swift 5.0 及更高版本使这个过程变得更加容易,并且消除了我们需要做的一些猜测。 Unicode.Scalar 的新 Property 类型有助于确定我们正在处理的内容。 但是,这些属性仅在检查字形中的其他标量时才有意义。这就是为什么我们要在 Character 类中添加一些方便的方法来帮助我们。

更多详情,I wrote an article explaining how this works

对于 Swift 5.0,这会给您以下结果:

extension Character {
    /// A simple emoji is one scalar and presented to the user as an Emoji
    var isSimpleEmoji: Bool {
        guard let firstScalar = unicodeScalars.first else { return false }
        return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
    }

    /// Checks if the scalars will be merged into an emoji
    var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false }

    var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
}

extension String {
    var isSingleEmoji: Bool { count == 1 && containsEmoji }

    var containsEmoji: Bool { contains { $0.isEmoji } }

    var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } }

    var emojiString: String { emojis.map { String($0) }.reduce("", +) }

    var emojis: [Character] { filter { $0.isEmoji } }

    var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } }
}

这将为您提供以下结果:

"A̛͚̖".containsEmoji // false
"3".containsEmoji // false
"A̛͚̖▶️".unicodeScalars // [65, 795, 858, 790, 9654, 65039]
"A̛͚̖▶️".emojiScalars // [9654, 65039]
"3️⃣".isSingleEmoji // true
"3️⃣".emojiScalars // [51, 65039, 8419]
"??".isSingleEmoji // true
"??‍♂️".isSingleEmoji // true
"??".isSingleEmoji // true
"⏰".isSingleEmoji // true
"?".isSingleEmoji // true
"?‍?‍?‍?".isSingleEmoji // true
"???????".isSingleEmoji // true
"???????".containsOnlyEmoji // true
"?‍?‍?‍?".containsOnlyEmoji // true
"Hello ?‍?‍?‍?".containsOnlyEmoji // false
"Hello ?‍?‍?‍?".containsEmoji // true
"? Héllo ?‍?‍?‍?".emojiString // "??‍?‍?‍?"
"?‍?‍?‍?".count // 1

"? Héllœ ?‍?‍?‍?".emojiScalars // [128107, 128104, 8205, 128105, 8205, 128103, 8205, 128103]
"? Héllœ ?‍?‍?‍?".emojis // ["?", "?‍?‍?‍?"]
"? Héllœ ?‍?‍?‍?".emojis.count // 2

"??‍?‍?‍??‍?‍?".isSingleEmoji // false
"??‍?‍?‍??‍?‍?".containsOnlyEmoji // true

For older Swift versions, check out this gist containing my old code.

【讨论】:

  • 这是迄今为止最好和最正确的答案。谢谢!一个小提示,您的示例与代码不匹配(您在 sn-p 中将 containsOnlyEmoki 重命名为 containsEmoji - 我想是因为它更正确,在我的测试中,它对于混合字符的字符串返回 true)。
  • 我的错,我改变了一些代码,我猜我搞砸了。我更新了示例
  • @Andrew:当然,我在示例中添加了另一种方法来演示这一点:)。
  • @Andrew 感谢您指出这一点,我改变了containsOnlyEmoji 检查的方式。我还将示例更新为 Swift 3.0。
  • 我还添加了比较:$0.properties.generalCategory == .otherSymbol 使其适用于更多表情符号,如⏰、?等
【解决方案2】:

最简单、最干净和最快捷的方法是简单地检查字符串中每个字符的 Unicode 代码点与已知的表情符号和 dingbats 范围,如下所示:

extension String {

    var containsEmoji: Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x1F600...0x1F64F, // Emoticons
                 0x1F300...0x1F5FF, // Misc Symbols and Pictographs
                 0x1F680...0x1F6FF, // Transport and Map
                 0x2600...0x26FF,   // Misc symbols
                 0x2700...0x27BF,   // Dingbats
                 0xFE00...0xFE0F,   // Variation Selectors
                 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs
                 0x1F1E6...0x1F1FF: // Flags
                return true
            default:
                continue
            }
        }
        return false
    }

}

【讨论】:

  • 这样的代码示例比建议包含第三方库依赖项要好得多。 Shardul 的回答是不明智的建议——始终编写自己的代码。
  • 这很好,感谢您评论案例的相关内容
  • 就像您的代码一样,我在答案here 中实现了它。我注意到的一件事是它错过了一些表情符号,可能是因为它们不属于您列出的类别,例如这个:Robot Face emoji ?
  • @Tel 我猜这将是0x1F900...0x1F9FF 的范围(根据维基百科)。不确定所有范围都应该被视为表情符号。
【解决方案3】:

斯威夫特 5.0

...引入了一种新的检查方法!

您必须将您的 String 拆分为 Scalars。每个Scalar 都有一个Property 值,它支持isEmoji 值!

实际上,您甚至可以检查标量是否是表情符号修饰符或更多。查看 Apple 的文档:https://developer.apple.com/documentation/swift/unicode/scalar/properties

您可能需要考虑检查isEmojiPresentation 而不是isEmoji,因为Apple 为isEmoji 声明了以下内容:

此属性适用于默认呈现为表情符号的标量,也适用于后跟 U+FE0F VARIATION SELECTOR-16 时具有非默认表情符号呈现的标量。这包括一些通常不被视为表情符号的标量。


这种方式实际上将 Emoji 拆分为所有修饰符,但处理起来更简单。由于 Swift 现在将带有修饰符的表情符号(例如:?‍?‍?‍?、??‍?、?)算作 1,你可以做各种事情。

var string = "? test"

for scalar in string.unicodeScalars {
    let isEmoji = scalar.properties.isEmoji

    print("\(scalar.description) \(isEmoji)"))
}

// ? true
//   false
// t false
// e false
// s false
// t false

NSHipster 指出了获取所有 Emoji 的有趣方法:

import Foundation

var emoji = CharacterSet()

for codePoint in 0x0000...0x1F0000 {
    guard let scalarValue = Unicode.Scalar(codePoint) else {
        continue
    }

    // Implemented in Swift 5 (SE-0221)
    // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md
    if scalarValue.properties.isEmoji {
        emoji.insert(scalarValue)
    }
}

【讨论】:

  • 很好的答案,谢谢。值得一提的是,您的 min sdk 必须为 10.2 才能使用 Swift 5 的这一部分。此外,为了检查字符串是否仅由表情符号组成,我必须检查它是否具有以下属性之一:scalar.properties.isEmoji scalar.properties.isEmojiPresentation scalar.properties.isEmojiModifier scalar.properties.isEmojiModifierBase scalar.properties.isJoinControl scalar.properties.isVariationSelector
  • 请注意,整数 0-9 被视为表情符号。所以"6".unicodeScalars.first!.properties.isEmoji 将评估为true
  • 还有其他字符,例如 #*,对于 isEmoji 检查也将返回 true。 isEmojiPresentation 似乎效果更好,至少它为0...9#* 以及我可以在英美键盘上尝试的任何其他符号返回false。任何人都有更多的经验并知道它是否可以被信任用于输入验证?
  • ❤️ 有两个标量。第一个标量的isEmojitrue,但isEmojiPresentationfalse。第二个标量将只返回 true for isVariationSelector。所以看起来不是一种直接的方式来理解什么是表情符号?
【解决方案4】:

使用 Swift 5,您现在可以检查字符串中每个字符的 unicode 属性。这为我们在每个字母上提供了方便的isEmoji 变量。问题是isEmoji 对于任何可以转换为 2 字节表情符号的字符(例如 0-9)都会返回 true。

我们可以查看变量isEmoji 并检查是否存在表情符号修饰符,以确定模棱两可的字符是否会显示为表情符号。

这个解决方案应该比这里提供的正则表达式解决方案更具未来性。

extension String {
    func containsOnlyEmojis() -> Bool {
        if count == 0 {
            return false
        }
        for character in self {
            if !character.isEmoji {
                return false
            }
        }
        return true
    }
    
    func containsEmoji() -> Bool {
        for character in self {
            if character.isEmoji {
                return true
            }
        }
        return false
    }
}

extension Character {
    // An emoji can either be a 2 byte unicode character or a normal UTF8 character with an emoji modifier
    // appended as is the case with 3️⃣. 0x238C is the first instance of UTF16 emoji that requires no modifier.
    // `isEmoji` will evaluate to true for any character that can be turned into an emoji by adding a modifier
    // such as the digit "3". To avoid this we confirm that any character below 0x238C has an emoji modifier attached
    var isEmoji: Bool {
        guard let scalar = unicodeScalars.first else { return false }
        return scalar.properties.isEmoji && (scalar.value > 0x238C || unicodeScalars.count > 1)
    }
}

给我们

"hey".containsEmoji() //false

"Hello World ?".containsEmoji() //true
"Hello World ?".containsOnlyEmojis() //false

"3".containsEmoji() //false
"3️⃣".containsEmoji() //true

【讨论】:

  • 还有Character("3️⃣").isEmoji // trueCharacter("3").isEmoji // false
【解决方案5】:
extension String {
    func containsEmoji() -> Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x3030, 0x00AE, 0x00A9,// Special Characters
            0x1D000...0x1F77F,          // Emoticons
            0x2100...0x27BF,            // Misc symbols and Dingbats
            0xFE00...0xFE0F,            // Variation Selectors
            0x1F900...0x1F9FF:          // Supplemental Symbols and Pictographs
                return true
            default:
                continue
            }
        }
        return false
    }
}

这是我的修复,更新了范围。

【讨论】:

    【解决方案6】:

    Swift 3 注意:

    cnui_containsEmojiCharacters 方法似乎已被删除或移至不同的动态库。 _containsEmoji 应该仍然可以工作。

    let str: NSString = "hello?"
    
    @objc protocol NSStringPrivate {
        func _containsEmoji() -> ObjCBool
    }
    
    let strPrivate = unsafeBitCast(str, to: NSStringPrivate.self)
    strPrivate._containsEmoji() // true
    str.value(forKey: "_containsEmoji") // 1
    
    
    let swiftStr = "hello?"
    (swiftStr as AnyObject).value(forKey: "_containsEmoji") // 1
    

    Swift 2.x:

    我最近在 NSString 上发现了一个私有 API,它公开了检测字符串是否包含 Emoji 字符的功能:

    let str: NSString = "hello?"
    

    使用 objc 协议和unsafeBitCast:

    @objc protocol NSStringPrivate {
        func cnui_containsEmojiCharacters() -> ObjCBool
        func _containsEmoji() -> ObjCBool
    }
    
    let strPrivate = unsafeBitCast(str, NSStringPrivate.self)
    strPrivate.cnui_containsEmojiCharacters() // true
    strPrivate._containsEmoji() // true
    

    valueForKey:

    str.valueForKey("cnui_containsEmojiCharacters") // 1
    str.valueForKey("_containsEmoji") // 1
    

    对于纯 Swift 字符串,必须将字符串转换为 AnyObject,然后才能使用 valueForKey

    let str = "hello?"
    
    (str as AnyObject).valueForKey("cnui_containsEmojiCharacters") // 1
    (str as AnyObject).valueForKey("_containsEmoji") // 1
    

    NSString header file 中找到的方法。

    【讨论】:

    • 这就是我要找的,谢谢 JAL
    • 这会被苹果拒绝吗?
    • @AndreyChernukha 总是有风险,但我还没有遇到任何拒绝。
    • 永远不要使用私有 API。充其量,伤害只会在明天到来。或者下个月。
    【解决方案7】:

    上面提到的任务有一个很好的solution。但是检查 unicode 标量的 Unicode.Scalar.Properties 对单个字符有好处。而且对字符串不够灵活。

    我们可以改用正则表达式——更通用的方法。下面有它如何工作的详细描述。解决方案就到这里了。

    解决方案

    在 Swift 中,您可以使用具有此类计算属性的扩展来检查字符串是否是单个 Emoji 字符:

    extension String {
    
        var isSingleEmoji : Bool {
            if self.count == 1 {
                let emodjiGlyphPattern = "\\p{RI}{2}|(\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})|[\\p{Emoji}&&\\p{Other_symbol}])(\\x{200D}(\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})|[\\p{Emoji}&&\\p{Other_symbol}]))*"
    
                let fullRange = NSRange(location: 0, length: self.utf16.count)
                if let regex = try? NSRegularExpression(pattern: emodjiGlyphPattern, options: .caseInsensitive) {
                    let regMatches = regex.matches(in: self, options: NSRegularExpression.MatchingOptions(), range: fullRange)
                    if regMatches.count > 0 {
                        // if any range found — it means, that that single character is emoji
                        return true
                    }
                }
            }
            return false
        }
    
    }
    

    工作原理(详细)

    一个表情符号(一个字形)可以通过许多不同的符号、序列及其组合来复制。 Unicode specification 定义了几种可能的 Emoji 字符表示。

    单字符表情符号

    由单个 Unicode 标量复制的表情符号字符。

    Unicode 将 Emoji Character 定义为:

    emoji_character := \p{Emoji}
    

    但这并不一定意味着这样的角色会被绘制为表情符号。一个普通的数字符号“1”的 Emoji 属性为真,尽管它仍可能被绘制为文本。并且有一个这样的符号列表:#、©、4 等。

    人们应该认为,我们可以使用附加属性来检查:“Emoji_Presentation”。但它不是这样工作的。有一个像? 或 ? 这样的 Emoji,具有 Emoji_Presentation=false 属性。

    为了确保字符默认绘制为表情符号,我们应该检查它的类别:它应该是“Other_symbol”。

    所以,实际上单字符表情符号的正则表达式应该定义为:

    emoji_character := \p{Emoji}&&\p{Other_symbol}
    

    表情符号演示序列

    一个字符,通常可以绘制为文本或表情符号。它的外观取决于一个特殊的跟随符号,一个表示选择器,它指示它的表示类型。 \x{FE0E} 定义文本表示。 \x{FE0F} 定义表情符号表示。

    可以在 [此处](https://unicode.org/Public/emoji/12.1/emoji-variation-sequences.txt) 找到此类符号的列表。

    Unicode 定义表示顺序如下:

    emoji_presentation_sequence := emoji_character emoji_presentation_selector
    

    它的正则表达式序列:

    emoji_presentation_sequence := \p{Emoji} \x{FE0F}
    

    表情键帽序列

    该序列看起来与 Presentation 序列非常相似,但它在末尾多了一个标量:\x{20E3}。用于它的可能的基本标量的范围相当狭窄:0-9#* - 仅此而已。示例:1️⃣、8️⃣、*️⃣。

    Unicode 定义键帽序列如下:

    emoji_keycap_sequence := [0-9#*] \x{FE0F 20E3}
    

    正则表达式:

    emoji_keycap_sequence := \p{Emoji} \x{FE0F} \x{FE0F}
    

    表情符号修饰符序列

    某些表情符号可以修改外观,例如肤色。例如表情符号?可以不同:???????????。要定义一个 Emoji,在这种情况下称为“Emoji_Modifier_Base”,可以使用后续的“Emoji_Modifier”。

    一般来说这样的顺序是这样的:

    emoji_modifier_sequence := emoji_modifier_base emoji_modifier
    

    要检测它,我们可以搜索正则表达式序列:

    emoji_modifier_sequence := \p{Emoji} \p{EMod}
    

    表情标志序列

    标志是具有特定结构的表情符号。每个标志都用两个“Regional_Indicator”符号表示。

    Unicode 将它们定义为:

    emoji_flag_sequence := regional_indicator regional_indicator
    

    例如,乌克兰的国旗??实际上用两个标量表示:\u{0001F1FA \u{0001F1E6}

    正则表达式:

    emoji_flag_sequence := \p{RI}{2}
    

    表情符号标签序列 (ETS)

    使用所谓的 tag_base 的序列,其后是由符号范围 \x{E0020}-\x{E007E} 组成的自定义标签规范,并以 tag_end 标记 \x{E007F} 结束。

    Unicode 是这样定义的:

    emoji_tag_sequence := tag_base tag_spec tag_end
    tag_base           := emoji_character
                        | emoji_modifier_sequence
                        | emoji_presentation_sequence
    tag_spec           := [\x{E0020}-\x{E007E}]+
    tag_end            := \x{E007F}
    

    奇怪的是,Unicode 允许标签基于ED-14a 中的 emoji_modifier_sequence 或 emoji_presentation_sequence。但同时在同一 documentation 提供的正则表达式中,它们似乎仅基于单个 Emoji 字符检查序列。

    在 Unicode 12.1 表情符号列表中,仅定义了 three such Emojis。它们都是英国国家的国旗:英格兰???????、苏格兰???????和威尔士???????。所有这些都基于一个表情符号字符。所以,我们最好只检查这样的序列。

    正则表达式:

    \p{Emoji} [\x{E0020}-\x{E007E}]+ \x{E007F}
    

    表情符号零宽度连接序列(ZWJ 序列)

    零宽度连接符是一个标量 \x{200D}。在它的帮助下,可以将几个本身已经是表情符号的字符组合成新的。

    例如,“有父亲、儿子和女儿的家庭”表情符号?‍?‍?是由父亲?、女儿?和儿子?用ZWJ符号粘在一起的表情符号组合而成的。

    允许将单个 Emoji 字符、Presentation 和 Modifier 序列等元素粘在一起。

    此类序列的正则表达式通常如下所示:

    emoji_zwj_sequence := emoji_zwj_element (\x{200d} emoji_zwj_element )+
    

    所有的正则表达式

    上面提到的所有表情符号都可以用一个正则表达式来描述:

    \p{RI}{2}
    | ( \p{Emoji} 
        ( \p{EMod} 
        | \x{FE0F}\x{20E3}? 
        | [\x{E0020}-\x{E007E}]+\x{E007F} 
        ) 
      | 
[\p{Emoji}&&\p{Other_symbol}] 
      )
      ( \x{200D}
        ( \p{Emoji} 
          ( \p{EMod} 
          | \x{FE0F}\x{20E3}? 
          | [\x{E0020}-\x{E007E}]+\x{E007F} 
          ) 
        | [\p{Emoji}&&\p{Other_symbol}] 
        ) 
      )*
    

    【讨论】:

      【解决方案8】:

      您可以使用此代码example 或此pod

      要在 Swift 中使用它,请将类别导入 YourProject_Bridging_Header

      #import "NSString+EMOEmoji.h"
      

      然后您可以检查字符串中每个表情符号的范围:

      let example: NSString = "string?‍?‍?‍?with?emojis✊?" //string with emojis
      
      let containsEmoji: Bool = example.emo_containsEmoji()
      
          print(containsEmoji)
      
      // Output: ["true"]
      

      I created an small example project with the code above.

      【讨论】:

        【解决方案9】:

        未来证明:手动检查角色的像素;随着新表情符号的添加,其他解决方案将中断(并且已经中断)。

        注意:这是Objective-C(可以转换成Swift)

        多年来,随着 Apple 通过新方法添加新的表情符号(例如通过预先诅咒一个角色和一个额外的角色构建的肤色表情符号)等,这些表情符号检测解决方案不断出现问题。

        我终于崩溃了,只写了以下适用于所有当前表情符号的方法,并且应该适用于所有未来的表情符号。

        该解决方案创建一个带有字符和黑色背景的 UILabel。然后 CG 拍摄标签的快照,然后我扫描快照中的所有像素以查找任何非纯黑色像素。我添加黑色背景的原因是为了避免由于Subpixel Rendering而导致的错误着色问题

        该解决方案在我的设备上运行得非常快,我每秒可以检查数百个字符,但应该注意这是一个 CoreGraphics 解决方案,不应该像使用常规文本方法那样大量使用。图形处理的数据量很大,因此一次检查数千个字符可能会导致明显的延迟。

        -(BOOL)isEmoji:(NSString *)character {
            
            UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
            characterRender.text = character;
            characterRender.font = [UIFont fontWithName:@"AppleColorEmoji" size:12.0f];//Note: Size 12 font is likely not crucial for this and the detector will probably still work at an even smaller font size, so if you needed to speed this checker up for serious performance you may test lowering this to a font size like 6.0
            characterRender.backgroundColor = [UIColor blackColor];//needed to remove subpixel rendering colors
            [characterRender sizeToFit];
            
            CGRect rect = [characterRender bounds];
            UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
            CGContextRef contextSnap = UIGraphicsGetCurrentContext();
            [characterRender.layer renderInContext:contextSnap];
            UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            
            CGImageRef imageRef = [capturedImage CGImage];
            NSUInteger width = CGImageGetWidth(imageRef);
            NSUInteger height = CGImageGetHeight(imageRef);
            CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
            unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
            NSUInteger bytesPerPixel = 4;//Note: Alpha Channel not really needed, if you need to speed this up for serious performance you can refactor this pixel scanner to just RGB
            NSUInteger bytesPerRow = bytesPerPixel * width;
            NSUInteger bitsPerComponent = 8;
            CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                         bitsPerComponent, bytesPerRow, colorSpace,
                                                         kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
            CGColorSpaceRelease(colorSpace);
            
            CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
            CGContextRelease(context);
            
            BOOL colorPixelFound = NO;
            
            int x = 0;
            int y = 0;
            while (y < height && !colorPixelFound) {
                while (x < width && !colorPixelFound) {
                    
                    NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
                    
                    CGFloat red = (CGFloat)rawData[byteIndex];
                    CGFloat green = (CGFloat)rawData[byteIndex+1];
                    CGFloat blue = (CGFloat)rawData[byteIndex+2];
                    
                    CGFloat h, s, b, a;
                    UIColor *c = [UIColor colorWithRed:red green:green blue:blue alpha:1.0f];
                    [c getHue:&h saturation:&s brightness:&b alpha:&a];//Note: I wrote this method years ago, can't remember why I check HSB instead of just checking r,g,b==0; Upon further review this step might not be needed, but I haven't tested to confirm yet. 
                    
                    b /= 255.0f;
                    
                    if (b > 0) {
                        colorPixelFound = YES;
                    }
                    
                    x++;
                }
                x=0;
                y++;
            }
            
            return colorPixelFound;
            
        }
        

        【讨论】:

        • 我喜欢你的想法! ;) - 开箱即用!
        • 你为什么要这样对我们? #apple #unicodestandard ???????
        • 我有一段时间没看这个了,但我想知道是否必须先转换为 UIColor 然后再转换为 hsb;看来我可以检查 r,g,b all == 0?如果有人尝试让我知道
        • 我喜欢这个解决方案,但它不会被 ℹ 这样的字符打破吗?
        • @JuanCarlosOspinaGonzalez 不,在表情符号中呈现为带有白色 i 的蓝色框。尽管 UILabel 应该强制字体为AppleColorEmoji,但它确实提出了一个很好的观点,并补充说现在作为故障保险,尽管我认为苹果无论如何都会默认它
        【解决方案10】:

        对于 Swift 3.0.2,下面的答案是最简单的一个:

        class func stringContainsEmoji (string : NSString) -> Bool
        {
            var returnValue: Bool = false
        
            string.enumerateSubstrings(in: NSMakeRange(0, (string as NSString).length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in
        
                let objCString:NSString = NSString(string:substring!)
                let hs: unichar = objCString.character(at: 0)
                if 0xd800 <= hs && hs <= 0xdbff
                {
                    if objCString.length > 1
                    {
                        let ls: unichar = objCString.character(at: 1)
                        let step1: Int = Int((hs - 0xd800) * 0x400)
                        let step2: Int = Int(ls - 0xdc00)
                        let uc: Int = Int(step1 + step2 + 0x10000)
        
                        if 0x1d000 <= uc && uc <= 0x1f77f
                        {
                            returnValue = true
                        }
                    }
                }
                else if objCString.length > 1
                {
                    let ls: unichar = objCString.character(at: 1)
                    if ls == 0x20e3
                    {
                        returnValue = true
                    }
                }
                else
                {
                    if 0x2100 <= hs && hs <= 0x27ff
                    {
                        returnValue = true
                    }
                    else if 0x2b05 <= hs && hs <= 0x2b07
                    {
                        returnValue = true
                    }
                    else if 0x2934 <= hs && hs <= 0x2935
                    {
                        returnValue = true
                    }
                    else if 0x3297 <= hs && hs <= 0x3299
                    {
                        returnValue = true
                    }
                    else if hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50
                    {
                        returnValue = true
                    }
                }
            }
        
            return returnValue;
        }
        

        【讨论】:

          【解决方案11】:

          与我之前写的那些答案绝对相似,但更新了一组表情符号标量。

          extension String {
              func isContainEmoji() -> Bool {
                  let isContain = unicodeScalars.first(where: { $0.isEmoji }) != nil
                  return isContain
              }
          }
          
          
          extension UnicodeScalar {
          
              var isEmoji: Bool {
                  switch value {
                  case 0x1F600...0x1F64F,
                       0x1F300...0x1F5FF,
                       0x1F680...0x1F6FF,
                       0x1F1E6...0x1F1FF,
                       0x2600...0x26FF,
                       0x2700...0x27BF,
                       0xFE00...0xFE0F,
                       0x1F900...0x1F9FF,
                       65024...65039,
                       8400...8447,
                       9100...9300,
                       127000...127600:
                      return true
                  default:
                      return false
                  }
              }
          
          }
          

          【讨论】:

          • 关于您的更新的简短信息:65024... 65039 == 0xFE00...0xFE0F 所以它翻了一番。
          【解决方案12】:

          Swift 5 解决方案使用 Scalars,适用于文本、笑脸??、心形表情符号?❤️❤️‍? 和数字 0️⃣ 1 2 3 等

          Swift 5 Scalars 具有 isEmojiisEmojiPresentation 属性,这将有助于查找特定字符串中的表情符号。

          isEmoji - 布尔值,指示标量是否有表情符号表示,是否为默认值。

          isEmojiPresentation - 一个布尔值,指示标量是否应使用表情符号呈现, 默认情况下,而不是文本演示。

          从这些定义可以看出,我们不能只在字符串的标量上使用isEmojiisEmojiPresentation - 这不会告诉我们这个标量是否真的是一个表情符号

          幸运的是苹果给了我们一个线索:

          在单个标量上单独测试 isEmoji 不足以 确定文本单元是否呈现为表情符号;正确的测试 需要检查 Character 中的多个标量。此外 检查基本标量是否有isEmoji == true,您还必须 检查其默认显示(请参阅isEmojiPresentation)和 确定它后面是否有一个变体选择器,该变体选择器将 修改演示文稿。

          所以最后是我的实现,适用于数字、笑脸?、文本和❤️符号:

          import Foundation
          
          extension String {
          
              func containsEmoji() -> Bool {
                  
                  for character in self {
                      var shouldCheckNextScalar = false
                      for scalar in character.unicodeScalars {
                         if shouldCheckNextScalar {
                              if scalar == "\u{FE0F}" { // scalar that indicates that character should be displayed as emoji
                                  return true
                              }
                              shouldCheckNextScalar = false
                          }
                          
                          if scalar.properties.isEmoji {
                              if scalar.properties.isEmojiPresentation {
                                  return true
                              }
                              shouldCheckNextScalar = true
                          }
                      }
                  }
                  
                  return false
              }
              
          }
          

          测试:

          "hello ❤️".containsEmoji()   // true
          "1234567890".containsEmoji() // false
          "numero 0️⃣".containsEmoji()  // true
          "abcde".containsEmoji()      // false
          "panda ?".containsEmoji()   // true
          

          【讨论】:

            【解决方案13】:

            你可以像这样使用NSString-RemoveEmoji

            if string.isIncludingEmoji {
            
            }
            

            【讨论】:

              【解决方案14】:
              extension String {
                  // Returns false for if string contains characters like "Á??‍?‍???1️⃣"
                  var hasRestrictedCharacter: Bool {
                      contains { !$0.isASCII }
                  }
              }
              
              let testChars = " d1/Á??‍?‍???1️⃣"
              
              for char in testChars {
                  let value = "\(char)".hasRestrictedCharacter
                  print("\(char) : \(value)")
              }
              
              //  : false
              //d : false
              //1 : false
              /// : false
              //Á : true
              //? : true
              //?‍?‍? : true
              //?? : true
              //1️⃣ : true
              

              【讨论】:

                【解决方案15】:

                使用以下扩展,

                extension Character {
                    var isSimpleEmoji: Bool {
                        guard let firstScalar = unicodeScalars.first else {
                            return false
                        }
                        return firstScalar.properties.isEmoji && firstScalar.value > 0x238C
                    }  
                
                    var isCombinedIntoEmoji: Bool {
                        unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false
                    }
                
                    var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji }
                }
                
                extension String {
                    var containsEmoji: Bool {
                        contains(where: { $0.isEmoji })
                    }
                }
                

                如何使用

                let str = "?"
                print(str.containsEmoji) // true
                

                reference的原始答案。

                【讨论】:

                • 来自the docs:“仅在单个标量上测试 isEmoji 不足以确定文本单元是否呈现为表情符号;正确的测试需要检查字符中的多个标量。除了检查基本标量是否具有 isEmoji == true,您还必须检查其默认表示形式(请参阅 isEmojiPresentation)并确定它后面是否有会修改表示形式的变体选择器。"
                • 我已经做出了相应的更改@humblehacker
                【解决方案16】:

                我遇到了同样的问题,最后做了一个 StringCharacter 扩展。

                代码太长,无法发布,因为它实际上在 CharacterSet 中列出了所有表情符号(来自官方 unicode 列表 v5.0),您可以在这里找到它:

                https://github.com/piterwilson/StringEmoji

                常量

                让 emojiCharacterSet: CharacterSet

                包含所有已知表情符号的字符集(如官方 Unicode 列表 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html 中所述)

                字符串

                var isEmoji: 布尔 { 获取 }

                String 实例是否代表已知的单个 Emoji 字符

                print("".isEmoji) // false
                print("?".isEmoji) // true
                print("??".isEmoji) // false (String is not a single Emoji)
                
                var containsEmoji: Bool { get }

                String 实例是否包含已知的 Emoji 字符

                print("".containsEmoji) // false
                print("?".containsEmoji) // true
                print("??".containsEmoji) // true
                
                var unicodeName: 字符串 { 获取 }

                在字符串的副本上应用 kCFStringTransformToUnicodeName - CFStringTransform

                print("á".unicodeName) // \N{LATIN SMALL LETTER A WITH ACUTE}
                print("?".unicodeName) // "\N{FACE WITH STUCK-OUT TONGUE AND WINKING EYE}"
                
                var niceUnicodeName: 字符串 { 获取 }

                返回 kCFStringTransformToUnicodeName - CFStringTransform 的结果,去掉 \N{ 前缀和 } 后缀

                print("á".unicodeName) // LATIN SMALL LETTER A WITH ACUTE
                print("?".unicodeName) // FACE WITH STUCK-OUT TONGUE AND WINKING EYE
                

                字符

                var isEmoji: 布尔 { 获取 }

                Character 实例是否代表已知的 Emoji 字符

                print("".isEmoji) // false
                print("?".isEmoji) // true
                

                【讨论】:

                  【解决方案17】:

                  原生一行代码

                  "❤️".unicodeScalars.contains { $0.properties.isEmoji } // true
                  

                  从 Swift 5.0 开始工作

                  【讨论】:

                  • 是的,还有:"1".unicodeScalars.contains { $0.properties.isEmoji } // true
                  猜你喜欢
                  • 2021-06-24
                  • 2018-06-05
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-07-27
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-11-23
                  相关资源
                  最近更新 更多