2016-06-30 2 views
3

У меня есть строка @"Hi there! \U0001F603", которая правильно отображает эмози, как Hi there!, если я положил ее в UILabel.Динамически создавать NSString с Unicode emoji

Но я хочу создать его динамически, как [NSString stringWithFormat:@"Hi there! \U0001F60%ld", (long)arc4random_uniform(10)], но он даже не компилируется. Если я удваиваю обратную косую черту, он показывает значение Unicode буквально как Hi there! \U0001F605.

Как я могу это достичь?

ответ

1

Шаг назад, для второго: это число, которое у вас есть, 1F6603 , является Unicode точка код, который, в попробуйте сделать это как можно проще, это индекс этого emoji в списке всех элементов Unicode. Это не то же самое, что байты, которые компьютер фактически обрабатывает, которые являются «закодированным значением» (технически, код единиц.

Когда вы пишете буквального@"\U0001F603" в коде, компилятор делает кодировку для вас, писать необходимые байты. * Если у вас нет буквальных во время компиляции, вы должны сделать кодирующую самостоятельно. То есть вы должны преобразовать кодовую точку в набор байтов, которые ее представляют. Например, в кодировке UTF-16, которую использует внутренний код NSString, ваша кодовая точка представлена ​​байтами ff fe 3d d8 03 de.

Вы не можете во время выполнения модифицировать этот литерал и в итоге получить правильные байты, потому что компилятор уже выполнил свою работу и ушел спать.

(Вы можете прочитать в глубине об этом материале, и как она относится к NSString в an article by Ole Begemann at objc.io.)

К счастью, один из доступных кодировок, UTF-32, представляет собой кодовые точки напрямую: значение байтов так же, как и кодовая точка. Другими словами, если вы присвоите номер кодовой точки 32-разрядному беззнаковому целому, у вас есть соответствующие данные в кодировке UTF-32.

Это приводит нас к процессу вам нужно:

// Encoded start point 
uint32_t base_point_UTF32 = 0x1F600; 

// Generate random point 
uint32_t offset = arc4random_uniform(10); 
uint32_t new_point = base_point_UTF32 + offset; 

// Read the four bytes into NSString, interpreted as UTF-32LE. 
// Intel machines and iOS on ARM are little endian; others byte swap/change 
// encoding as necessary. 
NSString * emoji = [[NSString alloc] initWithBytes:&new_point 
              length:4 
              encoding:NSUTF32LittleEndianStringEncoding]; 

(. NB, что это не может работать, как ожидается, для произвольной точки кода, не все кодовые точки действительны)


* Заметьте, он делает то же самое для «обычных» строк, например, @"b".

+0

Отличное объяснение, большое спасибо! –

+0

Не могли бы вы помочь мне с [этим вопросом] (http://stackoverflow.com/questions/38181966/print-unicode-emoji-from-api-response) тоже? –

3

\U0001F603 - это буква, который оценивается во время компиляции. Вы хотите решение, которое может быть выполнено во время выполнения.

Таким образом, вы хотите иметь строку с динамическим символом Юникода. %C, если спецификатор формата для символа Юникода ( unichar).

[NSString stringWithFormat:@"Hi there! %C", (unichar)(0x01F600 + arc4random_uniform(10))]; 

unichar слишком мал для смайликов. Спасибо @JoshCaswell за то, что исправил меня.


Update: рабочий ответ

@JoshCaswell имеет правильный ответ с -initWithBytes:length:encoding:, но я думаю, что я могу написать лучшую обертку.

  1. Создайте функцию для выполнения всей работы.
  2. Используйте network ordering для стандартного байтового заказа.
  3. Нет волшебного номера для длины.

Вот мой ответ

NSString *MyStringFromUnicodeCharacter(uint32_t character) { 
    uint32_t bytes = htonl(character); // Convert the character to a known ordering 
    return [[NSString alloc] initWithBytes:&bytes length:sizeof(uint32_t) encoding:NSUTF32StringEncoding]; 
} 

Таким образом, в использовании ...

NSString *emoji = MyStringFromUnicodeCharacter(0x01F600 + arc4random_uniform(10)); 
NSString *message = [NSString stringWithFormat:@"Hi there! %@", emoji]; 

Update 2

Наконец, положить в категории, чтобы сделать его реальным Objective-C.

@interface NSString (MyString) 
+ (instancetype)stringWithUnicodeCharacter:(uint32_t)character; 
@end 
@implementation NSString (MyString) 
+ (instancetype)stringWithUnicodeCharacter:(uint32_t)character { 
    uint32_t bytes = htonl(character); // Convert the character to a known ordering 
    return [[NSString alloc] initWithBytes:&bytes length:sizeof(uint32_t) encoding:NSUTF32StringEncoding]; 
} 
@end 

И опять же, в использовании ...

NSString *emoji = [NSString stringWithUnicodeCharacter:0x01F600 + arc4random_uniform(10)]; 
NSString *message = [NSString stringWithFormat:@"Hi there! %@", emoji]; 
+0

Он появляется как '' (квадрат с вопросительным знаком внутри него), вместо emoji –

+0

@IulianOnofrei убедитесь, что у вас есть' 0x01F600'. Когда я впервые опубликовал, я ошибся 0x01F60. –

+0

Я видел, что вы отредактировали свой ответ, и у меня действительно есть '0x01F600' –

Смежные вопросы