poner para emojis descargar copiar como ios string swift character emoji

ios - para - ΒΏAverigua si Character in String es emoji?



emojis ios descargar (13)

Necesito averiguar si un personaje en una cadena es un emoji.

Por ejemplo, tengo este personaje:

let string = "πŸ˜€" let character = Array(string)[0]

Necesito averiguar si ese personaje es un emoji.


Prueba futura: comprueba manualmente los píxeles del personaje; las otras soluciones se romperán (y se han roto) a medida que se agreguen nuevos emojis.

Nota: Esto es Objective-C (se puede convertir a Swift)

A lo largo de los años, estas soluciones de detección de emojis se siguen rompiendo a medida que Apple agrega nuevos emojis con nuevos métodos (como emojis con tonos de piel creados mediante la maldición previa de un personaje con un personaje adicional), etc.

Finalmente me quebré y simplemente escribí el siguiente método que funciona para todos los emojis actuales y debería funcionar para todos los emojis futuros.

La solución crea un UILabel con el personaje y un fondo negro. CG toma una instantánea de la etiqueta y escaneo todos los píxeles de la instantánea en busca de píxeles no negros sólidos. La razón por la que agrego el fondo negro es para evitar problemas de colores falsos debido a la representación de subpíxeles

La solución se ejecuta MUY rápido en mi dispositivo, puedo verificar cientos de caracteres por segundo, pero se debe tener en cuenta que esta es una solución CoreGraphics y no se debe usar mucho como lo haría con un método de texto normal. El procesamiento de gráficos requiere muchos datos, por lo que verificar miles de caracteres a la vez podría generar un retraso notable.

-(BOOL)isEmoji:(NSString *)character { UILabel *characterRender = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 1, 1)]; characterRender.text = character; characterRender.font = [UIFont fontWithName:@"AppleColorEmoji" size:12.0f];//Note: Size 12 font is likely not crucial for this and the detector will probably still work at an even smaller font size, so if you needed to speed this checker up for serious performance you may test lowering this to a font size like 6.0 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;//Note: Alpha Channel not really needed, if you need to speed this up for serious performance you can refactor this pixel scanner to just RGB 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];//Note: I wrote this method years ago, can''t remember why I check HSB instead of just checking r,g,b==0; Upon further review this step might not be needed, but I haven''t tested to confirm yet. b /= 255.0f; if (b > 0) { colorPixelFound = YES; } x++; } x=0; y++; } return colorPixelFound; }


Para Swift 5.0:

Cuentas para:

  • No emoji emoji (por ejemplo, caracteres ASCII)
  • Unión de ancho cero (cuenta πŸ‘¨‍πŸ‘©‍πŸ‘§‍πŸ‘¦ como un emoji, no 4)
  • Modificadores (p. Ej., Tono de piel 🏽)
  • Selectores de variación

Todo cierto:

"πŸ˜€".isPureEmojiString(withMinLength: 1, max: 1) // 1 scalar "πŸ‘ŒπŸΎ".isPureEmojiString(withMinLength: 1, max: 1) // 2 scalars (emoji modifier) "⛄️".isPureEmojiString(withMinLength: 1, max: 1) // 2 scalars (variation selector) "πŸ‡΅πŸ‡·".isPureEmojiString(withMinLength: 1, max: 1) // 2 scalars (2x regional indicators) "πŸ§–‍♀️".isPureEmojiString(withMinLength: 1, max: 1) // 4 scalars (ZWJ + ♀ + variation) "πŸ‘¨‍πŸ‘©‍πŸ‘¦‍πŸ‘¦".isPureEmojiString(withMinLength: 1, max: 1) // 7 scalars (ZW joiners)

extension String { func isPureEmojiString(withMinLength min: Int, max: Int) -> Bool { if count < min || count > max { return false } return isPureEmojiString() } func isPureEmojiString() -> Bool { for scalar in unicodeScalars { let prop = scalar.properties if prop.isJoinControl || prop.isVariationSelector || prop.isEmojiModifier { continue } else if scalar.properties.isEmoji == false || scalar.isASCII == true { return false } } return true } }


Swift 5.0

… Introdujo una nueva forma de verificar exactamente esto!

Tienes que romper tu String en sus Scalars . ¡Cada Scalar tiene un valor de Property que admite el valor isEmoji !

