Проверьте, есть ли в строке эмодзи
Я получаю размер текста строки с этим
textSize = [[tempDict valueForKeyPath:@"caption.text"] sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:CGSizeMake(280, CGFLOAT_MAX) lineBreakMode: NSLineBreakByWordWrapping];
Единственная проблема, которую я имею, состоит в том, что, если строка содержит только эмодзи, мое приложение вылетает. Есть ли простой способ проверить наличие смайликов или мне нужно создать массив со всеми возможными смайликами, а затем проверить их с помощью этого?
ошибка:
-[NSNull sizeWithFont:constrainedToSize:lineBreakMode:]: unrecognized selector sent to instance 0x3aa88a60
if ([tempDict valueForKeyPath:@"caption.text"]){
NSLog(@"%@", [tempDict valueForKeyPath:@"caption"]);
//Measure the message label box height
textSize = [[tempDict valueForKeyPath:@"caption.text"] sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:CGSizeMake(280, CGFLOAT_MAX) lineBreakMode: NSLineBreakByWordWrapping];
int height = 320 + 20 + textSize.height;
[cellHeight addObject:[NSNumber numberWithInt:height]];
}
11 ответов
Попробуйте этот код:
- (BOOL)stringContainsEmoji:(NSString *)string {
__block BOOL returnValue = NO;
[string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:
^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
const unichar hs = [substring characterAtIndex:0];
// surrogate pair
if (0xd800 <= hs && hs <= 0xdbff) {
if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f) {
returnValue = YES;
}
}
} else if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
if (ls == 0x20e3) {
returnValue = YES;
}
} else {
// non surrogate
if (0x2100 <= hs && hs <= 0x27ff) {
returnValue = YES;
} else if (0x2B05 <= hs && hs <= 0x2b07) {
returnValue = YES;
} else if (0x2934 <= hs && hs <= 0x2935) {
returnValue = YES;
} else if (0x3297 <= hs && hs <= 0x3299) {
returnValue = YES;
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {
returnValue = YES;
}
}
}];
return returnValue;
}
@Simha.IC ответ отличный.
Однако это не работает с новым Emojis iOS 9.1.
Фрагмент Simha.IC не обнаруживает эти:
☂️✝️✡️☯️
Поэтому, чтобы решить проблему, я немного подправил код и создал с ним категорию:
- (BOOL)emo_containsEmoji
{
__block BOOL containsEmoji = NO;
[self enumerateSubstringsInRange:NSMakeRange(0,
[self length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring,
NSRange substringRange,
NSRange enclosingRange,
BOOL *stop)
{
const unichar hs = [substring characterAtIndex:0];
// surrogate pair
if (0xd800 <= hs &&
hs <= 0xdbff)
{
if (substring.length > 1)
{
const unichar ls = [substring characterAtIndex:1];
const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc &&
uc <= 0x1f9c0)
{
containsEmoji = YES;
}
}
}
else if (substring.length > 1)
{
const unichar ls = [substring characterAtIndex:1];
if (ls == 0x20e3 ||
ls == 0xfe0f ||
ls == 0xd83c)
{
containsEmoji = YES;
}
}
else
{
// non surrogate
if (0x2100 <= hs &&
hs <= 0x27ff)
{
containsEmoji = YES;
}
else if (0x2B05 <= hs &&
hs <= 0x2b07)
{
containsEmoji = YES;
}
else if (0x2934 <= hs &&
hs <= 0x2935)
{
containsEmoji = YES;
}
else if (0x3297 <= hs &&
hs <= 0x3299)
{
containsEmoji = YES;
}
else if (hs == 0xa9 ||
hs == 0xae ||
hs == 0x303d ||
hs == 0x3030 ||
hs == 0x2b55 ||
hs == 0x2b1c ||
hs == 0x2b1b ||
hs == 0x2b50)
{
containsEmoji = YES;
}
}
}];
return containsEmoji;
}
Моя категория может рассчитать:
- Если в строке есть смайлики
- Количество смайликов
- Диапазон для каждого смайлика
Я создал небольшой проект с категорией в качестве примера.
Я также создал пакет с категорией, может быть, вы хотите попробовать.
Это то, что я использую:
func isAllEmoji(aString: String) -> Bool {
for scalar in aString.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
0x0030...0x0039,
0x00A9...0x00AE,
0x203C...0x2049,
0x2122...0x3299,
0x1F004...0x1F251,
0x1F910...0x1F990:
break
default:
return false
}
}
return true
}
Я взял это, в котором отсутствовали некоторые диапазоны смайликов, а затем использовал этот массив смайликов, чтобы найти пропущенные повторения за итерацией... Не проверял подробно.
Вы можете использовать этот новый фреймворк
https://github.com/woxtu/NSString-RemoveEmoji
Использование:
if (string.isIncludingEmoji)
{
NSLog(@"String Contains Emoji");
}
else
{
NSLog(@"String Doesn't Contains Emoji");
}
Эта проблема может возникнуть, если есть какие-либо символы в прошлом 0x00ff
, Другими словами, в дополнение к эмодзи есть много символов Юникода, которые вам, возможно, придется учитывать. Чтобы увидеть, есть ли какие-либо символы Unicode (за пределами расширенного ASCII), используйте следующее.
extension String {
var containsUnicodeCharacters: Bool {
for scalar in unicodeScalars {
if scalar.value > 0x00FF {
return true
}
}
return false
}
}
Слишком поздно ответить на это, но это может кому-то помочь.
- (BOOL)stringContainsEmoji:(NSString *)string {
__block BOOL returnValue = NO;
[string enumerateSubstringsInRange:NSMakeRange(0, string.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString* substring, NSRange substringRange, NSRange enclosingRange, BOOL* stop)
{
const unichar hs = [substring characterAtIndex:0];
const unichar ls = substring.length > 1 ? [substring characterAtIndex:1] : 0;
#define IS_IN(val, min, max) (((val) >= (min)) && ((val) <= (max)))
if(IS_IN(hs, 0xD800, 0xDBFF))
{
if(substring.length > 1)
{
const int uc = ((hs - 0xD800) * 0x400) + (ls - 0xDC00) + 0x10000;
// Musical: [U+1D000, U+1D24F]
// Enclosed Alphanumeric Supplement: [U+1F100, U+1F1FF]
// Enclosed Ideographic Supplement: [U+1F200, U+1F2FF]
// Miscellaneous Symbols and Pictographs: [U+1F300, U+1F5FF]
// Supplemental Symbols and Pictographs: [U+1F900, U+1F9FF]
// Emoticons: [U+1F600, U+1F64F]
// Transport and Map Symbols: [U+1F680, U+1F6FF]
if(IS_IN(uc, 0x1D000, 0x1F9FF))
returnValue = YES;
}
}
else if(substring.length > 1 && ls == 0x20E3)
{
// emojis for numbers: number + modifier ls = U+20E3
returnValue = YES;
}
else
{
if( // Latin-1 Supplement
hs == 0x00A9 || hs == 0x00AE
// General Punctuation
|| hs == 0x203C || hs == 0x2049
// Letterlike Symbols
|| hs == 0x2122 || hs == 0x2139
// Arrows
|| IS_IN(hs, 0x2194, 0x2199) || IS_IN(hs, 0x21A9, 0x21AA)
// Miscellaneous Technical
|| IS_IN(hs, 0x231A, 0x231B) || IS_IN(hs, 0x23E9, 0x23F3) || IS_IN(hs, 0x23F8, 0x23FA) || hs == 0x2328 || hs == 0x23CF
// Geometric Shapes
|| IS_IN(hs, 0x25AA, 0x25AB) || IS_IN(hs, 0x25FB, 0x25FE) || hs == 0x25B6 || hs == 0x25C0
// Miscellaneous Symbols
|| IS_IN(hs, 0x2600, 0x2604) || IS_IN(hs, 0x2614, 0x2615) || IS_IN(hs, 0x2622, 0x2623) || IS_IN(hs, 0x262E, 0x262F)
|| IS_IN(hs, 0x2638, 0x263A) || IS_IN(hs, 0x2648, 0x2653) || IS_IN(hs, 0x2665, 0x2666) || IS_IN(hs, 0x2692, 0x2694)
|| IS_IN(hs, 0x2696, 0x2697) || IS_IN(hs, 0x269B, 0x269C) || IS_IN(hs, 0x26A0, 0x26A1) || IS_IN(hs, 0x26AA, 0x26AB)
|| IS_IN(hs, 0x26B0, 0x26B1) || IS_IN(hs, 0x26BD, 0x26BE) || IS_IN(hs, 0x26C4, 0x26C5) || IS_IN(hs, 0x26CE, 0x26CF)
|| IS_IN(hs, 0x26D3, 0x26D4) || IS_IN(hs, 0x26D3, 0x26D4) || IS_IN(hs, 0x26E9, 0x26EA) || IS_IN(hs, 0x26F0, 0x26F5)
|| IS_IN(hs, 0x26F7, 0x26FA)
|| hs == 0x260E || hs == 0x2611 || hs == 0x2618 || hs == 0x261D || hs == 0x2620 || hs == 0x2626 || hs == 0x262A
|| hs == 0x2660 || hs == 0x2663 || hs == 0x2668 || hs == 0x267B || hs == 0x267F || hs == 0x2699 || hs == 0x26C8
|| hs == 0x26D1 || hs == 0x26FD
// Dingbats
|| IS_IN(hs, 0x2708, 0x270D) || IS_IN(hs, 0x2733, 0x2734) || IS_IN(hs, 0x2753, 0x2755)
|| IS_IN(hs, 0x2763, 0x2764) || IS_IN(hs, 0x2795, 0x2797)
|| hs == 0x2702 || hs == 0x2705 || hs == 0x270F || hs == 0x2712 || hs == 0x2714 || hs == 0x2716 || hs == 0x271D
|| hs == 0x2721 || hs == 0x2728 || hs == 0x2744 || hs == 0x2747 || hs == 0x274C || hs == 0x274E || hs == 0x2757
|| hs == 0x27A1 || hs == 0x27B0 || hs == 0x27BF
// CJK Symbols and Punctuation
|| hs == 0x3030 || hs == 0x303D
// Enclosed CJK Letters and Months
|| hs == 0x3297 || hs == 0x3299
// Supplemental Arrows-B
|| IS_IN(hs, 0x2934, 0x2935)
// Miscellaneous Symbols and Arrows
|| IS_IN(hs, 0x2B05, 0x2B07) || IS_IN(hs, 0x2B1B, 0x2B1C) || hs == 0x2B50 || hs == 0x2B55
)
{
returnValue = YES;
}
}
#undef IS_IN
}];
return returnValue;
}
Простое решение Swift с проверкой каждого скаляра в unicodeScalars находится в наборе CharacterSet.symbols
extension String {
var containsEmoji: Bool {
for scalar in unicodeScalars {
if CharacterSet.symbols.contains(scalar) {
return true
}
}
return false
}
}
Но я обнаружил, что некоторые элементы эмодзи 1.0, такие как ℹ️, не классифицируются как эмодзи. Итак, я создаю эту проверку:
extension Unicode.Scalar {
extension Unicode.Scalar {
var isEmojiMiscSymbol: Bool {
switch self.value {
case 0x2030...0x329F: // Misc symbols
return true
default:
return false
}
}
}
}
И это средство проверки, которое может обнаружить ℹ️:
extension String {
var containsEmoji: Bool {
for scalar in unicodeScalars {
if CharacterSet.symbols.contains(scalar) {
return true
} else if scalar.isEmojiMiscSymbol {
return true
}
}
return false
}
}
Теперь с новым выпуском iOS 10, для следующих эмодзи это не работает:
Следующий фрагмент кода опробован и протестирован как для iOS 9, так и для iOS 10:
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
0x1F910...0x1F918, // New Emoticons
0x1F1E6...0x1F1FF, // Flags
0x1F980...0x1F984,
0x1F191...0x1F19A,
0x1F201...0x1F202,
0x1F232...0x1F23A,
0x1F250...0x1F251,
0x23E9...0x23F3,
0x23F8...0x23FA,
0x1F170...0x1F171,
0x1F17E,
0xA9,
0xAE,
0x2122,
0x2328,
0x3030,
0x1F0CF,
0x1F18E,
0x1F9C0:
return true
default:
continue
}
}
return false
}
}
Создайте расширение String в своем приложении, как указано выше.
И может быть использовано так:
if string.containsEmoji {
// Do operations here
}
Снова и снова люди используют valueForKeyPath вместо objectForKey. Никто из них не может объяснить почему. Прочитайте документацию. Если после прочтения вы можете объяснить, почему вы используете valueForKeyPath (а "Я скопировал его откуда-то" - это не объяснение), измените его на objectForKey.
Ваша проблема не имеет никакого отношения к Emojis вообще. Любая попытка обнаружить Emojis в строке потерпит неудачу - по той простой причине, что у вас нет строки в первую очередь, у вас есть [NSNull null]. Проблема может быть решена с помощью objectForKey - вместо этого вы можете получить nil, который ведет себя гораздо более снисходительно. Или вы все еще получаете [NSNull null].
Узнайте, почему вы получаете [NSNull null]. Кто-то кладет это туда. Если вы не можете помешать этому быть там, тогда вам нужно справиться с этим.
Пожалуйста, посмотрите этот ответ. Как получить размер NSString, если NSString включает эмодзи?
Если у вас есть только смайлики, то вы не должны использовать sizeWithFont
, Используйте ярлык для этого или что-то еще.
NSString не работает с Emojis, только текст. Если у вас есть смайлики, то вы должны использовать [label sizeToFit]
или же [label sizeThatFits:]
Также вы можете использовать этот код:
id text = [tempDict valueForKeyPath:@"caption.text"]
if (![text isKindOfClass:[NSNull class]]) {
...
}
Потому что вы получаете NSNull из словаря, но он не равен нулю, и ваше условие работает.
Как вы, наверное, заметили, все эти методы обнаружения смайликов ломаются почти каждый раз, когда Apple добавляет новые смайлики.
Я создал решение для сканирования компьютерной графики, которое должно работать со всеми текущими и всеми будущими смайлами здесь: /questions/4862338/opredelit-nabral-li-polzovatel-simvol-emoji-v-uitextview/4862361#4862361
Насколько мне известно, это единственный фактический ответ на этот вопрос, размещенный где-либо в Интернете.
-(BOOL)isEmoji:(NSString *)character {//argument can be character or entire string
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;
}
Вы можете использовать это так:
NSString *myString = @"Hello this contains an emoji";
BOOL containsEmoji = [self isEmoji:myString];
if (containsEmoji) {
NSLog(@"Contained Emoji");
} else {
NSLog(@"Did not contain Emoji");
}