新浪微博客户端(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

最终效果:

原文地址:https://www.cnblogs.com/yongdaimi/p/6172271.html