使用 Unicode 表情符号动态创建 NSString

Dynamically create NSString with Unicode emoji

我有字符串 @"Hi there! \U0001F603",如果我将它放在 UILabel.

中,它会正确显示像 Hi there! 这样的表情符号

但我想像 [NSString stringWithFormat:@"Hi there! \U0001F60%ld", (long)arc4random_uniform(10)] 那样动态创建它,但它甚至无法编译。 如果我加倍反斜杠,它会像 Hi there! \U0001F605.

这样显示 Unicode 值

我怎样才能做到这一点?

\U0001F603 是在编译时评估的文字。您想要一个可以在运行时执行的解决方案。

所以你想要一个带有动态 unicode 字符的字符串。 %C 如果 unicode 字符的格式说明符 (unichar)。

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

unichar 对于表情符号来说太小了。感谢@JoshCaswell 纠正我。


更新:有效答案

@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];

更新 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];

先退后一步:您拥有的号码 1F660316 是一个 Unicode 代码点,尽量简单地说,就是这个表情符号在所有 Unicode 项目列表中的索引。这与计算机实际处理的字节不同,后者是 "encoded value"(技术上,代码 units

当您在代码中编写 literal @"\U0001F603" 时,编译器会为您进行编码,写入必要的字节。*如果您没有编译时的文字,你必须自己做编码。也就是说,您必须将代码点转换为一组表示它的字节。例如,在 NSString 内部使用的 UTF-16 编码中,您的代码点由字节 ff fe 3d d8 03 de.

表示

您不能在 运行 时间修改该文字并以正确的字节结束,因为编译器已经完成工作并去睡觉了。

(您可以在 an article by Ole Begemann at objc.io 中深入阅读这些内容以及它与 NSString 的关系。)

幸运的是,可用的编码之一 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];

(N.B。对于任意代码点,这可能无法按预期工作;并非所有代码点都有效。)


*注意,它对 "normal" 字符串(如 @"b")也做同样的事情。