动态创建带有Unicode表情符的NSString

5
我有一个字符串@"嗨,你好!\U0001F603",如果我将它放在UILabel中,它会正确显示表情符号,如嗨,你好!
但是,我想要像这样动态创建它:[NSString stringWithFormat:@"嗨,你好!\U0001F60%ld", (long)arc4random_uniform(10)],但它甚至都无法编译。如果我双倍反斜杠,它就会像这样直接显示Unicode值:嗨,你好!\U0001F605
我该如何实现这个需求?
2个回答

4
先退后一步:你手头的那个数字,1F660316,是一个 Unicode 码点。简单来说,它是在所有 Unicode 项目列表中这个表情符号的索引。这与计算机实际处理的字节不同,后者是“编码值”(技术上称为代码 单元)。
当你在代码中写下字面量 @"\U0001F603" 时,编译器会为你进行编码,写入必要的字节。如果你在编译时没有这个字面量,你必须自己进行编码。也就是说,你必须将代码点转换为一组代表它的字节。例如,在 NSString 内部使用的 UTF-16 编码中,你的代码点由字节 ff fe 3d d8 03 de 表示。
你不能在运行时修改该字面量并得到正确的字节,因为编译器已经完成了它的工作并去睡觉了。

关于这些东西以及它们与NSString的相关性,您可以深入阅读{{link1:objc.io上Ole Begemann撰写的文章}}。

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

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


*注意,它对于像@"b"这样的“普通”字符串也是一样的。


3

\U0001F603是在编译时计算的文字。你需要一个可以在运行时执行的解决方案。

所以你想要一个包含动态Unicode字符的字符串。%C是Unicode字符(unichar)的格式说明符。

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

unichar对于表情符号来说太小了。感谢@JoshCaswell指出我的错误。


更新:一个有效的答案

@JoshCaswell的-initWithBytes:length:encoding:是正确的答案,但我认为我可以写一个更好的包装器。

  1. 创建一个函数来完成所有工作。
  2. 使用网络顺序以进行标准字节顺序。
  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

And again, in use…

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

它显示为(一个带有问号的方块),而不是表情符号。 - Iulian Onofrei
@IulianOnofrei 请确保你有 0x01F600。当我第一次发布时,我犯了一个错误,写成了 0x01F60 - Jeffery Thomas
我看到你编辑了你的回答,我确实有0x01F600 - Iulian Onofrei
@JoshCaswell 哎呀,我还没有遇到过这个问题。我会更新我的解决方案。 - Jeffery Thomas
@JefferyThomas,你最新的解决方案也可以,但我接受了Josh的答案,因为他更好地解释了Unicode部分。谢谢! - Iulian Onofrei
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接