Découvrez si le caractère dans la chaîne est emoji?

J'ai besoin de savoir si un caractère dans une chaîne est un emoji.

Par exemple, j'ai ce caractère:

let string = "
41
demandé sur ABakerSmith 2015-06-10 16:03:01

10 réponses

Ce que je suis tombé sur est la différence entre les caractères, les scalaires unicode et les glyphes.

Par exemple, le glyphe est constitué de 7 scalaires unicode:

Un autre exemple, le glyphe est constitué de 2 scalaires unicode:

  • les emoji réguliers:
  • un modificateur de tonalité de peau:

Donc, lors du rendu des caractères, les glyphes résultants comptent vraiment.

Ce que je cherchais un moyen de détecter si une chaîne est exactement et seulement un emoji. Donc, je pourrais le rendre plus grand que le texte normal(comme les Messages sur iOS10 et WhatsApp le font de nos jours). Comme décrit ci-dessus, le nombre de caractères est vraiment inutile. (Le "caractère de colle" n'est pas non plus considéré comme un emoji).

Ce que vous pouvez faire est d'utiliser CoreText pour vous aider à décomposer la chaîne en glyphes et les compter. En outre, je déplacerais une partie de l'extension proposée par Arnold et Sebastian Lopez vers une extension séparée de UnicodeScalar.

, Il vous laisse avec le résultat suivant:

import UIKit

extension UnicodeScalar {

    var isEmoji: Bool {

        switch value {
        case 0x1F600...0x1F64F, // Emoticons
            0x1F300...0x1F5FF, // Misc Symbols and Pictographs
            0x1F680...0x1F6FF, // Transport and Map
            0x1F1E6...0x1F1FF, // Regional country flags
            0x2600...0x26FF,   // Misc symbols
            0x2700...0x27BF,   // Dingbats
            0xFE00...0xFE0F,   // Variation Selectors
            0x1F900...0x1F9FF,  // Supplemental Symbols and Pictographs
            127000...127600, // Various asian characters
            65024...65039, // Variation selector
            9100...9300, // Misc items
            8400...8447: // Combining Diacritical Marks for Symbols
            return true

        default: return false
        }
    }

    var isZeroWidthJoiner: Bool {

        return value == 8205
    }
}

extension String {

    var glyphCount: Int {

        let richText = NSAttributedString(string: self)
        let line = CTLineCreateWithAttributedString(richText)
        return CTLineGetGlyphCount(line)
    }

    var isSingleEmoji: Bool {

        return glyphCount == 1 && containsEmoji
    }

    var containsEmoji: Bool {

        return unicodeScalars.contains { $0.isEmoji }
    }

    var containsOnlyEmoji: Bool {

        return !isEmpty
            && !unicodeScalars.contains(where: {
                !$0.isEmoji
                    && !$0.isZeroWidthJoiner
            })
    }

    // The next tricks are mostly to demonstrate how tricky it can be to determine emoji's
    // If anyone has suggestions how to improve this, please let me know
    var emojiString: String {

        return emojiScalars.map { String($0) }.reduce("", +)
    }

    var emojis: [String] {

        var scalars: [[UnicodeScalar]] = []
        var currentScalarSet: [UnicodeScalar] = []
        var previousScalar: UnicodeScalar?

        for scalar in emojiScalars {

            if let prev = previousScalar, !prev.isZeroWidthJoiner && !scalar.isZeroWidthJoiner {

                scalars.append(currentScalarSet)
                currentScalarSet = []
            }
            currentScalarSet.append(scalar)

            previousScalar = scalar
        }

        scalars.append(currentScalarSet)

        return scalars.map { $0.map{ String($0) } .reduce("", +) }
    }

    fileprivate var emojiScalars: [UnicodeScalar] {

        var chars: [UnicodeScalar] = []
        var previous: UnicodeScalar?
        for cur in unicodeScalars {

            if let previous = previous, previous.isZeroWidthJoiner && cur.isEmoji {
                chars.append(previous)
                chars.append(cur)

            } else if cur.isEmoji {
                chars.append(cur)
            }

            previous = cur
        }

        return chars
    }
}

, Qui vous donnera les résultats suivants:

"".isSingleEmoji // true
"‍♂️".isSingleEmoji // true
"‍‍‍".isSingleEmoji // true
"‍‍‍".containsOnlyEmoji // true
"Hello ‍‍‍".containsOnlyEmoji // false
"Hello ‍‍‍".containsEmoji // true
" Héllo ‍‍‍".emojiString // "‍‍‍"
"‍‍‍".glyphCount // 1
"‍‍‍".characters.count // 4, Will return '1' in Swift 4.2 so previous method not needed anymore

" Héllœ ‍‍‍".emojiScalars // [128107, 128104, 8205, 128105, 8205, 128103, 8205, 128103]
" Héllœ ‍‍‍".emojis // ["", "‍‍‍"]

"‍‍‍‍‍".isSingleEmoji // false
"‍‍‍‍‍".containsOnlyEmoji // true
"‍‍‍‍‍".glyphCount // 3
"‍‍‍‍‍".characters.count // 8, Will return '3' in Swift 4.2 so previous method not needed anymore
111
répondu Kevin R 2018-07-03 13:39:46

Le moyen le plus simple, le plus propre et le plus rapide pour y parvenir est de simplement vérifier les points de code Unicode pour chaque caractère de la chaîne par rapport aux plages emoji et dingbats connues, comme ceci:

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
                 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs
                 0x1F1E6...0x1F1FF: // Flags
                return true
            default:
                continue
            }
        }
        return false
    }

}
35
répondu Arnold 2017-10-27 23:03:24
extension String {
    func containsEmoji() -> Bool {
        for scalar in unicodeScalars {
            switch scalar.value {
            case 0x3030, 0x00AE, 0x00A9,// Special Characters
            0x1D000...0x1F77F,          // Emoticons
            0x2100...0x27BF,            // Misc symbols and Dingbats
            0xFE00...0xFE0F,            // Variation Selectors
            0x1F900...0x1F9FF:          // Supplemental Symbols and Pictographs
                return true
            default:
                continue
            }
        }
        return false
    }
}

