Проверить, есть ли в строке эмодзи

Я получаю размер текста строки с этим

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

person david2391    schedule 10.11.2013    source источник
comment
Что за авария? Вы должны устранить сбой, а не проверять смайлики.   -  person rmaddy    schedule 10.11.2013
comment
Сбой происходит из-за того, что когда я вызываю sizeWithFont, нет текста для получения размера. Или sizeWithFont работает, если есть только смайлики и нет текста?   -  person david2391    schedule 10.11.2013
comment
Пока вы не предоставите подробную информацию о сбое (например, полное сообщение об ошибке и соответствующий код), узнать об этом невозможно.   -  person rmaddy    schedule 10.11.2013
comment
Я получаю сообщение об ошибке -[NSNull sizeWithFont:constrainedToSize:lineBreakMode:]: нераспознанный селектор отправлен экземпляру 0x3aa88a60   -  person david2391    schedule 10.11.2013
comment
sizeWithFonts должен работать с эмодзи, потому что эмодзи — это некоторая последовательность символов, вы можете прочитать об этом на вики.   -  person alex_izh    schedule 10.11.2013
comment
tempDict возвращает NSNull, потому что не найдено значение для пути caption.text   -  person alex_izh    schedule 10.11.2013
comment
Ну, он работает нормально, пока я не доберусь до поста только с смайликами / без текста. 14 до этого все работает нормально   -  person david2391    schedule 10.11.2013
comment
Вот что вызывает сбой text = \Ud83d\Ude4c;   -  person david2391    schedule 10.11.2013
comment
Можете ли вы добавить код? Какое значение должно быть в словаре?   -  person alex_izh    schedule 10.11.2013
comment
Это не имеет никакого отношения к эмодзи. Это не имеет ничего общего с вызовом sizeWithFont:. Это просто связано с тем, что NSNull возвращается из вызова valueForKeyPath: вместо ожидаемого NSString.   -  person rmaddy    schedule 10.11.2013
comment
но прямо перед тем, как произойдет сбой, я регистрирую [tempDict valueForKeyPath:@caption] и вижу текстовый ключ. Он показывает: текст = \Ud83d\Ude4c;   -  person david2391    schedule 10.11.2013
comment
Разделите свой код: NSString *str = [tempDict valueForKeyPath:@"caption.text"]; textSize = [str sizeWithFont.... Вы обнаружите, что str это NSNull, а не NSString в том случае, когда у вас возникла проблема.   -  person rmaddy    schedule 10.11.2013


Ответы (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;
}
person Simha.IC    schedule 09.04.2014

Ответ @Simha.IC отличный.

Однако это не работает с новыми смайликами 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;
}

Моя категория может рассчитать:

  • Если в строке есть эмодзи
  • Количество смайликов
  • Диапазон для каждого эмодзи

Я создал небольшой проект с категорией в качестве примера.

Я также создал модуль с категорией, возможно, вы захотите попробовать.

person Gabriel.Massana    schedule 07.01.2016
comment
Я проголосовал за то, что вы сказали об ошибке, и вы это исправили. - person user3182143; 17.02.2017

Это то, что я использую:

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
}

Я взял это в котором отсутствовали некоторые диапазоны эмодзи, а затем использовался этот массив эмодзи, чтобы найти отсутствующие ранты по итерации... Не глубоко проверено.

person Andres Canella    schedule 14.07.2016

Вы можете использовать эту новую структуру

https://github.com/woxtu/NSString-RemoveEmoji

Применение:

if (string.isIncludingEmoji) 
{
   NSLog(@"String Contains Emoji");
}

