Dowiedz się, czy znak w łańcuchu to emoji?

Muszę się dowiedzieć, czy znak w łańcuchu jest emoji.

Na przykład mam taki znak:

let string = "
Author: ABakerSmith, 2015-06-10

10 answers

Natknąłem się na różnicę między znakami, skalarami unicode i glifami.

Na przykład glif składa się z 7 skalarów unicode:

Inny przykład, glif składa się z 2 skalarów unicode:

  • zwykły Emotikon:
  • modyfikator odcienia skóry:

Więc podczas renderowania znaków, powstałe glify naprawdę mają znaczenie.

Szukałem sposobu, aby wykryć, czy ciąg jest dokładnie i tylko jeden emoji. Więc mogę renderować go większy niż normalny tekst(jak wiadomości robi na iOS10 i WhatsApp robi obecnie). Jak opisano powyżej, liczba postaci jest naprawdę bezużyteczna. ("Znak kleju" nie jest również uważany za emoji).

To, co możesz zrobić, to użyć CoreText, aby pomóc Ci podzielić ciąg znaków na glify i policz je. Ponadto chciałbym przenieść część rozszerzenia zaproponowanego przez Arnolda i Sebastiana Lopeza do osobnego rozszerzenia UnicodeScalar.

Zostawia Ci następujący wynik:

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

Co da Ci następujące wyniki:

"".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
Author: Kevin R,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-03 13:39:46

Najprostszym, najczystszym i najszybszym sposobem na to jest sprawdzenie punktów kodu Unicode dla każdego znaku w łańcuchu względem znanych zakresów emoji i dingbats, jak tak:

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
Author: Arnold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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
    }
}

To jest moja poprawka, z zaktualizowanymi zakresami.

 8
Author: Sebastian Lopez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-18 20:25:50

Swift 3 Uwaga:

Wygląda na to, że metoda cnui_containsEmojiCharacters została usunięta lub przeniesiona do innej dynamicznej biblioteki. _containsEmoji nadal powinno działać.

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:

Niedawno odkryłem prywatne API na NSString, które ujawnia funkcjonalność do wykrywania, czy łańcuch zawiera znak Emoji:

let str: NSString = "hello"

Z protokołem objc i unsafeBitCast:

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

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

Z valueForKey:

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

Z czystym, szybkim sznurkiem, musisz rzucić sznur jako AnyObject przed użyciem valueForKey:

let str = "hello"

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

Metody Znalezione w pliku nagłówkowym NSString .

 5
Author: JAL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-02 00:27:43

Możesz użyć tego kodu przykład lub tego pod .

Aby użyć go w Swift, zaimportuj kategorię do YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Następnie możesz sprawdzić zakres dla każdego emoji w Twoim ciągu:

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

let containsEmoji: Bool = example.emo_containsEmoji()

    print(containsEmoji)

// Output: ["true"]

Stworzyłem mały przykładowy projekt z powyższym kodem.

 2
Author: Gabriel.Massana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-25 23:03:52

Dla Swifta 3.0.2 najprostsza jest następująca odpowiedź:

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
Author: Ankit Goyal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-06 06:15:36

Możesz użyć NSString-RemoveEmoji w następujący sposób:

if string.isIncludingEmoji {

}
 1
Author: Shardul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 02:36:14

! Objective-C (można przekonwertować na Swift)

[3]}z biegiem lat te rozwiązania wykrywające emoji ciągle się psują, ponieważ Apple dodaje nowe emoji w/ nowe metody (takie jak emoji w odcieniu skóry zbudowane przez wcześniejsze przeklinanie postaci z dodatkowym znakiem) itp.

W końcu się zepsułem i po prostu napisałem następującą metodę, która działa dla wszystkich obecnych emotikon i powinna działać dla wszystkich przyszłych Emotikon.

Rozwiązanie tworzy UILabel ze znakiem i czarnym tłem. CG następnie bierze migawka etykiety i skanuję wszystkie piksele migawki w poszukiwaniu pikseli innych niż stałe-czarne. Powodem, dla którego dodaję czarne tło, jest uniknięcie problemów z fałszywym kolorowaniem z powodu Subpikselowego renderowania

Rozwiązanie działa bardzo szybko na moim urządzeniu, mogę sprawdzić setki znaków na sekundę, ale należy zauważyć, że jest to rozwiązanie CoreGraphics i nie powinno być używane mocno, jak można z zwykłą metodą tekstową. Przetwarzanie grafiki jest ciężkie więc sprawdzanie tysięcy znaki naraz może spowodować zauważalne opóźnienie.

-(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
Author: Albert Renshaw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-16 20:59:27

Absolutnie podobna odpowiedź do tych, które napisały przede mną, ale z zaktualizowanym zestawem skalarów 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
Author: Alex Shoshiashvili,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-10 08:53:43

Miałem ten sam problem i skończyło się na zrobieniu String i Character rozszerzeń.

Kod jest zbyt długi, aby go opublikować, ponieważ faktycznie wyświetla wszystkie emotikony (z oficjalnej listy unicode v5. 0) w CharacterSet można go znaleźć tutaj:

Https://github.com/piterwilson/StringEmoji

Stałe

let emojiCharacterSet: CharacterSet

Zestaw znaków zawierający wszystkie znane emoji (jak opisano w oficjalnej liście Unicode 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

String

var isEmoji: Bool { get }

Czy instancja String reprezentuje znany pojedynczy znak Emoji

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

Czy instancja String zawiera znany znak Emoji

print("".containsEmoji) // false
print("".containsEmoji) // true
print("".containsEmoji) // true
var unicodeName: String { get }

Stosuje kCFStringTransformToUnicodeName - CFStringTransform na kopii łańcucha

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

Zwraca wynik kCFStringTransformToUnicodeName - CFStringTransform z usuniętymi przedrostkami \N{ i } przyrostkami

print("á".unicodeName) // LATIN SMALL LETTER A WITH ACUTE
print("".unicodeName) // FACE WITH STUCK-OUT TONGUE AND WINKING EYE

Znak

var isEmoji: Bool { get }

Czy instancja Character reprezentuje znany znak Emoji

print("".isEmoji) // false
print("".isEmoji) // true
 -1
Author: Juan Carlos Ospina Gonzalez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-24 14:50:21