C'est mon correctif, avec des plages mises à jour.

8
répondu Sebastian Lopez 2016-08-18 20:25:50

Swift 3 Remarque:

Il semble que la méthode cnui_containsEmojiCharacters ait été supprimée ou déplacée vers une bibliothèque dynamique différente. _containsEmoji devrait quand même fonctionner.

let str: NSString = "hello"

@objc protocol NSStringPrivate {
    func _containsEmoji() -> ObjCBool
}

let strPrivate = unsafeBitCast(str, to: NSStringPrivate.self)
strPrivate._containsEmoji() // true
str.value(forKey: "_containsEmoji") // 1


let swiftStr = "hello"
(swiftStr as AnyObject).value(forKey: "_containsEmoji") // 1

Swift 2.x:

J'ai récemment découvert une API privée sur {[7] } qui expose des fonctionnalités pour détecter si une chaîne contient un caractère Emoji:

let str: NSString = "hello"

Avec un protocole objc et unsafeBitCast:

@objc protocol NSStringPrivate {
    func cnui_containsEmojiCharacters() -> ObjCBool
    func _containsEmoji() -> ObjCBool
}

let strPrivate = unsafeBitCast(str, NSStringPrivate.self)
strPrivate.cnui_containsEmojiCharacters() // true
strPrivate._containsEmoji() // true

Avec valueForKey:

str.valueForKey("cnui_containsEmojiCharacters") // 1
str.valueForKey("_containsEmoji") // 1

Avec une Chaîne Swift pure, vous devez lancer la chaîne comme AnyObject avant d'utiliser valueForKey:

let str = "hello"

(str as AnyObject).valueForKey("cnui_containsEmojiCharacters") // 1
(str as AnyObject).valueForKey("_containsEmoji") // 1

Méthodes trouvées dans le fichier D'en-têteNSString .

5
répondu JAL 2016-12-02 00:27:43

Vous pouvez utiliser ce code exemple, ou ce pod.

Pour l'utiliser dans Swift, importez la catégorie dans YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Ensuite, vous pouvez vérifier la plage pour chaque emoji dans votre chaîne:

let example: NSString = "string‍‍‍withemojis" //string with emojis

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

J'ai créé un petit exemple de projet avec le code ci-dessus.

2
répondu Gabriel.Massana 2016-01-25 23:03:52

Pour Swift 3.0.2, la réponse suivante est la plus simple:

class func stringContainsEmoji (string : NSString) -> Bool
{
    var returnValue: Bool = false

    string.enumerateSubstrings(in: NSMakeRange(0, (string as NSString).length), options: NSString.EnumerationOptions.byComposedCharacterSequences) { (substring, substringRange, enclosingRange, stop) -> () in

        let objCString:NSString = NSString(string:substring!)
        let hs: unichar = objCString.character(at: 0)
        if 0xd800 <= hs && hs <= 0xdbff
        {
            if objCString.length > 1
            {
                let ls: unichar = objCString.character(at: 1)
                let step1: Int = Int((hs - 0xd800) * 0x400)
                let step2: Int = Int(ls - 0xdc00)
                let uc: Int = Int(step1 + step2 + 0x10000)

                if 0x1d000 <= uc && uc <= 0x1f77f
                {
                    returnValue = true
                }
            }
        }
        else if objCString.length > 1
        {
            let ls: unichar = objCString.character(at: 1)
            if ls == 0x20e3
            {
                returnValue = true
            }
        }
        else
        {
            if 0x2100 <= hs && hs <= 0x27ff
            {
                returnValue = true
            }
            else if 0x2b05 <= hs && hs <= 0x2b07
            {
                returnValue = true
            }
            else if 0x2934 <= hs && hs <= 0x2935
            {
                returnValue = true
            }
            else if 0x3297 <= hs && hs <= 0x3299
            {
                returnValue = true
            }
            else if hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50
            {
                returnValue = true
            }
        }
    }

