Detailed implementation of emoticon keyboard in IOS

Time:2020-1-17

Preface

Recently, I have made a demand for expression keyboard in the company. The technical difficulty of this demand will not be great, and it is more business oriented. But it’s not easy to do a good user experience. There are several points that need special attention. If you don’t want to say much, start with the text below (Note: the demo corresponding to this article is placed on GitHub: https://github.com/vernonvan/ppstickerkeyboard (local upload)).

Analysis of expression keyboard on the market

First of all, let’s take a look at the emoticon keyboards on several major apps on the market. Usually, we don’t pay attention to the details when we use them. This time, we specially used emoticon keyboards, and found that the experience of each app is good or bad.

First of all, QQ and wechat are almost the same. When switching to the emoticon keyboard, there is no cursor. This kind of user experience is very bad. There is no way to select the area when inputting emoticons, nor to drag the cursor to copy, paste and delete the specific position. Wechat even displays the emoticon picture of clicking in the input box, but the text description Narrated.

Wechat QQ emoticon keyboard.jpg

Next, take a look at the international version of Weibo. The international version has a cursor when the emoticon keyboard is activated, which is a “real” keyboard. However, when you want to drag the cursor, it will trigger the behavior of saving pictures (as shown in the figure below), which makes it impossible to drag the light pointer at all.


Weibo international touched by mistake.jpg

At the same time, the cursor position after the expression is pasted on the input box of microblog international is wrong, as shown in the following figure. The start time mark is behind the fourth expression, and then copy the dog head + shy expressions to stick to the cursor, whether the cursor is behind the fourth expression, and at the same time, there are many spaces before and after the pasted expression.


Weibo international version paste.jpg

Finally, microblog, microblog client’s emoticon keyboard experience is very good, the above mentioned problems do not exist, and emoticon keyboard delete button can long press Delete input box content.


Weibo emoticon keyboard.jpg

The realization of expression keyboard

Realization effect

The main functions are as follows

  • Can input expression, have cursor, support copy, paste and delete expression, etc
  • Long press preview emoticon
  • Delete expression, long press to delete expression continuously
  • Fit iPhone x


Demo.GIF

Basic thinking

First of all, the picture of expression package is organized in the form of bundle. Ppsticker class is used to represent a set of expression package, ppemoji class is used to represent a certain expression, and plist is used as configuration file to store the information of expression package.

Expression organization.jpg

Ppstickerdatamanager class is mainly responsible for the data part, in the form of a single example, so that all the expression information in plist file can be read only once during initialization; at the same time, we send the input box content to the server and the request from the server are pure text, for example, we will turn “laugh to death” to “laugh to death [laugh to cry]” instead of directly Send the emoticon image directly to the server, that is to say, there are a lot of places in the project that will have the operation of text – > emoticon, so ppstickerdatamanager class also provides the function of matching the emoticon in a plain text and replacing the text with the image. The header file of ppstickerdatamanager class is as follows:

@interface PPStickerDataManager : NSObject

+ (instancetype)sharedInstance;

///All expression packs
@property (nonatomic, strong, readonly) NSArray<PPSticker *> *allStickers;

/*Match all Emoji in the given attributedstring. If the matched Emoji has a local picture, it will be directly replaced with a local picture
*
*@ param attributedstring may contain attributedstring of expression pack
*@ param font aligned font size of emoticons
*/
- (void)replaceEmojiForAttributedString:(NSMutableAttributedString *)attributedString font:(UIFont *)font;
@end

“Real” keyboard

The real keyboard means that the input box has a cursor when the emoticon keyboard is turned on. It can drag the cursor, select the area, and so on. This experience is consistent with the system keyboard. In fact, the system has provided interfaces for us to use directly. Inputview and inputaccessoryview, both of uitextview and uitextfield, are used to implement user-defined keyboard. The definitions of these two properties are as follows:


// Presented when object becomes first responder. If set to nil, reverts to following responder chain. If
// set while first responder, will not take effect until reloadInputViews is called.
@property (nullable, readwrite, strong) UIView *inputView;  
@property (nullable, readwrite, strong) UIView *inputAccessoryView;

At the same time, when the system keyboard is set – > Sound – > keystone option is turned on and the phone is not mute, the keystone can also be supported. As long as the user-defined keyboard class follows the uiinputviewaudiofeedback protocol, and implements the enableinputclickswhenvisible method and returns yes, it can call [[uidevice currentde] when clicking the expression Vice] playinputclick] method sends out a button tone. Please check Apple’s official documents for details.

The following is the implementation of keyboard switching method in Demo:

- (void)changeKeyboardTo:(PPKeyboardType)toType
{
 switch (toType) {
 case PPKeyboardTypeSystem:
  Self.textview.inputview = nil; // switch to the system keyboard
  [self. Textview reloadinputviews]; // calling the reloadinputviews method will immediately switch the keyboard
  break;
 case PPKeyboardTypeSticker:  
  Self.textview.inputview = self.tickerkeyboard; // switch to a custom emoticon keyboard
  [self.textView reloadInputViews];
  break;
 default:
  break;
 }
}

