UITextView是ios的富文本編輯控件,除了文字還能夠插入圖片等。今天主要介紹一下UITextView對自定義表情的處理。ios
一、首先識別出文本中的表情文本,而後在對應的位置插入NSTextAttachment對象,該對象存放的就是自定義表情。正則表達式
1 static NSString *emojiTextPttern = @"\\[[0-9a-zA-Z\\u4e00-\\u9fa5]+\\]"; 2 3 _emojiDic = @{@"[大笑]":@"smile",@"[愛心]":@"love"}; 4 5 -(NSMutableAttributedString*)getEmojiText:(NSString*)content{ 6 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:content attributes:self.typingAttributes]; 7 static NSRegularExpression *regExpress = nil; 8 if(regExpress == nil){ 9 regExpress = [[NSRegularExpression alloc]initWithPattern:emojiTextPttern options:0 error:nil]; 10 } 11 //經過正則表達式識別出emojiText 12 NSArray *matches = [regExpress matchesInString:content options:0 range:NSMakeRange(0, content.length)]; 13 if(matches.count > 0){ 14 for(NSTextCheckingResult *result in [matches reverseObjectEnumerator]){ 15 NSString *emojiText = [content substringWithRange:result.range]; 16 //構造NSTextAttachment對象 17 NSTextAttachment *attachment = [self createEmojiAttachment:emojiText]; 18 if(attachment){ 19 NSAttributedString *rep = [NSAttributedString attributedStringWithAttachment:attachment]; 20 //在對應的位置替換 21 [attributedString replaceCharactersInRange:result.range withAttributedString:rep]; 22 } 23 } 24 } 25 return attributedString; 26 }
二、構造NSTextAttachment的過程爲:app
1 -(NSTextAttachment*)createEmojiAttachment:(NSString*)emojiText{ 2 if(emojiText.length==0){ 3 return nil; 4 } 5 NSString *imageName = _emojiDic[emojiText]; 6 if(imageName.length == 0){ 7 return nil; 8 } 9 UIImage *image = [UIImage imageNamed:imageName]; 10 if(image == nil){ 11 return nil; 12 } 13 //把圖片縮放到符合當前textview行高的大小 14 CGFloat emojiWHScale = image.size.width/1.0/image.size.height; 15 CGSize emojiSize = CGSizeMake(self.font.lineHeight*emojiWHScale, self.font.lineHeight); 16 UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, emojiSize.width, emojiSize.height)]; 17 imageView.image = image; 18 //防止模糊 19 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale); 20 [imageView.layer renderInContext:UIGraphicsGetCurrentContext()]; 21 UIImage *emojiImage = UIGraphicsGetImageFromCurrentImageContext(); 22 UIGraphicsEndImageContext(); 23 EmojiTextAttachment *attachment = [[EmojiTextAttachment alloc]init]; 24 attachment.image = emojiImage; 25 attachment.emojiText = emojiText; 26 attachment.bounds = CGRectMake(0, -3, emojiImage.size.width, emojiImage.size.height); 27 return attachment; 28 }
其中EmojiTextAttachment繼承了NSTextAttachment類,主要是爲了記住自定義表情對應的emojiText,在後面實現copy和cut須要用到。EmojiTextAttachment聲明爲:字體
1 @interface EmojiTextAttachment : NSTextAttachment 2 3 /** 4 保存emojiText的值 5 */ 6 @property (nonatomic, strong) NSString *emojiText; 7 @end
三、實現對自定義表情的粘貼atom
從新paste方法便可spa
1 -(void)paste:(id)sender{ 2 UIPasteboard *defaultPasteboard = [UIPasteboard generalPasteboard]; 3 if(defaultPasteboard.string.length>0){ 4 NSRange range = self.selectedRange; 5 if(range.location == NSNotFound){ 6 range.location = self.text.length; 7 } 8 if([self.delegate textView:self shouldChangeTextInRange:range replacementText:defaultPasteboard.string]){ 9 NSAttributedString *newAttriString = [self getEmojiText:defaultPasteboard.string]; 10 [self insertAttriStringToTextview:newAttriString]; 11 } 12 return; 13 } 14 [super paste:sender]; 15 } 16 17 -(void)insertAttriStringToTextview:(NSAttributedString*)attriString{ 18 NSMutableAttributedString *mulAttriString = [[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText]; 19 NSRange range = self.selectedRange; 20 if(range.location == NSNotFound){ 21 range.location = self.text.length; 22 } 23 [mulAttriString insertAttributedString:attriString atIndex:range.location]; 24 self.attributedText = [mulAttriString copy]; 25 self.selectedRange = NSMakeRange(range.location+attriString.length, 0); 26 }
四、實現自定義表情的拷貝和剪切code
拷貝和剪切自定義表情的時候,不是獲取自定義表情對應的圖片而是自定義表情對應的emojiText,這也是咱們在上面要定義EmojiTextAttachment類的緣由。具體代碼以下:對象
1 -(void)copy:(id)sender{ 2 NSRange range = self.selectedRange; 3 NSString *content = [self getStrContentInRange:range]; 4 if(content.length>0){ 5 UIPasteboard *defaultPasteboard = [UIPasteboard generalPasteboard]; 6 [defaultPasteboard setString:content]; 7 return; 8 } 9 [super copy:sender]; 10 } 11 -(void)cut:(id)sender{ 12 NSRange range = self.selectedRange; 13 NSString *content = [self getStrContentInRange:range]; 14 if(content.length>0){ 15 [super cut:sender]; 16 UIPasteboard *defaultPasteboard = [UIPasteboard generalPasteboard]; 17 [defaultPasteboard setString:content]; 18 return; 19 } 20 [super cut:sender]; 21 } 22 23 /** 24 把textview的attributedText轉化爲NSString,其中把自定義表情轉化爲emojiText 25 26 @param range 轉化的範圍 27 @return 返回轉化後的字符串 28 */ 29 -(NSString*)getStrContentInRange:(NSRange)range{ 30 NSMutableString *result = [[NSMutableString alloc]initWithCapacity:10]; 31 NSRange effectiveRange = NSMakeRange(range.location,0); 32 NSUInteger length = NSMaxRange(range); 33 while (NSMaxRange(effectiveRange)<length) { 34 NSTextAttachment *attachment = [self.attributedText attribute:NSAttachmentAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange]; 35 if(attachment){ 36 if([attachment isKindOfClass:[EmojiTextAttachment class]]){ 37 EmojiTextAttachment *emojiAttachment = (EmojiTextAttachment*)attachment; 38 [result appendString:emojiAttachment.emojiText]; 39 } 40 } 41 else{ 42 NSString *subStr = [self.text substringWithRange:effectiveRange]; 43 [result appendString:subStr]; 44 } 45 } 46 return [result copy]; 47 }
經過上面的努力,咱們已經實現了全部的功能。可是咱們用起來的時候,會發現兩個問題:blog
一、在自定義表情的後面輸入文本,UITextview設置的屬性(好比字體大小,顏色等)都消失,又變成了默認屬性;繼承
二、在ios 10.11系統上,長按自定義表情的時候,keyboard會退出,而且彈出保存圖片的系統窗口,這樣的體驗也很差。
解決第一個問題:
咱們在初始化的時候保存一下UITextview的typingAttributes屬性,而後在每次UITextview的內容將要發生變化的時候,重置一下他的該屬性。
1 @interface ViewController ()<UITextViewDelegate> 2 @property (nonatomic, strong)CustomTextView *textView; 3 4 @property (nonatomic, strong)NSDictionary *typingAttributes; 5 @end 6 7 -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ 8 textView.typingAttributes = self.typingAttributes; 9 return YES; 10 }
解決第二個問題:
只須要實現一個delegate方法就行,直接返回NO
1 -(BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction{ 2 return NO; 3 }