看看字符串里的字符是不是表情符号?

我需要知道字符串中的字符是否是表情符号。

例如,我有这个字符:

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

我需要知道这个字是不是表情符号。

81319 次浏览

你可以这样使用 NSString-Removeemoji:

if string.isIncludingEmoji {


}

您可以使用这个代码 例子或这个 豆荚

要在 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.

实现这一点的最简单、最干净和 最快的的方法是根据已知的 emoji 和 dingbat 范围检查字符串中每个字符的 Unicode 代码点,如下所示:

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
}


}

Swift 3注:

It appears the cnui_containsEmojiCharacters method has either been removed or moved to a different dynamic library. _containsEmoji should still work though.

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

斯威夫特2. x:

I recently discovered a private API on NSString which exposes functionality for detecting if a string contains an Emoji character:

let str: NSString = "hello😊"

使用 Objecc 协议和 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 字符串,在使用 valueForKey之前必须将字符串强制转换为 AnyObject:

let str = "hello😊"


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

NSString 头文件中找到的方法。

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

这是我的修复程序,带有更新的范围。

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

For example, the glyph 👨‍👨‍👧‍👧 consists of 7 unicode scalars:

  • 四个表情符号:
  • 在每个表情符号之间是一个特殊的字符,其工作原理类似于字符胶水; 见 the specs for more info

另一个例子是,标志符号由两个 unicode 标量组成:

  • 普通的表情符号:
  • A skin tone modifier: 🏿

最后一个符号1 something 包含三个 Unicode 字符:

所以在渲染字符时,最终的字形非常重要。

Swift 5.0及以上版本使这个过程变得更加简单,并且摆脱了我们需要做的一些猜测工作。Unicode.Scalar的新 Property类型有助于确定我们正在处理什么。 但是,这些属性只有在检查标志符号中的其他标量时才有意义。这就是为什么我们要在字符类中添加一些方便的方法来帮助我们解决这个问题。

详情请看 我写了一篇文章解释这是如何运作的

对于 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.

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

我也有同样的问题,结果做了一个 StringCharacter的扩展。

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

https://github.com/piterwilson/StringEmoji

常数

Let emoji 字符集: 字符集

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

绳子

Var isEmoji: Bool { get }

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

print("".isEmoji) // false
print("😁".isEmoji) // true
print("😁😜".isEmoji) // false (String is not a single Emoji)
Var 包含 emoji: Bool { get }

String实例是否包含已知的表情符号字符

print("".containsEmoji) // false
print("😁".containsEmoji) // true
print("😁😜".containsEmoji) // true
Var unicodeName: String { get }

对 String 的副本应用 kCFStringTransformToUnicodeName-CFStringTransform

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

返回删除了 \N{前缀和 }后缀的 kCFStringTransformToUnicodeName-CFStringTransform的结果

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

性格

Var isEmoji: Bool { get }

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

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

未来证明: 手动检查字符的像素; 其他解决方案将打破(并已打破) ,作为新的表情符号添加。

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

多年来,这些表情符号检测解决方案不断打破苹果增加新的表情符号 w/新的方法(像皮肤色调的表情符号建立预先诅咒一个字符与一个额外的字符) ,等等。

我最终崩溃了,只是写了下面的方法,适用于所有目前的表情符号,应该适用于所有未来的表情符号。

该解决方案创建一个带有字符和黑色背景的 UILabel。然后 CG 获取标签的快照,我扫描快照中的所有像素,寻找任何非纯黑像素。我添加黑色背景的原因是为了避免由于 子像素渲染造成的错误着色问题

这个解决方案在我的设备上运行非常快,我可以每秒检查数百个字符,但是应该注意的是,这是一个 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;
    

}

和我之前写的答案完全一样,只不过更新了一套表情符号。

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


}

Swift 5.0

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

你必须把你的 String分解成它的 Scalars。每个 Scalar都有一个支持 isEmoji值的 Property值!

实际上,你甚至可以检查 Scalar 是否是一个或更多的表情符号修饰符

你可能需要考虑检查 isEmojiPresentation而不是 isEmoji,因为苹果对 isEmoji有以下说明:

对于默认呈现为表情符号的标量以及后跟 U + FE 0F VARIATION SELECTOR-16的具有非默认表情符号呈现的标量,此属性是正确的。这包括一些通常不被认为是表情符号的标量。


这种方法实际上将表情符号分解为所有的修饰符,但是处理起来更简单。斯威夫特现在把带有修饰符的表情符号(例如: ,)统计为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 指出了一个获得所有表情符号的有趣方法:

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

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

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

This solution should be much more future proof than the regex solutions offered here.