Remove drag and drop interaction of expression

On IOS 11, nstextattachment (emoticon) on uitextview can drag by default, but it is easy to trigger this interaction when dragging the cursor (the figure can see the wrong touch in the international version of Weibo mentioned above). After a search, a relatively hidden property is found: textdraginteraction. Setting it to no directly can disable the dragging interaction of nstextattachment.

If (@ available (IOS 11.0, *) {// this attribute is only available for IOS 11 and above
 _textView.textDragInteraction.enabled = NO;
}

Interaction with the server

When we interact with the server in the input box, we use plain text. For example, we will change “smile dead” to “smile dead [laugh and cry]” and send the plain text to the server instead of directly sending the emoticons. When we request the content from the server, we will also send back “smile dead [laugh and cry]”, and then the client will find the emoticons and replace them with corresponding ones according to the regular matching The emoticon image is then displayed on the page. The specific process can be seen in the following figure:


Interaction with server.png

That is to say, each nstextattachment in the nsattributedstring we set to the input box has a “hidden” attribute – the text description of the expression, which can be realized by extending nsattributedstring here. Pp_settextbackedstring can set an attribute of type pptextbackedstring to the specified range of nsattributedstring, while pp_plaintextforrange can get the plain text of the specified range of nsattributedstring. The specific implementation is as follows:


@implementation NSAttributedString (PPAddition)

- (NSString *)pp_plainTextForRange:(NSRange)range
{
 if (range.location == NSNotFound || range.length == NSNotFound) {
 return nil;
 }

 NSMutableString *result = [[NSMutableString alloc] init];
 if (range.length == 0) {
 return result;
 }

 NSString *string = self.string;
 [self enumerateAttribute:PPTextBackedStringAttributeName inRange:range options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) {
 PPTextBackedString *backed = value;
 if (backed && backed.string) {
  [result appendString:backed.string];
 } else {
  [result appendString:[string substringWithRange:range]];
 }
 }];
 return result;
}

@end

@implementation NSMutableAttributedString (PPAddition)

- (void)pp_setTextBackedString:(PPTextBackedString *)textBackedString range:(NSRange)range
{
 if (textBackedString && ![NSNull isEqual:textBackedString]) {
 [self addAttribute:PPTextBackedStringAttributeName value:textBackedString range:range];
 } else {
 [self removeAttribute:PPTextBackedStringAttributeName range:range];
 }
}
@end

Flexible cursor

The expression function and uitextview are all assigned with nsattributedstring, and our underlying layer is actually implemented with the above-mentioned plain text, so turning [laughing dead] into ㊃ will change from 4 characters to 1 character, and there is a difference here. If we don’t handle it, the expression of copying and pasting the input box in the international version of Weibo mentioned above will appear, which will lead to the cursor The location is not right, or even inexplicably more space before and after the problem. In order to locate the cursor accurately, we need to deal with these problems by ourselves.

Here, I inherit and implement the subclass ppstickertextview of uitextview, in which operations such as copy, paste and cut are overloaded. The corresponding methods are as follows:

-(void) cut: (ID) sender; // cut
-(void) copy: (ID) sender; // copy
-(void) paste: (ID) sender; // paste

Here is an example of cutting method to see how to deal with the cursor problem. Please see the corresponding notes for the points needing attention:

- (void)cut:(id)sender
{
 //1. Get the corresponding plain text from textview, for example: dead with a smile [dead with a smile]
 NSString *string = [self.attributedText pp_plainTextForRange:self.selectedRange];
 if (string.length) {
 //2. Write plain text to clipboard
 [UIPasteboard generalPasteboard].string = string;

 //3. Remember the current cursor position
 NSRange selectedRange = self.selectedRange;
 NSMutableAttributedString *attributeContent = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
 //4. Replace the text detected as an expression with the corresponding image
 [attributeContent replaceCharactersInRange:self.selectedRange withString:@""];
 self.attributedText = attributeContent;
 
 //5. Reset cursor
 self.selectedRange = NSMakeRange(selectedRange.location, 0);
 }
}

The analysis of technical points is just the above. The detailed code can be viewed on GitHub under clone: https://github.com/vernonvan/ppstickerkeyboard (upload locally)

summary

The above is the whole content of this article. I hope that the content of this article has a certain reference learning value for everyone’s study or work. If you have any questions, you can leave a message and exchange. Thank you for your support for developepaar.

Recommended Today

Notes on tensorflow 2 deep learning (I) tensorflow Foundation

This series of notes records the process of learning tensorflow2, mainly based on https://github.com/dragen1860/Deep-Learning-with-TensorFlow-book Learning First of all, it needs to be clear that tensorflow is a scientific computing library for deep learning algorithm, and the internal data is stored in theTensor objectAll operations (OPS) are also based on tensor objects. data type Fundamentals in […]