找出string中的字符是表情符号吗?

我需要找出一个string中的字符是否是一个表情符号。

例如,我有这个字符:

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

我需要找出这个angular色是否是一个表情符号。

我偶然发现的是人物,unicode标量和glyphs之间的区别。

例如,字形👨👨👧👧由7个unicode标量组成:

  • 四个表情符号:👨👩👧👧
  • 在每个表情符号之间是一个特殊的字符,它像字符胶一样工作; 请参阅规格了解更多信息

另一个例子,字形👌🏿由2个Unicode标量组成:

  • 常规的表情符号:👌
  • 肤色修改器:🏿

所以在渲染字符的时候,结果字形真的很重要。

我正在寻找的是一种方法来检测一个string是否只是一个表情符号。 所以我可以渲染它比正常文本更大(如iOS10和WhatsApp现在做的消息)。 如上所述,字符数真的没用。 (“胶水人物”也不被视为表情符号)。

你可以做的是使用CoreText来帮助你把string分解成字形并对它们进行计数。 此外,我会将部分由Arnold和Sebastian Lopez提出的扩展部分UnicodeScalar的单独扩展部分。

它给你以下结果:

 import UIKit extension UnicodeScalar { var isEmoji: Bool { switch value { case 0x1F600...0x1F64F, // Emoticons 0x1F300...0x1F5FF, // Misc Symbols and Pictographs 0x1F680...0x1F6FF, // Transport and Map 0x1F1E6...0x1F1FF, // Regional country flags 0x2600...0x26FF, // Misc symbols 0x2700...0x27BF, // Dingbats 0xFE00...0xFE0F, // Variation Selectors 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs 65024...65039, // Variation selector 8400...8447: // Combining Diacritical Marks for Symbols return true default: return false } } var isZeroWidthJoiner: Bool { return value == 8205 } } extension String { var glyphCount: Int { let richText = NSAttributedString(string: self) let line = CTLineCreateWithAttributedString(richText) return CTLineGetGlyphCount(line) } var isSingleEmoji: Bool { return glyphCount == 1 && containsEmoji } var containsEmoji: Bool { return unicodeScalars.contains { $0.isEmoji } } var containsOnlyEmoji: Bool { return !isEmpty && !unicodeScalars.contains(where: { !$0.isEmoji && !$0.isZeroWidthJoiner }) } // The next tricks are mostly to demonstrate how tricky it can be to determine emoji's // If anyone has suggestions how to improve this, please let me know var emojiString: String { return emojiScalars.map { String($0) }.reduce("", +) } var emojis: [String] { var scalars: [[UnicodeScalar]] = [] var currentScalarSet: [UnicodeScalar] = [] var previousScalar: UnicodeScalar? for scalar in emojiScalars { if let prev = previousScalar, !prev.isZeroWidthJoiner && !scalar.isZeroWidthJoiner { scalars.append(currentScalarSet) currentScalarSet = [] } currentScalarSet.append(scalar) previousScalar = scalar } scalars.append(currentScalarSet) return scalars.map { $0.map{ String($0) } .reduce("", +) } } fileprivate var emojiScalars: [UnicodeScalar] { var chars: [UnicodeScalar] = [] var previous: UnicodeScalar? for cur in unicodeScalars { if let previous = previous, previous.isZeroWidthJoiner && cur.isEmoji { chars.append(previous) chars.append(cur) } else if cur.isEmoji { chars.append(cur) } previous = cur } return chars } } 

这会给你以下结果:

 "👌🏿".isSingleEmoji // true "🙎🏼‍♂️".isSingleEmoji // true "👨‍👩‍👧‍👧".isSingleEmoji // true "👨‍👩‍👧‍👧".containsOnlyEmoji // true "Hello 👨‍👩‍👧‍👧".containsOnlyEmoji // false "Hello 👨‍👩‍👧‍👧".containsEmoji // true "👫 Héllo 👨‍👩‍👧‍👧".emojiString // "👫👨‍👩‍👧‍👧" "👨‍👩‍👧‍👧".glyphCount // 1 "👨‍👩‍👧‍👧".characters.count // 4 "👫 Héllœ 👨‍👩‍👧‍👧".emojiScalars // [128107, 128104, 8205, 128105, 8205, 128103, 8205, 128103] "👫 Héllœ 👨‍👩‍👧‍👧".emojis // ["👫", "👨‍👩‍👧‍👧"] "👫👨‍👩‍👧‍👧👨‍👨‍👦".isSingleEmoji // false "👫👨‍👩‍👧‍👧👨‍👨‍👦".containsOnlyEmoji // true "👫👨‍👩‍👧‍👧👨‍👨‍👦".glyphCount // 3 "👫👨‍👩‍👧‍👧👨‍👨‍👦".characters.count // 8 

最简单,最干净,最快捷的方法是简单地检查string中每个字符的Unicode代码点是否与已知的emoji和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 } } 
 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 } } 

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

Swift 3注意:

看起来cnui_containsEmojiCharacters方法已被删除或移动到不同的dynamic库。 _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,它暴露了检测string是否包含Emoji字符的function:

 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 

使用纯Swiftstring时,必须在使用valueForKey之前将stringAnyObjectAnyObject

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

在NSString头文件中find的方法。

您可以使用此代码示例或此窗格 。

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

 #import "NSString+EMOEmoji.h" 

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

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

我用上面的代码创build了一个小例子项目。

对于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; } 

你可以像这样使用NSString-RemoveEmoji :

 if string.isIncludingEmoji { } 

我有同样的问题,并最终作出StringCharacter扩展。

代码太长,因为它实际上列出了一个CharacterSet中的所有emojis(来自官方unicode列表v5.0),你可以在这里find它:

https://github.com/piterwilson/StringEmoji

常量

让emojiCharacterSet:CharacterSet

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

var isEmoji:Bool {get}

String实例是否表示已知的单个表情符号字符

 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:String {get}

在String的副本上应用kCFStringTransformToUnicodeNameCFStringTransform

 print("á".unicodeName) // \N{LATIN SMALL LETTER A WITH ACUTE} print("😜".unicodeName) // "\N{FACE WITH STUCK-OUT TONGUE AND WINKING EYE}" 

var niceUnicodeName:String {get}

返回kCFStringTransformToUnicodeName的结果 – 带有\N{前缀和}后缀的CFStringTransform

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

字符

var isEmoji:Bool {get}

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

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