En realidad, incluso puede verificar si el Scalar es un modificador Emoji o más. Consulte la documentación de Apple: Property

Es posible que desee considerar buscar isEmojiPresentation lugar de isEmoji , porque Apple establece lo siguiente para isEmoji :

Esta propiedad es verdadera para los escalares que se representan como emoji de forma predeterminada y también para los escalares que tienen una representación de emoji no predeterminada cuando es seguido por U + FE0F VARIATION SELECTOR-16. Esto incluye algunos escalares que generalmente no se consideran emoji.

De esta manera, los Emoji se dividen en todos los modificadores, pero es mucho más sencillo de manejar. Y como Swift ahora cuenta los Emoji con modificadores (por ejemplo: πŸ‘¨‍πŸ‘©‍πŸ‘§‍πŸ‘¦, πŸ‘¨πŸ»‍πŸ’», 🏴) como 1, puedes hacer todo tipo de cosas.

var string = "πŸ€“ test" for scalar in string.unicodeScalars { let isEmoji = scalar.properties.isEmoji print("/(scalar.description) /(isEmoji)")) } // πŸ€“ true // false // t false // e false // s false // t false

NSHipster señala una forma interesante de obtener todos los Emoji:

import Foundation var emoji = CharacterSet() for codePoint in 0x0000...0x1F0000 { guard let scalarValue = Unicode.Scalar(codePoint) else { continue } // Implemented in Swift 5 (SE-0221) // https://github.com/apple/swift-evolution/blob/master/proposals/0221-character-properties.md if scalarValue.properties.isEmoji { emoji.insert(scalarValue) } }


Con Swift 5 ahora puede inspeccionar las propiedades unicode de cada personaje en su cadena. Esto nos da la conveniente variable isEmoji en cada letra. El problema es que isEmoji devolverá verdadero para cualquier personaje que se pueda convertir en un emoji de 2 bytes, como 0-9.

Podemos ver la variable isEmoji y también verificar la presencia de un modificador de emoji para determinar si los caracteres ambiguos se mostrarán como un emoji.

Esta solución debería ser mucho más a prueba de futuro que las soluciones de expresiones regulares que se ofrecen aquí.

if string.isIncludingEmoji { }

Dándonos

extension String { func containsOnlyEmojis() -> Bool { if count == 0 { return false } for character in self { if !character.isEmoji { return false } } return true } func containsEmoji() -> Bool { for character in self { if character.isEmoji { return true } } return false } } extension Character { // An emoji can either be a 2 byte unicode character or a normal UTF8 character with an emoji modifier // appended as is the case with 3️⃣. 0x238C is the first instance of UTF16 emoji that requires no modifier. // `isEmoji` will evaluate to true for any character that can be turned into an emoji by adding a modifier // such as the digit "3". To avoid this we confirm that any character below 0x238C has an emoji modifier attached var isEmoji: Bool { guard let scalar = unicodeScalars.first else { return false } return scalar.properties.isEmoji && (scalar.value > 0x238C || unicodeScalars.count > 1) } }


La forma más simple, limpia y rápida de lograr esto es simplemente verificar los puntos de código Unicode para cada carácter en la cadena contra los rangos conocidos de emoji y dingbats, de esta manera:

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


La respuesta absolutamente similar a las que escribieron antes que yo, pero con un conjunto actualizado de escalares 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 } } }


Me topé con la diferencia entre caracteres, escalares unicode y glifos.

Por ejemplo, el glifo πŸ‘¨‍πŸ‘¨‍πŸ‘§‍πŸ‘§ consta de 7 escalares unicode:

Otro ejemplo, el glifo πŸ‘ŒπŸΏ consta de 2 escalares unicode:

  • El emoji normal: πŸ‘Œ
  • Un modificador de tono de piel: 🏿

Entonces, al representar los caracteres, los glifos resultantes realmente importan.

Lo que estaba buscando era una forma de detectar si una cadena es exactamente y solo un emoji. Por lo tanto, podría hacerlo más grande que el texto normal (como lo hace Messages en iOS10 y WhatsApp lo hace hoy en día). Como se describió anteriormente, el recuento de caracteres es realmente inútil. (El ''personaje de pegamento'' tampoco se considera un emoji).

