Replace iOS app emoji with twitter open source twemoji

牧云@^-^@ 提交于 2019-12-17 20:00:56

问题


I want to replace all standard iOS emoji from a UILable or UITextView with twitters open source twemoji.

I can't find any library or documentation to do this in iOS. Does anyone have a solution that does not involve me implementing this from scratch?

The solution needs to be efficient and work offline.


回答1:


The question got me intrigued, and after a bit of searching on how it would be possible to replace all standard iOS emoji with a custom set, I noticed that even Twitter's own iOS app doesn't use Twemoji:

In the end, I came to the same conclusion as you:

I can't find any library or documentation to do this in iOS.

So, I created a framework in Swift for this exact purpose.

It does all the work for you, but if you want to implement your own solution, I'll describe below how to replace all standard emoji with Twemoji.


1. Document all characters that can be represented as emoji

There are 1126 base characters that have emoji representations, and over a thousand additional representations formed by sequences. Although most base characters are confined to six Unicode blocks, all but one of these blocks are mixed with non-emoji characters and/or unassigned code points. The remaining base characters outside these blocks are scattered across various other blocks.

My implementation simply declares the UTF-32 code points for these characters, as the value property of UnicodeScalar is exactly this.

2. Check whether a character is an emoji

In Swift, a String contains a collection of Character objects, each of which represent a single extended grapheme cluster. An extended grapheme cluster is a sequence of Unicode scalars that together represent one1 human-readable character, which is helpful since you can loop through the Characters of a string and handling them based on the UnicodeScalars they contain (rather than looping through the UTF-16 values of the string).

To identify whether a Character is an emoji, only the first UnicodeScalar is significant, so comparing this value to your table of emoji characters is enough. However, I'd also recommend checking if the Character contains a Variation Selector, and if it does, make sure that it's VS16 – otherwise the character shouldn't be presented as emoji.

Extracting the UnicodeScalars from a Character requires a tiny hack:

let c: Character = "A"
let scalars = String(c).unicodeScalars

3. Convert the code points into the correct format

Twemoji images are named according to their corresponding code points2, which makes sense. So, the next step is to convert the Character into a string equivalent to the image name:

let codePoint = String("🙃").unicodeScalars.first!.value  // 128579
let imageName = String(codePoint, radix: 16)              // "1f643"

Great, but this won't work for flags or keycaps, so we'll have to modify our code to take those into account:

let scalars = String("🇧🇪").unicodeScalars
let filtered = scalars.filter{ $0.value != 0xfe0f }       // Remove VS16 from variants, including keycaps.
let mapped = filtered.map{ String($0.value, radix: 16) }
let imageName = mapped.joined(separator: "-")             // "1f1e7-1f1ea"

4. Replace the emoji in the string

In order to replace the emoji in a given String, we'll need to use NSMutableAttributedString for storing the original string, and replace the emoji with NSTextAttachment objects containing the corresponding Twemoji image.

let originalString = "🙃"

let attributedString = NSMutableAttributedString(string: originalString)

for character in originalString.characters {
    // Check if character is emoji, see section 2.
    ...

    // Get the image name from the character, see section 3.
    let imageName = ...

    // Safely unwrapping to make sure the image exists.
    if let image = UIImage(named: imageName) {
        let attachment = NSTextAttachment()
        attachment.image = image

        // Create an attributed string from the attachment.
        let twemoji = NSAttributedString(attachment: attachment)

        // Get the range of the character in attributedString.
        let range = attributedString.mutableString.range(of: String(character))

        // Replace the emoji with the corresponding Twemoji.
        attributedString.replaceCharacters(in: range, with: twemoji)
    }
}

To display the resulting attributed string, just set it as the attributedText property of a UITextView/UILabel.

Note that the above method doesn't take into account zero-width joiners or modifier sequences, but I feel like this answer is already too long as it stands.


1. There is a quirk with the Character type that interprets a sequence of joined regional indicator symbols as one object, despite containing a theoretically unlimited amount of Unicode scalars. Try "🇩🇰🇫🇮🇮🇸🇳🇴🇸🇪".characters.count in a playground.

2. The naming pattern varies slightly when it comes to zero-width joiners and variation selectors, so it's easier to strip these out of the image names – see here.




回答2:


Easiest thing to do:

1) Load the twemoji images into your project. 2) Create an NSDictionary that correlates the emoji codes supported by iOS with the paths to the respective twemoji images:

NSArray *iOSEmojis = @[@"iOSEmoji1",@"iOSEmoji2];
NSDictionary *twemojiPaths = [NSDictionary dictionaryWithObjects:@[@"Project/twemoji1.png",@"Project/twemoji2.png"] andKeys:@[@"iOSEmoji1","iOSEmoji2"]];

3) Code your app to search for emoji strings and display the twemojis where the regular emojis would go:

for (NSString *emoji in iOSEmojis)
{
    NSString *twemojiPath = [twemojiPaths valueForKey:emoji];
    // Find the position of the emoji string in the text
    // and put an image view there.
    NSRange range = [label.text rangeOfString:emoji];
    NSString *prefix = [label.text substringToIndex:range.location];
    CGSize prefixSize = [prefix sizeWithAttributes: @{NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:14]}];
    CGSize emojiSize = [label.text sizeWithAttributes: @{NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:14]}];
    CGRect imageViewFrame = CGRectMake(prefixSize.width,label.frame.size.height,emojiSize.width,label.frame.size.height);
    imageViewFrame = [self.view convertRect:imageViewFrame fromView:label];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:imageViewFrame];
    imageView.image = [UIImage imageWithContentsOfFile:twemojiPath];
}


来源:https://stackoverflow.com/questions/40663230/replace-ios-app-emoji-with-twitter-open-source-twemoji

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!