Add attributes to all emoji in an NSAttributedString?

1k views Asked by At

The font I'm using in my iOS app has an unfortunate quality: its characters are unusually small, and as a result, if a user types in a string which includes emoji (or possibly other characters not included in the font? Haven't tested that), when iOS draws those glyphs in the AppleColorEmoji font they come out huge relative to the other glyphs.

This is of course complicated by the fact that emoji are "two-part" glyphs so I can't just do a simple for-each-character-in-the-string loop.

What I need is a method along the lines of -(NSAttributedString *)attributedStringByAddingAttributes:(NSArray *)attrs toString:(NSString*)myString forCharactersNotInFont:(UIFont *)font

... Or failing that, at least -(NSAttributedString *)attributedStringByAddingAttributes:(NSArray *)attrs toStringForEmoji:(NSString*)myString

...or something.

Not sure of the best way to do this.

1

There are 1 answers

0
DanM On

Code I ended up with, using code adapted from here:

- (BOOL)isEmoji:(NSString *)str {
    const unichar high = [str characterAtIndex: 0];

    // Surrogate pair (U+1D000-1F77F)
    if (0xd800 <= high && high <= 0xdbff) {
        const unichar low = [str characterAtIndex: 1];
        const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000;

        return (0x1d000 <= codepoint && codepoint <= 0x1f77f);

        // Not surrogate pair (U+2100-27BF)
    } else {
        return (0x2100 <= high && high <= 0x27bf);
    }
}

// The following takes a string s and returns an attributed string where all the "special characters" contain the provided attributes

- (NSAttributedString *)attributedStringForString:(NSString *)s withAttributesForEmoji:(NSDictionary *)attrs {

    NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:@""];

    NSRange fullRange = NSMakeRange(0, [s length]);

    [s enumerateSubstringsInRange:fullRange
                          options:NSStringEnumerationByComposedCharacterSequences
                       usingBlock:^(NSString *substring, NSRange substringRange,
                                    NSRange enclosingRange, BOOL *stop)
    {
        if ([self isEmoji:substring]) {
            [as appendAttributedString:[[NSAttributedString alloc] initWithString:substring
                                                                       attributes:attrs]];
        } else {
            [as appendAttributedString:[[NSAttributedString alloc] initWithString:substring]];
        }
    }];
    return as;
}

So far, seems to work quite nicely!