extension String {
func containsEmoji() -> Bool {
contains { $0.isEmoji }
}


func containsOnlyEmojis() -> Bool {
return count > 0 && !contains { !$0.isEmoji }
}
}


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️⃣. 0x203C 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 0x203C has an emoji modifier attached
var isEmoji: Bool {
guard let scalar = unicodeScalars.first else { return false }
return scalar.properties.isEmoji && (scalar.value >= 0x203C || unicodeScalars.count > 1)
}
}

给我们

"hey".containsEmoji() //false


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


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

对于上面提到的任务,有一个很好的 解决方案。但检查 Unicode。标量。Unicode 标量的属性适用于单个字符。对弦来说不够灵活。

我们可以使用正则表达式代替 ーー更通用的方法。下面将详细介绍它是如何工作的。解决办法来了。

解决方案

在 Swift 中,你可以检查一个字符串是否是一个单独的表情符号字符,使用一个具有这样一个计算属性的扩展名:

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
}


}

它是如何工作的(详细说明)

A single Emoji (a glyph) can be reproduced by a number of different symbols, sequences and their combinations. Unicode 规范 定义了几种可能的表情符号字符表示形式。

Single-Character Emoji

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

Unicode 将表情符号定义为:

emoji_character := \p{Emoji}

But it doesn’t necessarily mean that such a character will be drawn as an Emoji. An ordinary numeric symbol “1” has Emoji property being true, though it still might be drawn as text. And there is a list of such symbols: #, ©, 4, etc.

人们应该想到,我们可以使用额外的属性来检查: “表情符号呈现”。但事情不是这样的。有一个表情符号像或者,它有属性表情符号呈现 = 假。

为了确保这个字符在默认情况下是表情符号,我们应该检查它的类别: 它应该是“其他 _ 符号”。

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

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}

表情符号键帽序列

The sequence looks very alike with Presentation sequence, but it has additional scalar at the end: \x{20E3}. The scope of possible base scalars used for it is rather narrow: 0-9#* — and that’s all. Examples: 1️⃣, 8️⃣, *️⃣.

Unicode 定义 keycap 序列如下:

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

它的正则表达式:

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

Emoji Modifier Sequence

一些表情符号可以改变外观,比如肤色。例如,表情符号可以是不同的: . To def一个表情符号,在这个例子中被称为“表情符号 _ 修饰符 _ 基础”,人们可以使用后续的“表情符号 _ 修饰符”。

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

emoji_modifier_sequence := emoji_modifier_base emoji_modifier

为了检测它,我们可以搜索一个正则表达式序列:

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

Emoji Flag Sequence

标志是带有特定结构的表情符号。每个标志用两个“区域指示器”符号表示。

Unicode 对它们的定义如下:

emoji_flag_sequence := regional_indicator regional_indicator

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

Regular expression for it:

emoji_flag_sequence := \p{RI}{2}

表情符号标签序列(ETS)

一种序列,它使用所谓的 tag _ base,后面跟着一个由符号范围 x { E0020}-x { E007E }组成的自定义标记规范,由 tag _ end mark 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 允许标签基于表情符号修饰符序列或者表情符号呈现序列。但同时,在同一个 documentation提供的正则表达式中,他们似乎只根据一个表情符号来检查序列。

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

正则表达式:

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

表情零宽连字序列(ZWJ 序列)

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

举个例子,一个“有父亲,儿子和女儿的家庭”表情符号是由父亲,女儿和儿子的表情符号与 ZWJ 符号粘合在一起而复制的。

它允许粘贴在一起的元素,这是单一的表情符号字符,表示和修饰符序列。

Regular expression for such sequence in general looks like this:

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

所有的正则表达式

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

\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}]
)
)*

Use the following extensions,

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

原答案由 参考文献提供。

原生一行代码

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

Works from Swift 5.0

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

Swift 5 解决方案使用 标量,它可以处理文本、笑脸、心形表情符号等等

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

isEmoji -布尔值,指示标量是否有一个 emoji 表示,是否是默认值。

IsemojiPresation -一个布尔值,指示标量是否应该用 emoji 表示呈现, 而不是文本表示,默认情况下。

从这些定义可以看出,我们不能仅仅在字符串的标量上使用 isEmoji或者 isEmojiPresentation-这不能告诉我们这个标量是否是 really一个表情符号

Luckily Apple gave us a clue:

单独在一个标量上测试 isEmoji不足以 确定一个文本单元是否作为表情符号呈现; 正确的测试 需要检查 Character中的多个标量 检查基本标量是否有 isEmoji == true,您还必须 检查其默认表示(见 isEmojiPresentation)和 确定它后面是否跟着一个变异选择器,这个变异选择器将 modify the presentation.

最后,这里是我的实现,它可以处理数字、笑脸、文本和厄瓜多尔符号:

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