123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- //
- // 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; i<maxDetctIndex; i++) {
- // NSLog(@"%@",[emojiKey.string substringToIndex:3+i]);
- imageName = emojiDict[[emojiKey.string substringToIndex:3+i]];
- if (imageName) {
- otherAppendStr = [emojiKey attributedSubstringFromRange:NSMakeRange(3+i, emojiKey.length-3-i)];
- break;
- }
- }
- }
- }
-
- if (imageName) {
- // 这里不用空格,空格有个问题就是连续空格的时候只显示在一行
- NSMutableAttributedString *replaceStr = [[NSMutableAttributedString alloc] initWithString:kEmojiReplaceCharacter];
- NSRange __range = NSMakeRange([attrStr length], 1);
- [attrStr appendAttributedString:replaceStr];
- if (otherAppendStr) { //有其他需要添加的
- [attrStr appendAttributedString:otherAppendStr];
- }
-
- // 定义回调函数
- CTRunDelegateCallbacks callbacks;
- callbacks.version = kCTRunDelegateCurrentVersion;
- callbacks.getAscent = ascentCallback;
- callbacks.getDescent = descentCallback;
- callbacks.getWidth = widthCallback;
- callbacks.dealloc = deallocCallback;
-
- // 这里设置下需要绘制的图片的大小,这里我自定义了一个结构体以便于存储数据
- CustomGlyphMetricsRef metrics = malloc(sizeof(CustomGlyphMetrics));
- metrics->width = 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; i<maxIndex; i++) {
- if (self.disableThreeCommon&&i<kURLActionCount-2) {
- continue;
- }
- NSString *urlAction = kURLActions[i];
- [regexps[i] enumerateMatchesInString:mutableAttributedString.string options:0 range:stringRange usingBlock:^(NSTextCheckingResult *result, __unused NSMatchingFlags flags, __unused BOOL *stop) {
-
- //检查是否和之前记录的有交集,有的话则忽略
- for (NSTextCheckingResult *record in results){
- if (NSMaxRange(NSIntersectionRange(record.range, result.range))>0){
- 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; i<kURLActionCount; i++) {
- if ([result.replacementString hasPrefix:kURLActions[i]]) {
- NSString *content = [result.replacementString substringFromIndex:kURLActions[i].length];
- //type的数组和i刚好对应
- [self.delegate mlEmojiLabel:self didSelectLink:content withType:i];
-
- self.activeLink = nil;
- return;
- }
- }
- }
- }
-
- [super touchesEnded:touches withEvent:event];
- }
- #pragma mark - UIResponderStandardEditActions
- - (void)copy:(__unused id)sender {
- if (!self.emojiText) {
- return;
- }
-
- NSString *text = [self.emojiText isKindOfClass:[NSAttributedString class]]?((NSAttributedString*)self.emojiText).string:self.emojiText;
-
- if (text.length>0) {
- [[UIPasteboard generalPasteboard] setString:text];
- }
- }
- //#pragma mark - other
- //为了生成plist方便的一个方法罢了
- //- (void)initPlist
- //{
- // NSString *testString = @"/::)/::~/::B/::|/:8-)/::</::$/::X/::Z/::'(/::-|/::@/::P/::D/::O/::(/::+/:--b/::Q/::T/:,@P/:,@-D/::d/:,@o/::g/:|-)/::!/::L/::>/::,@/:,@f/::-S/:?/:,@x/:,@@/::8/:,@!/:!!!/:xx/:bye/:wipe/:dig/:handclap/:&-(/:B-)/:<@/:@>/::-O/:>-|/:P-(/::'|/:X-)/::*/:@x/:8*/:pd/:<W>/: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/:<L>/:jump/:shake/:<O>/: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
|