【问题标题】:Dynamically create NSString with Unicode emoji使用 Unicode 表情符号动态创建 NSString
【发布时间】:2016-11-02 16:09:39
【问题描述】:

我有字符串@"Hi there! \U0001F603",如果我把它放在UILabel 中,它会正确显示Hi there! ???? 之类的表情符号。

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

我怎样才能做到这一点?

【问题讨论】:

    标签: ios objective-c unicode emoji


    【解决方案1】:

    退一步,等一下:您拥有的那个数字 1F660316 是一个 Unicode 代码点,试着简单地说可能,是这个表情符号在所有 Unicode 项目列表中的索引。这与计算机实际处理的字节不同,后者是“编码值”(技术上,代码 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];
    

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


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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 它显示为(一个带有问号的正方形),而不是表情符号
      • @IulianOnofrei 确保您拥有0x01F600。当我第一次发布时,我错误地使用了0x01F60
      • 我看到你编辑了你的答案,我确实有0x01F600
      • unichar, 是一个unsigned short -- 2 个字节 -- 所以你会丢失前两个字节。 (%C 也只读取 2 个字节。) 0x01F600 也不是此代码点的 UTF-16 编码。我不相信stringWithFormat: 有任何方法可以获取 UTF-32。
      • @JoshCaswell 哦,笨蛋,我还没有遇到这个问题。我会更新我的解决方案。
      猜你喜欢
      • 2016-12-31
      • 1970-01-01
      • 2023-03-04
      • 2012-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-04
      • 2021-10-02
      相关资源
      最近更新 更多