else
{
  NSLog(@"String Doesn't Contains Emoji");
}
person DHEERAJ    schedule 21.08.2015
comment
В iOS 9.1 добавлено больше смайликов, которые вышеупомянутый метод не распознает (особенно эти: ИСПРАВЛЕНИЕ: замените return (0x1d000 <= codepoint && codepoint <= 0x1f77f); в методе isEmoji на return (0x1d000 <= codepoint && codepoint <= 0x1f77f) || (0x1F900 <= codepoint && codepoint <=0x1f9ff); - person Vahan; 04.11.2015
comment
@Vahan Что делать, если я хочу определить, состоит ли строка только из 1 смайлика (с учетом оттенков кожи и т. Д.)? - person Joe; 11.11.2015
comment
@JosephK, просто проверьте, равна ли длина строки 1, а isIncludingEmoji - ДА. - person Vahan; 11.11.2015
comment
@Vahan длина может быть 2, если пользователь применяет обычный смайлик, иначе длина равна 4, если смайлик имеет оттенок кожи. (модификатор эмодзи) - person Joe; 11.11.2015
comment
@JosephK да, вы правы, обычная длина смайликов - 2, это плохо. Нужно подумать о цвете кожи. Я буду держать вас в курсе. - person Vahan; 11.11.2015

Эта проблема может возникнуть, если после 0x00ff есть какие-либо символы. Другими словами, в дополнение к эмодзи есть много символов Unicode, которые вам, возможно, придется учитывать. Чтобы увидеть, есть ли какие-либо символы Unicode (за пределами расширенного ASCII), используйте следующее.

extension String {

  var containsUnicodeCharacters: Bool {
    for scalar in unicodeScalars {
      if scalar.value > 0x00FF {
        return true
      }
    }
    return false
  }

}
person Jeff Wolski    schedule 02.03.2017

слишком поздно отвечать на это, но это может помочь кому-то.

- (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;
}
person Shyantanu    schedule 23.07.2018

Пожалуйста, посмотрите этот ответ Как получить размер NSString, когда NSString включает смайлики ?

Если у вас есть только смайлики, вам не следует использовать sizeWithFont. Используйте ярлык для этого или что-то еще.

NSString не работает с эмодзи, только с текстом. Если у вас есть смайлики, используйте [label sizeToFit] или [label sizeThatFits:].

Кроме того, вы можете использовать этот код:

 id text = [tempDict valueForKeyPath:@"caption.text"]

 if (![text isKindOfClass:[NSNull class]]) {
 ...
 }

Потому что вы получаете NSNull из словаря, но он не равен нулю, и ваше условие работает.

person alex_izh    schedule 10.11.2013
comment
Это был мой первоначальный вопрос, потому что я не знаю, будет ли строка содержать только смайлики или нет. ссылку проверю - person david2391; 10.11.2013
comment
Но у него есть не только смайлики. У него есть объект NSNull. - person rmaddy; 10.11.2013
comment
Вам помог мой ответ? - person alex_izh; 10.11.2013
comment
В строке нет эмодзи, потому что строки нет. Итак, вопрос в том, является ли этот ответ чем-то, что вы проверили, или просто предположением. - person gnasher729; 19.01.2016

Снова и снова люди используют valueForKeyPath вместо objectForKey. Никто из них никогда не может объяснить, почему. Прочтите документацию. Если после прочтения вы можете объяснить, почему вы используете valueForKeyPath (и «я откуда-то скопировал» — это не объяснение), измените его на objectForKey.

Проблема, с которой вы столкнулись, вообще не имеет ничего общего с эмодзи. Любая попытка обнаружить смайлики в строке потерпит неудачу — по той простой причине, что у вас вообще нет строки, у вас есть [NSNull null]. Проблема может быть решена с помощью objectForKey — вместо этого вы можете получить nil, который ведет себя гораздо более щадяще. Или вы все еще получаете [NSNull null].

Узнайте, почему вы получаете [NSNull null]. Кто-то ставит его туда. Если вы не можете предотвратить его появление, то вам нужно справиться с этим.

person gnasher729    schedule 19.01.2016

Теперь, с новой версией 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
}
person Annie Gupta    schedule 10.10.2016

Простое решение 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
    }
}
person Bill Chan    schedule 21.09.2018

Как вы, наверное, заметили, все эти методы обнаружения смайликов ломаются почти каждый раз, когда Apple добавляет новые смайлики.

Я создал решение для сканирования CG, которое должно работать для всех текущих и всех БУДУЩИХ смайликов здесь: https://stackoverflow.com/a/14472163/2057171

Насколько мне известно, это единственный фактический ответ на этот вопрос, размещенный где-либо в Интернете.


-(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");
}
person Albert Renshaw    schedule 23.02.2017