// // MLEmojiLabel.m // MLEmojiLabel // // Created by molon on 5/19/14. // Copyright (c) 2014 molon. All rights reserved. // #import "MLEmojiLabel.h" #pragma mark - 正则列表 #define REGULAREXPRESSION_OPTION(regularExpression,regex,option) \ \ static inline NSRegularExpression * k##regularExpression() { \ static NSRegularExpression *_##regularExpression = nil; \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ _##regularExpression = [[NSRegularExpression alloc] initWithPattern:(regex) options:(option) error:nil];\ });\ \ return _##regularExpression;\ }\ #define REGULAREXPRESSION(regularExpression,regex) REGULAREXPRESSION_OPTION(regularExpression,regex,NSRegularExpressionCaseInsensitive) REGULAREXPRESSION(URLRegularExpression,@"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)") REGULAREXPRESSION(PhoneNumerRegularExpression, @"\\d{3}-\\d{8}|\\d{3}-\\d{7}|\\d{4}-\\d{8}|\\d{4}-\\d{7}|1+[358]+\\d{9}|\\d{8}|\\d{7}") REGULAREXPRESSION(EmailRegularExpression, @"[A-Z0-9a-z\\._%+-]+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2,4}") REGULAREXPRESSION(AtRegularExpression, @"@[\\u4e00-\\u9fa5\\w\\-]+") //@"#([^\\#|.]+)#" REGULAREXPRESSION_OPTION(PoundSignRegularExpression, @"#([\\u4e00-\\u9fa5\\w\\-]+)#", NSRegularExpressionCaseInsensitive) //微信的表情符其实不是这种格式,这个格式的只是让人看起来更友好。。 //REGULAREXPRESSION(EmojiRegularExpression, @"\\[[a-zA-Z0-9\\u4e00-\\u9fa5]+\\]") //@"/:[\\w:~!@$&*()|+<>',?-]{1,8}" , // @"/:[\\x21-\\x2E\\x30-\\x7E]{1,8}" ,经过检测发现\w会匹配中文,好奇葩。 REGULAREXPRESSION(SlashEmojiRegularExpression, @"/:[\\x21-\\x2E\\x30-\\x7E]{1,8}") const CGFloat kLineSpacing = 4.0; const CGFloat kAscentDescentScale = 0.25; //在这里的话无意义,高度的结局都是和宽度一样 const CGFloat kEmojiWidthRatioWithLineHeight = 1.15;//和字体高度的比例 const CGFloat kEmojiOriginYOffsetRatioWithLineHeight = 0.10; //表情绘制的y坐标矫正值,和字体高度的比例,越大越往下 NSString *const kCustomGlyphAttributeImageName = @"CustomGlyphAttributeImageName"; #define kEmojiReplaceCharacter @"\uFFFC" #define kURLActionCount 5 NSString * const kURLActions[] = {@"url->",@"email->",@"phoneNumber->",@"at->",@"poundSign->"}; /** * 搞个管理器,否则自定义plist的话,每个label都会有个副本很操蛋 */ @interface MLEmojiLabelRegexPlistManager : NSObject - (NSDictionary*)emojiDictForKey:(NSString*)key; @property (nonatomic, strong) NSMutableDictionary *emojiDictRecords; @property (nonatomic, strong) NSMutableDictionary *emojiRegularExpressions; @end @implementation MLEmojiLabelRegexPlistManager + (instancetype)sharedInstance { static MLEmojiLabelRegexPlistManager *_sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [[[self class] alloc]init]; }); return _sharedInstance; } #pragma mark - getter - (NSMutableDictionary *)emojiDictRecords { if (!_emojiDictRecords) { _emojiDictRecords = [NSMutableDictionary new]; } return _emojiDictRecords; } - (NSMutableDictionary *)emojiRegularExpressions { if (!_emojiRegularExpressions) { _emojiRegularExpressions = [NSMutableDictionary new]; } return _emojiRegularExpressions; } #pragma mark - common - (NSDictionary*)emojiDictForKey:(NSString*)key { NSAssert(key&&key.length>0, @"emojiDictForKey:参数不得为空"); if (self.emojiDictRecords[key]) { return self.emojiDictRecords[key]; } NSString *emojiFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:key]; NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:emojiFilePath]; NSAssert(dict,@"表情字典%@找不到",key); self.emojiDictRecords[key] = dict; return self.emojiDictRecords[key]; } - (NSRegularExpression *)regularExpressionForRegex:(NSString*)regex { NSAssert(regex&®ex.length>0, @"regularExpressionForKey:参数不得为空"); if (self.emojiRegularExpressions[regex]) { return self.emojiRegularExpressions[regex]; } NSRegularExpression *re = [[NSRegularExpression alloc] initWithPattern:regex options:NSRegularExpressionCaseInsensitive error:nil]; NSAssert(re,@"正则%@有误",regex); self.emojiRegularExpressions[regex] = re; return self.emojiRegularExpressions[regex]; } @end @interface TTTAttributedLabel(MLEmojiLabel) @property (readwrite, nonatomic, strong) TTTAttributedLabelLink *activeLink; - (void)commonInit; - (NSArray *)addLinksWithTextCheckingResults:(NSArray *)results attributes:(NSDictionary *)attributes; - (void)drawStrike:(CTFrameRef)frame inRect:(CGRect)rect context:(CGContextRef)c; @end @interface MLEmojiLabel() @property (nonatomic, weak) NSRegularExpression *customEmojiRegularExpression; @property (nonatomic, weak) NSDictionary *customEmojiDictionary; //这玩意如果有也是在MLEmojiLabelPlistManager单例里面存着 @property (nonatomic, assign) BOOL ignoreSetText; //留个初始副本 @property (nonatomic, copy) id emojiText; @end @implementation MLEmojiLabel #pragma mark - 表情包字典 + (NSDictionary *)emojiDictionary { static NSDictionary *emojiDictionary = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *emojiFilePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"MLEmoji_ExpressionImage.plist"]; emojiDictionary = [[NSDictionary alloc] initWithContentsOfFile:emojiFilePath]; }); return emojiDictionary; } #pragma mark - 表情 callback typedef struct CustomGlyphMetrics { CGFloat ascent; CGFloat descent; CGFloat width; } CustomGlyphMetrics, *CustomGlyphMetricsRef; static void deallocCallback(void *refCon) { free(refCon), refCon = NULL; } static CGFloat ascentCallback(void *refCon) { CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; return metrics->ascent; } static CGFloat descentCallback(void *refCon) { CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; return metrics->descent; } static CGFloat widthCallback(void *refCon) { CustomGlyphMetricsRef metrics = (CustomGlyphMetricsRef)refCon; return metrics->width; } #pragma mark - 初始化和TTT的一些修正 - (void)commonInit { [super commonInit]; self.numberOfLines = 0; self.font = [UIFont systemFontOfSize:14.0]; self.textColor = [UIColor blackColor]; self.backgroundColor = [UIColor clearColor]; self.lineBreakMode = NSLineBreakByCharWrapping; self.lineSpacing = kLineSpacing; //默认行间距 //链接默认样式重新设置 NSMutableDictionary *mutableLinkAttributes = [@{(NSString *)kCTUnderlineStyleAttributeName:@(NO)}mutableCopy]; NSMutableDictionary *mutableActiveLinkAttributes = [@{(NSString *)kCTUnderlineStyleAttributeName:@(NO)}mutableCopy]; UIColor *commonLinkColor = [UIColor colorWithRed:0.112 green:0.000 blue:0.791 alpha:1.000]; //点击时候的背景色 [mutableActiveLinkAttributes setValue:(__bridge id)[[UIColor colorWithWhite:0.631 alpha:1.000] CGColor] forKey:(NSString *)kTTTBackgroundFillColorAttributeName]; if ([NSMutableParagraphStyle class]) { [mutableLinkAttributes setObject:commonLinkColor forKey:(NSString *)kCTForegroundColorAttributeName]; [mutableActiveLinkAttributes setObject:commonLinkColor forKey:(NSString *)kCTForegroundColorAttributeName]; } else { [mutableLinkAttributes setObject:(__bridge id)[commonLinkColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; [mutableActiveLinkAttributes setObject:(__bridge id)[commonLinkColor CGColor] forKey:(NSString *)kCTForegroundColorAttributeName]; } self.linkAttributes = [NSDictionary dictionaryWithDictionary:mutableLinkAttributes]; self.activeLinkAttributes = [NSDictionary dictionaryWithDictionary:mutableActiveLinkAttributes]; } /** * 如果是有attributedText的情况下,有可能会返回少那么点的,这里矫正下 * */ - (CGSize)sizeThatFits:(CGSize)size { if (!self.attributedText) { return [super sizeThatFits:size]; } CGSize rSize = [super sizeThatFits:size]; rSize.height +=1; return rSize; } //这里是抄TTT里的,因为他不是放在外面的 static inline CGFloat TTTFlushFactorForTextAlignment(NSTextAlignment textAlignment) { switch (textAlignment) { case NSTextAlignmentCenter: return 0.5f; case NSTextAlignmentRight: return 1.0f; case NSTextAlignmentLeft: default: return 0.0f; } } #pragma mark - 绘制表情 - (void)drawStrike:(CTFrameRef)frame inRect:(CGRect)rect context:(CGContextRef)c { [super drawStrike:frame inRect:rect context:c]; //PS:这个是在TTT里drawFramesetter....方法最后做了修改的基础上。 CGFloat emojiWith = self.font.lineHeight*kEmojiWidthRatioWithLineHeight; CGFloat emojiOriginYOffset = self.font.lineHeight*kEmojiOriginYOffsetRatioWithLineHeight; //修正绘制offset,根据当前设置的textAlignment CGFloat flushFactor = TTTFlushFactorForTextAlignment(self.textAlignment); CFArrayRef lines = CTFrameGetLines(frame); NSInteger numberOfLines = self.numberOfLines > 0 ? MIN(self.numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines); CGPoint lineOrigins[numberOfLines]; CTFrameGetLineOrigins(frame, CFRangeMake(0, numberOfLines), lineOrigins); BOOL truncateLastLine = (self.lineBreakMode == NSLineBreakByTruncatingHead || self.lineBreakMode == NSLineBreakByTruncatingMiddle || self.lineBreakMode == NSLineBreakByTruncatingTail); CFRange textRange = CFRangeMake(0, (CFIndex)[self.attributedText length]); for (CFIndex lineIndex = 0; lineIndex < numberOfLines; lineIndex++) { CTLineRef line = CFArrayGetValueAtIndex(lines, lineIndex); //这里其实是能获取到当前行的真实origin.x,根据textAlignment,而lineBounds.origin.x其实是默认一直为0的(不会受textAlignment影响) CGFloat penOffset = (CGFloat)CTLineGetPenOffsetForFlush(line, flushFactor, rect.size.width); CFIndex truncationAttributePosition = -1; //检测如果是最后一行,是否有替换... if (lineIndex == numberOfLines - 1 && truncateLastLine) { // Check if the range of text in the last line reaches the end of the full attributed string CFRange lastLineRange = CTLineGetStringRange(line); if (!(lastLineRange.length == 0 && lastLineRange.location == 0) && lastLineRange.location + lastLineRange.length < textRange.location + textRange.length) { // Get correct truncationType and attribute position truncationAttributePosition = lastLineRange.location; NSLineBreakMode lineBreakMode = self.lineBreakMode; // Multiple lines, only use UILineBreakModeTailTruncation if (numberOfLines != 1) { lineBreakMode = NSLineBreakByTruncatingTail; } switch (lineBreakMode) { case NSLineBreakByTruncatingHead: break; case NSLineBreakByTruncatingMiddle: truncationAttributePosition += (lastLineRange.length / 2); break; case NSLineBreakByTruncatingTail: default: truncationAttributePosition += (lastLineRange.length - 1); break; } //如果要在truncationAttributePosition这个位置画表情需要忽略 } } //找到当前行的每一个要素,姑且这么叫吧。可以理解为有单独的attr属性的各个range。 for (id glyphRun in (__bridge NSArray *)CTLineGetGlyphRuns(line)) { //找到此要素所对应的属性 NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((__bridge CTRunRef) glyphRun); //判断是否有图像,如果有就绘制上去 NSString *imageName = attributes[kCustomGlyphAttributeImageName]; if (imageName) { CFRange glyphRange = CTRunGetStringRange((__bridge CTRunRef)glyphRun); if (glyphRange.location == truncationAttributePosition) { //这里因为glyphRange的length肯定为1,所以只做这一个判断足够 continue; } CGRect runBounds = CGRectZero; CGFloat runAscent = 0.0f; CGFloat runDescent = 0.0f; runBounds.size.width = (CGFloat)CTRunGetTypographicBounds((__bridge CTRunRef)glyphRun, CFRangeMake(0, 0), &runAscent, &runDescent, NULL); if (runBounds.size.width!=emojiWith) { //这一句是为了在某些情况下,例如单行省略号模式下,默认行为会将个别表情的runDelegate改变,也就改变了其大小。这时候会引起界面上错乱,这里做下检测(浮点数做等于判断似乎有点操蛋啊。。) continue; } runBounds.size.height = runAscent + runDescent; CGFloat xOffset = 0.0f; switch (CTRunGetStatus((__bridge CTRunRef)glyphRun)) { case kCTRunStatusRightToLeft: xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location + glyphRange.length, NULL); break; default: xOffset = CTLineGetOffsetForStringIndex(line, glyphRange.location, NULL); break; } runBounds.origin.x = penOffset + xOffset; runBounds.origin.y = lineOrigins[lineIndex].y; runBounds.origin.y -= runDescent; NSString *imagePath = [self.customEmojiBundleName?:@"MLEmoji_Expression.bundle" stringByAppendingPathComponent:imageName]; UIImage *image = [UIImage imageNamed:imagePath]; runBounds.origin.y -= emojiOriginYOffset; //稍微矫正下。 CGContextDrawImage(c, runBounds, image.CGImage); } } } } #pragma mark - main /** * 返回经过表情识别处理的Attributed字符串 */ - (NSMutableAttributedString*)mutableAttributeStringWithEmojiText:(NSAttributedString *)emojiText { //获取所有表情的位置 // NSArray *emojis = [kEmojiRegularExpression() matchesInString:emojiText // options:NSMatchingWithTransparentBounds // range:NSMakeRange(0, [emojiText length])]; NSArray *emojis = nil; if (self.customEmojiRegularExpression) { //自定义表情正则 emojis = [self.customEmojiRegularExpression matchesInString:emojiText.string options:NSMatchingWithTransparentBounds range:NSMakeRange(0, [emojiText length])]; }else{ emojis = [kSlashEmojiRegularExpression() matchesInString:emojiText.string options:NSMatchingWithTransparentBounds range:NSMakeRange(0, [emojiText length])]; } NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init]; NSUInteger location = 0; CGFloat emojiWith = self.font.lineHeight*kEmojiWidthRatioWithLineHeight; for (NSTextCheckingResult *result in emojis) { NSRange range = result.range; NSAttributedString *attSubStr = [emojiText attributedSubstringFromRange:NSMakeRange(location, range.location - location)]; [attrStr appendAttributedString:attSubStr]; location = range.location + range.length; NSAttributedString *emojiKey = [emojiText attributedSubstringFromRange:range]; NSDictionary *emojiDict = self.customEmojiRegularExpression?self.customEmojiDictionary:[MLEmojiLabel emojiDictionary]; //如果当前获得key后面有多余的,这个需要记录下 NSAttributedString *otherAppendStr = nil; NSString *imageName = emojiDict[emojiKey.string]; if (!self.customEmojiRegularExpression) { //微信的表情没有结束符号,所以有可能会发现过长的只有头部才是表情的段,需要循环检测一次。微信最大表情特殊字符是8个长度,检测8次即可 if (!imageName&&emojiKey.length>2) { NSUInteger maxDetctIndex = emojiKey.length>8+2?8:emojiKey.length-2; //从头开始检测是否有对应的 for (NSUInteger i=0; iwidth = emojiWith; metrics->ascent = 1/(1+kAscentDescentScale)*metrics->width; metrics->descent = metrics->ascent*kAscentDescentScale; CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, metrics); [attrStr addAttribute:(NSString *)kCTRunDelegateAttributeName value:(__bridge id)delegate range:__range]; CFRelease(delegate); // 设置自定义属性,绘制的时候需要用到 [attrStr addAttribute:kCustomGlyphAttributeImageName value:imageName range:__range]; } else { [attrStr appendAttributedString:emojiKey]; } } if (location < [emojiText length]) { NSRange range = NSMakeRange(location, [emojiText length] - location); NSAttributedString *attrSubStr = [emojiText attributedSubstringFromRange:range]; [attrStr appendAttributedString:attrSubStr]; } return attrStr; } - (void)setText:(id)text { NSParameterAssert(!text || [text isKindOfClass:[NSAttributedString class]] || [text isKindOfClass:[NSString class]]); if (self.ignoreSetText) { [super setText:text]; return; } if (!text) { self.emojiText = nil; [super setText:nil]; return; } //记录下原始的留作备份使用 self.emojiText = text; NSMutableAttributedString *mutableAttributedString = nil; if (self.disableEmoji) { mutableAttributedString = [text isKindOfClass:[NSAttributedString class]]?[text mutableCopy]:[[NSMutableAttributedString alloc]initWithString:text]; //直接设置text即可,这里text可能为attrString,也可能为String,使用TTT的默认行为 [super setText:text]; }else{ //如果是String,必须通过setText:afterInheritingLabelAttributesAndConfiguringWithBlock:来添加一些默认属性,例如字体颜色。这是TTT的做法,不可避免 if([text isKindOfClass:[NSString class]]){ mutableAttributedString = [self mutableAttributeStringWithEmojiText:[[NSAttributedString alloc] initWithString:text]]; //这里面会调用 self setText:,所以需要做个标记避免下无限循环 self.ignoreSetText = YES; [super setText:mutableAttributedString afterInheritingLabelAttributesAndConfiguringWithBlock:nil]; self.ignoreSetText = NO; }else{ mutableAttributedString = [self mutableAttributeStringWithEmojiText:text]; //这里虽然会调用 [super setText:mutableAttributedString]; } } NSRange stringRange = NSMakeRange(0, mutableAttributedString.length); NSRegularExpression * const regexps[] = {kURLRegularExpression(),kEmailRegularExpression(),kPhoneNumerRegularExpression(),kAtRegularExpression(),kPoundSignRegularExpression()}; NSMutableArray *results = [NSMutableArray array]; NSUInteger maxIndex = self.isNeedAtAndPoundSign?kURLActionCount:kURLActionCount-2; for (NSUInteger i=0; i0){ return; } } //添加链接 NSString *actionString = [NSString stringWithFormat:@"%@%@",urlAction,[self.text substringWithRange:result.range]]; //这里暂时用NSTextCheckingTypeCorrection类型的传递消息吧 //因为有自定义的类型出现,所以这样方便点。 NSTextCheckingResult *aResult = [NSTextCheckingResult correctionCheckingResultWithRange:result.range replacementString:actionString]; [results addObject:aResult]; }]; } //这里直接调用父类私有方法,好处能内部只会setNeedDisplay一次。一次更新所有添加的链接 [super addLinksWithTextCheckingResults:results attributes:self.linkAttributes]; } #pragma mark - size fit result - (CGSize)preferredSizeWithMaxWidth:(CGFloat)maxWidth { maxWidth = maxWidth - self.textInsets.left - self.textInsets.right; return [self sizeThatFits:CGSizeMake(maxWidth, CGFLOAT_MAX)]; } #pragma mark - setter - (void)setIsNeedAtAndPoundSign:(BOOL)isNeedAtAndPoundSign { _isNeedAtAndPoundSign = isNeedAtAndPoundSign; self.text = self.emojiText; //简单重新绘制处理下 } - (void)setLineBreakMode:(NSLineBreakMode)lineBreakMode { [super setLineBreakMode:lineBreakMode]; self.text = self.emojiText; //简单重新绘制处理下 } - (void)setDisableEmoji:(BOOL)disableEmoji { _disableEmoji = disableEmoji; self.text = self.emojiText; //简单重新绘制处理下 } - (void)setDisableThreeCommon:(BOOL)disableThreeCommon { _disableThreeCommon = disableThreeCommon; self.text = self.emojiText; //简单重新绘制处理下 } - (void)setCustomEmojiRegex:(NSString *)customEmojiRegex { _customEmojiRegex = customEmojiRegex; if (customEmojiRegex&&customEmojiRegex.length>0) { self.customEmojiRegularExpression = [[MLEmojiLabelRegexPlistManager sharedInstance]regularExpressionForRegex:customEmojiRegex]; }else{ self.customEmojiRegularExpression = nil; } self.text = self.emojiText; //简单重新绘制处理下 } - (void)setCustomEmojiPlistName:(NSString *)customEmojiPlistName { if (customEmojiPlistName&&customEmojiPlistName.length>0&&![[customEmojiPlistName lowercaseString] hasSuffix:@".plist"]) { customEmojiPlistName = [customEmojiPlistName stringByAppendingString:@".plist"]; } _customEmojiPlistName = customEmojiPlistName; if (customEmojiPlistName&&customEmojiPlistName.length>0) { self.customEmojiDictionary = [[MLEmojiLabelRegexPlistManager sharedInstance]emojiDictForKey:customEmojiPlistName]; }else{ self.customEmojiDictionary = nil; } self.text = self.emojiText; //简单重新绘制处理下 } - (void)setCustomEmojiBundleName:(NSString *)customEmojiBundleName { if (customEmojiBundleName&&customEmojiBundleName.length>0&&![[customEmojiBundleName lowercaseString] hasSuffix:@".bundle"]) { customEmojiBundleName = [customEmojiBundleName stringByAppendingString:@".bundle"]; } _customEmojiBundleName = customEmojiBundleName; self.text = self.emojiText; //简单重新绘制处理下 } - (void)setFont:(UIFont *)font { [super setFont:font]; self.text = self.emojiText; //简单重新绘制处理下 } #pragma mark - select link override - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //如果delegate实现了mlEmojiLabel自身的选择link方法 if(self.delegate&&[self.delegate respondsToSelector:@selector(mlEmojiLabel:didSelectLink:withType:)]){ if (self.activeLink&&self.activeLink.result.resultType==NSTextCheckingTypeCorrection) { NSTextCheckingResult *result = self.activeLink.result; //判断消息类型 for (NSUInteger i=0; i0) { [[UIPasteboard generalPasteboard] setString:text]; } } //#pragma mark - other //为了生成plist方便的一个方法罢了 //- (void)initPlist //{ // NSString *testString = @"/::)/::~/::B/::|/:8-)/::/::,@/:,@f/::-S/:?/:,@x/:,@@/::8/:,@!/:!!!/:xx/:bye/:wipe/:dig/:handclap/:&-(/:B-)/:<@/:@>/::-O/:>-|/:P-(/::'|/:X-)/::*/:@x/:8*/:pd/:/:beer/:basketb/:oo/:coffee/:eat/:pig/:rose/:fade/:showlove/:heart/:break/:cake/:li/:bome/:kn/:footb/:ladybug/:shit/:moon/:sun/:gift/:hug/:strong/:weak/:share/:v/:@)/:jj/:@@/:bad/:lvu/:no/:ok/:love/:/:jump/:shake/:/:circle/:kotow/:turn/:skip/:oY"; // NSMutableArray *testArray = [NSMutableArray array]; // NSMutableDictionary *testDict = [NSMutableDictionary dictionary]; // [kSlashEmojiRegularExpression() enumerateMatchesInString:testString options:0 range:NSMakeRange(0, testString.length) usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { // [testArray addObject:[testString substringWithRange:result.range]]; // [testDict setObject:[NSString stringWithFormat:@"Expression_%u",testArray.count] forKey:[testString substringWithRange:result.range]]; // }]; // // NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; // NSString *doc = [NSString stringWithFormat:@"%@/expression.plist",documentDir]; // NSLog(@"%@,length:%u",doc,testArray.count); // if ([testArray writeToFile:doc atomically:YES]) { // NSLog(@"归档expression.plist成功"); // } // doc = [NSString stringWithFormat:@"%@/expressionImage.plist",documentDir]; // if ([testDict writeToFile:doc atomically:YES]) { // NSLog(@"归档到expressionImage.plist成功"); // } // // // NSString *testString = @"[微笑][撇嘴][色][发呆][得意][流泪][害羞][闭嘴][睡][大哭][尴尬][发怒][调皮][呲牙][惊讶][难过][酷][冷汗][抓狂][吐][偷笑][愉快][白眼][傲慢][饥饿][困][惊恐][流汗][憨笑][悠闲][奋斗][咒骂][疑问][嘘][晕][疯了][衰][骷髅][敲打][再见][擦汗][抠鼻][鼓掌][糗大了][坏笑][左哼哼][右哼哼][哈欠][鄙视][委屈][快哭了][阴险][亲亲][吓][可怜][菜刀][西瓜][啤酒][篮球][乒乓][咖啡][饭][猪头][玫瑰][凋谢][嘴唇][爱心][心碎][蛋糕][闪电][炸弹][刀][足球][瓢虫][便便][月亮][太阳][礼物][拥抱][强][弱][握手][胜利][抱拳][勾引][拳头][差劲][爱你][NO][OK][爱情][飞吻][跳跳][发抖][怄火][转圈][磕头][回头][跳绳][投降]"; // // NSMutableArray *testArray = [NSMutableArray array]; // // NSMutableDictionary *testDict = [NSMutableDictionary dictionary]; // // [kEmojiRegularExpression() enumerateMatchesInString:testString options:0 range:NSMakeRange(0, testString.length) usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) { // // [testArray addObject:[testString substringWithRange:result.range]]; // // [testDict setObject:[NSString stringWithFormat:@"Expression_%ld",testArray.count] forKey:[testString substringWithRange:result.range]]; // // }]; // // NSString *documentDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; // // NSString *doc = [NSString stringWithFormat:@"%@/expression.plist",documentDir]; // // NSLog(@"%@,length:%ld",doc,testArray.count); // // if ([testArray writeToFile:doc atomically:YES]) { // // NSLog(@"归档expression.plist成功"); // // } // // doc = [NSString stringWithFormat:@"%@/expressionImage.plist",documentDir]; // // if ([testDict writeToFile:doc atomically:YES]) { // // NSLog(@"归档到expressionImage.plist成功"); // // } // // //} @end