david2391
david2391

Reputation: 320

Check if there is an emoji contained in a string

I am getting the text size of a string with this

textSize = [[tempDict valueForKeyPath:@"caption.text"] sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:CGSizeMake(280, CGFLOAT_MAX) lineBreakMode: NSLineBreakByWordWrapping];

The only problem I have is that if the string only contains an emoji, my app crashes. Is there an easy way to check for emojis or do I have to create an array with all possible emojis and then check for them using that?

error:

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

Upvotes: 11

Views: 16031

Answers (11)

Bill Chan
Bill Chan

Reputation: 3455

Simple Swift solution with checking every scalar in unicodeScalars are in the set CharacterSet.symbols

extension String {
    var containsEmoji: Bool {
        for scalar in unicodeScalars {
            if CharacterSet.symbols.contains(scalar) {
                return true
            }
        }

        return false
    }
}

But I found some emoji 1.0 items like ā„¹ļø is not classify as an emoji. So I create this checker:

extension Unicode.Scalar {
    extension Unicode.Scalar {
        var isEmojiMiscSymbol: Bool {
            switch self.value {
            case 0x2030...0x329F:   // Misc symbols
                return true
            default:
                return false
            }
        }
    }
}

And this is the checker which can detect ā„¹ļø:

extension String {
    var containsEmoji: Bool {
        for scalar in unicodeScalars {
            if CharacterSet.symbols.contains(scalar) {
                return true
            } else if scalar.isEmojiMiscSymbol {
                return true
            }
        }

        return false
    }
}

Upvotes: 0

Shyantanu
Shyantanu

Reputation: 681

it's too late reply into this but it's may help someone.

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

Upvotes: 1

Jeff Wolski
Jeff Wolski

Reputation: 6372

This issue may occur if there are any characters past 0x00ff. In other words, there are many Unicode characters in addition to Emoji that you may need to account for. In order to see if there are any Unicode characters (beyond the boundary of extended ASCII) use the following.

extension String {

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

}

Upvotes: 1

Albert Renshaw
Albert Renshaw

Reputation: 17882

As you've probably noticed, all of these emoji detecting methods break almost anytime Apple adds new emojis.

I've created a CG scanning solution which should work for all current and all FUTURE emojis here: https://stackoverflow.com/a/14472163/2057171

To my knowledge it's the only actual answer to this issue posted anywhere online.


-(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;

}

You can use it like so:

NSString *myString = @"Hello this contains šŸ˜‡ an emoji";

BOOL containsEmoji = [self isEmoji:myString];
if (containsEmoji) {
    NSLog(@"Contained Emoji");
} else {
    NSLog(@"Did not contain Emoji");
}

Upvotes: -1

Annie Gupta
Annie Gupta

Reputation: 2822

Now with the new release of iOS 10, for the following emojis it doesn't work:

Emojis

The following code snippet is tried and tested for both iOS 9 and 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
    }
}

Create a String extension in your app as mentioned above.

And can be used like this:

if string.containsEmoji {
    // Do operations here
}

Upvotes: 0

Andres Canella
Andres Canella

Reputation: 3716

This is what I'm using:

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
}

I took this which was missing some emoji ranges, and then used this emoji array to find missing rantes by iteration... Have not deep tested.

Upvotes: 3

Gabriel.Massana
Gabriel.Massana

Reputation: 8225

@Simha.IC answer is great.

However it does not work with the new iOS 9.1 emojis'.

Simha.IC snippet is not detecting these ones:
šŸ¤šŸ¤‘šŸ¤’šŸ¤“šŸ¤”šŸ¤•šŸ¤–šŸ¤—šŸ¤˜šŸ¦€šŸ¦šŸ¦‚šŸ¦ƒšŸ¦„šŸ§€ā˜‚ļøāœļøāœ”ļøā˜Æļø

So to solve the problem I tweaked a little bit the code and created a category with it:

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

My category can calculate:

  • If the string has emojis
  • The number of emojis
  • The range for every emoji

I created a small project with the category as an example.

I also created a pod with the category maybe you want to try it.

Upvotes: 9

gnasher729
gnasher729

Reputation: 52530

Again and again people use valueForKeyPath instead of objectForKey. None of them can ever explain why. Read the documentation. If after reading it you can explain why you are using valueForKeyPath (and "I copied it from somewhere" is not an explanation), change it to objectForKey.

The problem you have has nothing to do with Emojis at all. Any attempt to detect Emojis in the string will fail - for the simple reason that you don't have a string in the first place, you have [NSNull null]. The problem might be fixed by using objectForKey - you might get nil instead which behaves a lot more forgiving. Or you still get [NSNull null].

Find out why you are getting [NSNull null]. Somebody puts it there. If you can't prevent it from being there, then you need to handle it.

Upvotes: 0

DHEERAJ
DHEERAJ

Reputation: 1468

You can use this new framework

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

Usage:

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

else
{
  NSLog(@"String Doesn't Contains Emoji");
}

Upvotes: 1

Loquatious
Loquatious

Reputation: 1775

try this code:

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

Upvotes: 11

alex_izh
alex_izh

Reputation: 527

Please, see this answer How to get NSString size when NSString includes emojis?

If you have only emojis then you shouldn't use sizeWithFont. Use label for it or something else.

NSString doesn't work with Emojis, only text. If you have emojis, then you should use [label sizeToFit] or [label sizeThatFits:]

Also, you can use this code:

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

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

Because, you get NSNull from dictionary, but it doesn't equal nil, and your condition works.

Upvotes: 0

Related Questions