新浪微博客户端(58)-处理点击微博内容中的关键字
DJStatus.m
// 创建一个用于包含特殊文本的集合 NSMutableArray *specialTextArray = [NSMutableArray array]; // 取出数组中的文本块进行拼接 for (DJStatusPart *part in statusParts) { NSAttributedString *subString = nil; if (part.isSpecial) { // 判断是否是特殊文本(若是特殊文本,则进行特殊处理,超链接:变色,表情文本:更换成表情图片) if (part.isEmotion) { //【Emotion表情】 DJEmotion *emotion = [DJEmotionTool emotionWithChs:part.text]; if (emotion) { // 找到了当前文本对应的Emotion表情 NSString *emotionName = emotion.png; NSString *imageName = nil; if ([emotionName hasPrefix:@"d_"] || [emotionName hasPrefix:@"f_"] || [emotionName hasPrefix:@"h_"] || [emotionName hasPrefix:@"l_"] || [emotionName hasPrefix:@"o_"] || [emotionName hasPrefix:@"w_"]) { // 默认表情 imageName = [NSString stringWithFormat:@"EmotionIcons/default/%@",emotion.png]; } else if ([emotionName hasPrefix:@"lxh_"]) { // 浪小花表情 imageName = [NSString stringWithFormat:@"EmotionIcons/lxh/%@",emotion.png]; } NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; attachment.image = [UIImage imageNamed:imageName]; attachment.bounds = CGRectMake(0, -4, 16, 16); subString = [NSAttributedString attributedStringWithAttachment:attachment]; } else { // 未在plist列表中找到对应表情,或该表情为Emoji subString = [[NSAttributedString alloc] initWithString:part.text]; } } else { // 【超链接&昵称】将字体颜色修改为DJColor(82,118,169) subString = [[NSAttributedString alloc] initWithString:part.text attributes:@{NSForegroundColorAttributeName : DJColor(82, 118, 169)}]; // 保存非表情的特殊文字 NSUInteger location = attributedText.length; NSUInteger length = part.text.length; DJSpecialText *specialText = [[DJSpecialText alloc] init]; specialText.text = part.text; specialText.range = NSMakeRange(location, length); [specialTextArray addObject:specialText]; } } else { // 【普通文本】不做任何处理 subString = [[NSAttributedString alloc] initWithString:part.text]; } [attributedText appendAttributedString:subString]; } // 将保存有特殊文本的集合添加到DJStatus的属性上 [attributedText addAttribute:@"special" value:specialTextArray range:NSMakeRange(0, 1)];
DJSpecialText.h
#import <Foundation/Foundation.h> @interface DJSpecialText : NSObject /** 特殊文本的内容 */ @property (nonatomic,copy) NSString *text; /** 特殊文本的范围 */ @property (nonatomic,assign) NSRange range; /** 特殊文本的矩形框 NSArray中存放的是CGRect对象 */ @property (nonatomic,strong) NSArray *rects; @end
DJStatusTextView.m
#import "DJStatusTextView.h" #import "DJSpecialText.h" #define DJStatusTextViewCoverTag 999 @implementation DJStatusTextView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.backgroundColor = [UIColor clearColor]; self.editable = NO; // 禁止编辑 self.textContainerInset = UIEdgeInsetsMake(0, -5, 0, -5); self.scrollEnabled = NO; // 禁止滚动 } return self; } // 类似于android里的 MotionEvent.ACTION_DOWN - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; // 获取触摸点的坐标 CGPoint touchPoint = [touch locationInView:self]; // 计算特殊文本所在的矩形框 [self setupAttributedTextRects]; // 根据触摸点判断是否在点击的是特殊文本 DJSpecialText *specialText = [self specialTextWithTouchPoint:touchPoint]; // 如果是特殊文本,则为其添加一个浅色背景 if (specialText) { NSArray *rectArray = specialText.rects; for (NSValue *rectValue in rectArray) { CGRect rect = rectValue.CGRectValue; // 取出当前文本所在的CGRect // 添加浅色背景 UIView *cover = [[UIView alloc] init]; cover.backgroundColor = DJColor(179, 193, 211); cover.frame = rect; cover.tag = DJStatusTextViewCoverTag; cover.layer.cornerRadius = 3; [self insertSubview:cover atIndex:0]; } } DJLog(@"点击了TextView......."); } /** 计算特殊文本所在的CGRect,并将其作为特殊文本的属性 */ - (void)setupAttributedTextRects { NSArray *specialTextArray = [self.attributedText attribute:@"special" atIndex:0 effectiveRange:NULL]; for (DJSpecialText *specialText in specialTextArray) { // 设置当前TextView选中范围 self.selectedRange = specialText.range; // 获取选中文字的CGRect NSArray *selectionRects = [self selectionRectsForRange:self.selectedTextRange]; // 清空当前TextView选中范围 self.selectedRange = NSMakeRange(0, 0); NSMutableArray *attributeTextRects = [NSMutableArray array]; // 属性文本所在的CGRect数组 for (UITextSelectionRect *selectionRect in selectionRects) { // 取出当前特殊文本所在的CGRect CGRect rect = selectionRect.rect; // 若存在垃圾矩形框,则跳过 if (rect.size.width == 0 || rect.size.height == 0) continue; // 添加当前CGRect到其所在的CGRect数组(由于CGRect是结构体,无法直接添加到NSArray当中,故需要将其转换为NSValue对象,再添加到数组) [attributeTextRects addObject:[NSValue valueWithCGRect:rect]]; } // 将特殊文本所在CGRect集合设置进去 specialText.rects = attributeTextRects; } } /** 传入一个触摸点,返回当前触摸点所在的特殊文本,若没有,则返回空 */ - (DJSpecialText *)specialTextWithTouchPoint:(CGPoint)touchPoint { NSArray *specialTextArray = [self.attributedText attribute:@"special" atIndex:0 effectiveRange:NULL]; for (DJSpecialText *specialText in specialTextArray) { // 取出特殊文本所在的CGRect范围 NSArray *rectArray = specialText.rects; // 遍历当前特殊文本所在的矩形框 for (NSValue *rectValue in rectArray) { if (CGRectContainsPoint(rectValue.CGRectValue, touchPoint)) { return specialText; // 若当前矩形框包含触摸点,则将该特殊文本返回 } } } return nil; } // 类似于android里的 MotionEvent.ACTION_UP - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self touchesCancelled:touches withEvent:event]; }); } // 当触摸事件被打断时调用此方法 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 去掉特殊字符后面的高亮背景 for (UIView *child in self.subviews) { if (child.tag == DJStatusTextViewCoverTag) [child removeFromSuperview]; } } // iOS触摸事件传递 // 1.判断点在谁身上,调用pointInside,返回YES的控件就是触摸点所在的控件 // 2.由触摸点所在的UI控件选出处理事件的UI控件,调用hitTest:(CGPoint)point withEvent // 此方法是用来决定触摸事件是由该控件上的哪一个子控件来处理 //- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { // // //} // 这个方法类似于android里面的onTouchEvent,需要返回一个布尔值,该布尔值代表是否由该控件消费掉此触摸事件 - (BOOL)pointInside:(CGPoint)touchPoint withEvent:(UIEvent *)event { [self setupAttributedTextRects]; DJSpecialText *specialText = [self specialTextWithTouchPoint:touchPoint]; if (specialText) { // 判断当前触摸点是否在特殊文本之上,若在,则认为是点击了特殊文本;若不在,则认为是点击了tableView上的Cell. return YES; } else { return NO; } } @end
最终效果: