检测用户是否在UITextView中键入了表情符号字符

我有一个UITextView,我需要检测用户是否input表情符号。

我认为只要检查最新字符的unicode值就足够了,但是新的emoji 2s,一些字符散布在整个unicode索引(即苹果新devise的版权和注册标志)中。

也许是用NSLocale或LocalizedString值检查字符的语言?

有谁知道一个好的解决scheme?

谢谢!

多年来,这些表情符号检测解决scheme一直在打破,因为苹果公司增加了新的表情符号/新方法(如通过额外angular色预先制作皮肤色调的表情符号)等。

我终于打破了,只是写了下面的方法,适用于所有当前的表情符号,并应为所有未来的表情符号。

该解决scheme创build一个字符和黑色背景的UILabel。 CG然后拍摄标签的快照,并且扫描快照中的所有像素以获得任何非纯黑像素。 我添加黑色背景的原因是为了避免由于子像素渲染造成的错误着色问题

该解决scheme在我的设备上运行速度非常快,我可以每秒钟检查数百个字符,但是应该注意的是,这是一个CoreGraphics解决scheme,不应像常规文本方法那样大量使用。 graphics处理数据繁重,因此一次检查数千个字符可能会导致明显的滞后。

-(BOOL)isEmoji:(NSString *)character { UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; characterRender.text = character; 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; 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]; b /= 255.0f; if (b > 0) { colorPixelFound = YES; } x++; } x=0; y++; } return colorPixelFound; } 

另一个解决scheme: https : //github.com/woxtu/NSString-RemooveEmoji

然后,在导入这个扩展名后,你可以像这样使用它:

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { // Detect if an Emoji is in the string "text" if(text.isIncludingEmoji) { // Show an UIAlertView, or whatever you want here return NO; } return YES; } 

希望有所帮助;)

首先让我们谈谈你的“55357方法”为什么它适用于许多表情符号。

在Cocoa中, NSStringunichar的集合, unichar只是unsigned short一个types,与UInt16相同。 由于UInt16的最大值是0xffff ,这就排除了很多表情符号,因为能够放入一个unichar ,因为表情符号的六个主要的Unicode块中只有两个属于这个范围:

  • 杂项符号 (U + 2600-U + 26FF)
  • 装饰 (U + 2700-U + 27BF)

这些块包含113个表情符号,而另外66个表情符号可以表示为一个单独的单位,可以发现分布在各种其他块。 然而,这179个字符只代表了1126个表情符号基本字符的一小部分,其余部分必须由一个以上的unichar

我们来分析一下你的代码:

 unichar unicodevalue = [text characterAtIndex:0]; 

发生什么事情是,你只是简单地取第一个unicharstring,虽然这适用于前面提到的179个字符,但是当你遇到一个UTF-32字符的时候,它会分解,因为NSString将所有的东西都转换成UTF-16编码。 转换的工作原理是用代理对代替UTF-32值 ,这意味着NSString现在包含两个unichar

现在我们来看看为什么数字55357或0xd83d出现在许多表情符号上:当你只看到一个UTF-32字符的第一个UTF-16值时,你会得到高代理,每个代理都有一个跨度1024低代理人。 高0xd83d的范围是U + 1F400-U + 1F7FF,它从最大的表情符号块( 杂项符号和象形文字 (U + 1F300-U + 1F5FF))的中间开始,一直延续到几何形状扩展 (U + 1F780-U + 1F7FF) – 包含总共563个表情符号和333个非表情符字符。

所以,一个令人印象深刻的表情符号基本人物的50%具有高代理0xd83d ,但这些演绎方法仍然留下384个表情符号字符未处理,同时给予至less同样多的误报。


那么,如何检测一个angular色是否是表情符号呢?

我最近用Swift实现了一个相关的问题 ,如果你愿意的话,你可以看看在这个框架中如何检测表情符号,这是我为了用自定义图像replace标准表情符而创build的。

无论如何,你可以做的是从字符中提取UTF-32的代码点,我们将按照规范来做:

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { // Get the UTF-16 representation of the text. unsigned long length = text.length; unichar buffer[length]; [text getCharacters:buffer]; // Initialize array to hold our UTF-32 values. NSMutableArray *array = [[NSMutableArray alloc] init]; // Temporary stores for the UTF-32 and UTF-16 values. UTF32Char utf32 = 0; UTF16Char h16 = 0, l16 = 0; for (int i = 0; i < length; i++) { unichar surrogate = buffer[i]; // High surrogate. if (0xd800 <= surrogate && surrogate <= 0xd83f) { h16 = surrogate; continue; } // Low surrogate. else if (0xdc00 <= surrogate && surrogate <= 0xdfff) { l16 = surrogate; // Convert surrogate pair to UTF-32 encoding. utf32 = ((h16 - 0xd800) << 10) + (l16 - 0xdc00) + 0x10000; } // Normal UTF-16. else { utf32 = surrogate; } // Add UTF-32 value to array. [array addObject:[NSNumber numberWithUnsignedInteger:utf32]]; } NSLog(@"%@ contains values:", text); for (int i = 0; i < array.count; i++) { UTF32Char character = (UTF32Char)[[array objectAtIndex:i] unsignedIntegerValue]; NSLog(@"\t- U+%x", character); } return YES; } 

UITextView键入“😎”将其写入控制台:

 😎 contains values: - U+1f60e 

有了这个逻辑,只要比较character的值与表情符号代码点的数据源,你就会知道该字符是否是表情符号。


PS

有几个“不可见”的字符,即Variation Selectors和零宽度的join者 ,也应该被处理,所以我build议学习他们的行为。

如果你不想让你的键盘显示表情符号,你可以使用YOURTEXTFIELD/YOURTEXTVIEW.keyboardType = .ASCIICapable
这将显示一个没有表情符号的键盘

那么你可以检测它是否只有ascii字符使用这个:

 [myString canBeConvertedToEncoding:NSASCIIStringEncoding]; 

如果失败(或者有表情符号),它会说不。 然后,你可以做一个if else语句,不允许他们点击input或其他东西。

表情符号的长度是2,所以在shouldChangeTextInRange方法中检查string长度是否为2:在键盘击中每个键之后调用

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { // Detect if an Emoji is in the string "text" if([text length]==2) { // Show an UIAlertView, or whatever you want here return YES; } else { return NO; } }