    return returnValue;
}
2
répondu Ankit Goyal 2017-02-06 06:15:36

Vous pouvez utiliser NSString-RemoveEmoji comme ceci:

if string.isIncludingEmoji {

}
1
répondu Shardul 2016-03-10 02:36:14

! Objective-C (peut être converti en Swift)

Au fil des ans, ces solutions de détection d'emoji continuent de se briser car Apple ajoute de nouveaux emojis avec de nouvelles méthodes (comme des emojis à la peau tonique construits en pré-maudissant un personnage avec un personnage supplémentaire), etc.

Je suis finalement tombé en panne et j'ai juste écrit la méthode suivante qui fonctionne pour tous les emojis actuels et devrait fonctionner pour tous les emojis futurs.

La solution crée un UILabel avec le caractère et un fond noir. CG prend alors un aperçu de l'étiquette et je scanne tous les pixels dans l'instantané pour tout non solide-pixels noirs. La raison pour laquelle j'ajoute le fond noir est d'éviter les problèmes de fausse coloration dus au Rendu sous-pixel

La solution fonctionne très vite sur mon appareil, je peux vérifier des centaines de caractères par seconde, mais il convient de noter qu'il s'agit d'une solution CoreGraphics et ne devrait pas être utilisée lourdement comme vous le Pourriez avec une méthode de texte régulière. Le traitement graphique est lourd de données afin de vérifier des milliers de les caractères à la fois pourraient entraîner un retard notable.

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

}
1
répondu Albert Renshaw 2018-01-16 20:59:27

La réponse absolument similaire à ceux qui ont écrit avant moi, mais avec un ensemble mis à jour de scalaires emoji.

extension String {
    func isContainEmoji() -> Bool {
        let isContain = unicodeScalars.first(where: { $0.isEmoji }) != nil
        return isContain
    }
}


extension UnicodeScalar {

    var isEmoji: Bool {
        switch value {
        case 0x1F600...0x1F64F,
             0x1F300...0x1F5FF,
             0x1F680...0x1F6FF,
             0x1F1E6...0x1F1FF,
             0x2600...0x26FF,
             0x2700...0x27BF,
             0xFE00...0xFE0F,
             0x1F900...0x1F9FF,
             65024...65039,
             8400...8447,
             9100...9300,
             127000...127600:
            return true
        default:
            return false
        }
    }

}
1
répondu Alex Shoshiashvili 2018-04-10 08:53:43

J'ai eu le même problème et j'ai fini par faire des extensions String et Character.

Le code est trop long à publier car il répertorie en fait tous les emojis (de la liste officielle unicode v5. 0) dans un CharacterSet vous pouvez le trouver ici:

Https://github.com/piterwilson/StringEmoji

Constantes

laissez emojiCharacterSet: CharacterSet

Jeu de caractères contenant tous les emoji connus (comme décrit dans la liste officielle Unicode 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html)

Chaîne

var isEmoji: Bool { get }

Indique si l'instance String représente ou non un caractère Emoji unique connu

print("".isEmoji) // false
print("".isEmoji) // true
print("".isEmoji) // false (String is not a single Emoji)
var containsEmoji: Bool { get }

Indique si l'instance String contient ou non un caractère Emoji connu

print("".containsEmoji) // false
print("".containsEmoji) // true
print("".containsEmoji) // true
var unicodeName: chaîne { get }

Applique une kCFStringTransformToUnicodeName - CFStringTransform sur une copie de la Chaîne

print("á".unicodeName) // \N{LATIN SMALL LETTER A WITH ACUTE}
print("".unicodeName) // "\N{FACE WITH STUCK-OUT TONGUE AND WINKING EYE}"
var niceUnicodeName: chaîne { get }

Renvoie le résultat d'une kCFStringTransformToUnicodeName - CFStringTransform avec \N{ préfixes et } suffixes retiré

print("á".unicodeName) // LATIN SMALL LETTER A WITH ACUTE
print("".unicodeName) // FACE WITH STUCK-OUT TONGUE AND WINKING EYE
{[25] Caractère} var isEmoji: Bool { get }

Indique si l'instance Character représente ou non un caractère Emoji connu

print("".isEmoji) // false
print("".isEmoji) // true
-1
répondu Juan Carlos Ospina Gonzalez 2017-11-24 14:50:21