Lo que puede hacer es usar CoreText para ayudarlo a dividir la cadena en glifos y contarlos. Además, movería parte de la extensión propuesta por Arnold y Sebastian Lopez a una extensión separada de UnicodeScalar .

Te deja con el siguiente resultado:

import Foundation extension UnicodeScalar { /// Note: This method is part of Swift 5, so you can omit this. /// See: https://developer.apple.com/documentation/swift/unicode/scalar 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 0xE0020...0xE007F, // Tags 0xFE00...0xFE0F, // Variation Selectors 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs 0x1F018...0x1F270, // Various asian characters 0x238C...0x2454, // Misc items 0x20D0...0x20FF: // Combining Diacritical Marks for Symbols return true default: return false } } var isZeroWidthJoiner: Bool { return value == 8205 } } extension String { // Not needed anymore in swift 4.2 and later, using `.count` will give you the correct result 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 } }

Lo que te dará los siguientes resultados:

"πŸ‘ŒπŸΏ".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 "πŸ‘¨‍πŸ‘©‍πŸ‘§‍πŸ‘§".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


Para Swift 3.0.2, la siguiente respuesta es la más 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; }


Puede usar NSString-RemoveEmoji esta manera:

"hey".containsEmoji() //false "Hello World 😎".containsEmoji() //true "Hello World 😎".containsOnlyEmojis() //false "😎".containsEmoji() //true "😎".containsOnlyEmojis() //true


Puede usar este example código o este pod .

Para usarlo en Swift, importe la categoría en YourProject_Bridging_Header

#import "NSString+EMOEmoji.h"

Luego puedes verificar el rango de cada emoji en tu cadena:

let example: NSString = "stringπŸ‘¨‍πŸ‘¨‍πŸ‘§‍πŸ‘§with😍emojis✊🏿" //string with emojis let containsEmoji: Bool = example.emo_containsEmoji() print(containsEmoji) // Output: ["true"]

Creé un pequeño proyecto de ejemplo con el código anterior.


Swift 3 Nota:

Parece que el método cnui_containsEmojiCharacters se ha eliminado o movido a una biblioteca dinámica diferente. _containsEmoji aún debería funcionar.

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:

Recientemente descubrí una API privada en NSString que expone la funcionalidad para detectar si una cadena contiene un carácter Emoji:

let str: NSString = "hello😊"

Con un protocolo objc y unsafeBitCast :

@objc protocol NSStringPrivate { func cnui_containsEmojiCharacters() -> ObjCBool func _containsEmoji() -> ObjCBool } let strPrivate = unsafeBitCast(str, NSStringPrivate.self) strPrivate.cnui_containsEmojiCharacters() // true strPrivate._containsEmoji() // true

Con valueForKey :

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

Con una cadena Swift pura, debe convertir la cadena como AnyObject antes de usar valueForKey :

let str = "hello😊" (str as AnyObject).valueForKey("cnui_containsEmojiCharacters") // 1 (str as AnyObject).valueForKey("_containsEmoji") // 1

Métodos encontrados en el archivo de encabezado NSString .


Tuve el mismo problema y terminé haciendo una String y extensiones de Character .

El código es demasiado largo para publicarlo, ya que en realidad enumera todos los emojis (de la lista oficial Unicode v5.0) en un conjunto de CharacterSet . Puede encontrarlo aquí:

https://github.com/piterwilson/StringEmoji

Constantes

let emojiCharacterSet: CharacterSet

Conjunto de caracteres que contiene todos los emoji conocidos (como se describe en la lista oficial Unicode 5.0 http://unicode.org/emoji/charts-5.0/emoji-list.html )

Cuerda

var isEmoji: Bool {get}

Si la instancia de String representa o no un único personaje Emoji conocido

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

Si la instancia de String contiene o no un personaje Emoji conocido

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

Aplica un kCFStringTransformToUnicodeName - CFStringTransform en una copia de la cadena

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

Devuelve el resultado de un kCFStringTransformToUnicodeName - CFStringTransform con /N{ prefijos y } sufijos eliminados

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

Personaje

var isEmoji: Bool {get}

Si la instancia del Character representa o no un personaje Emoji conocido

print("".isEmoji) // false print("😁".isEmoji) // true


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

Esta es mi solución, con rangos actualizados.