点击对应文字响应事件的Label

最近在做社交功能, 遇到一个坎, 点击对应的文字响应事件, 就像微博里那种, 有@谁的 这个后面就能点击跳到某某人的主页, 如果只是在前面, 像微博中的话题, 带#号的, 而且也不长, 这个倒是好解决, 一开始想到UITextView的超链接, 但是UITextView在XIB里又不能根据文字的多少而变大小, 我的想法是用label, 但是label怎么获取点击的范围, 这是重点, 当然, 如果只是像前面带#话题#这种, 在没有这个之前, 我的解决方法是, 在这个label上再加一个label用于显示#话题#,下面的label将这几个字拼接在前面, 用属性字符串将这几个字颜色搞成透明的, 只监听上面这个label的点击手势即可, 但是长远考虑, 这样不是长久之计, 如果很长, 换行了, 那就不好办了, 只能硬着头皮上了......

先看下效果图吧


可点击的label

基本上实现了文字点击响应事件, 简单测试, 应该可用

最主要的难点就是在于获取特定文字的范围
网上找过demo, 但是实现的思路不是特别明白, 所以我还是按照自己的思路来整

  • 设定可点击的文字
  • 获取可点击文字在控件上的rect
  • 点击的时候, 遍历控件保存的rect数组, 在哪个数组里
  • 找到对应的响应对象去响应事件

不知道会不会有人想说直接用TextView不就好了么?我要用label的原因, 就是因为它在XIB里能伸缩, 要是算出内容大小, 改变约束也不是不可以的

简单点说一下我的做法, 我是在label上加了一个textView, 获取文字的范围, 暂时没有找到能代理textView能去干这件事的

- (void)setUp{
    self.clickItems = [NSMutableArray array];
    self.clickAttachmentItems = [NSMutableDictionary dictionary];
    self.userInteractionEnabled = YES;
    self.clipsToBounds = YES;
    self.textView.font = self.font;
    self.textView.frame = CGRectMake(-5, -8, self.bounds.size.width + 10,  self.bounds.size.height + 16);
}

为什么textView的x值和y值要设为-5和-8呢, 做过有占位文件的TextView的童鞋就知道了, textView的文字显示, 与边缘有间距, 距离左边就是5了, 上面差不多就是8这个样子, 实在不行自己再xib里调调就知道了
懒加载的一个TextView, 将背景色设为透明, 将文字颜色设为透明, 设为透明的主要原因是label上去显示内容(其实也可以用textView去显示内容的)

NSString *str = @"呵呵哒 :呵呵呵呵呵呵呵呵额哈哈哈呵呵呵呵呵 这里可以点击 呵呵呵额哈哈哈呵呵呵呵呵呵呵呵额哈哈哈呵呵呵呵呵呵呵呵额哈哈哈呵呵呵";
    
    self.label.text = str;
    
    self.label.font = [UIFont systemFontOfSize:20];
    
    [self.label addClickText:@"呵呵哒 :" attributeds:@{NSForegroundColorAttributeName : [UIColor orangeColor]} transmitBody:(id)@"呵呵哒 被点击了" clickItemBlock:^(id transmitBody) {
        [[[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:@"%@", transmitBody] delegate:self cancelButtonTitle:@"取消" otherButtonTitles: nil] show];
    }];
    
    [self.label addClickText:@"这里可以点击" attributeds:@{NSForegroundColorAttributeName : [UIColor greenColor]} transmitBody:(id)@"确实可以点击" clickItemBlock:^(id transmitBody) {
        [[[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:@"%@", transmitBody] delegate:self cancelButtonTitle:@"取消" otherButtonTitles: nil] show];
    }];

这是demo里的添加点击事件的代码
看看具体的实现吧

- (void)addClickText:(NSString *)text attributeds:(NSDictionary *)attributeds transmitBody:(id)transmitBody clickItemBlock:(FMLinkLabelClickItemBlock)clickBlock{
    // 根据现有的文本生成可变的富文本
    NSMutableAttributedString *attr = nil;
    if (self.attributedText) {
        attr = [self.attributedText mutableCopy];
    } else {
        attr = [[[NSAttributedString alloc] initWithString:self.text] mutableCopy];
    }
    // 锁定可以点击文字的范围
    NSRange range = [[attr string] rangeOfString:text];
    
    if (range.location != NSNotFound) {
        [attr setAttributes:attributeds range:range];
        self.attributedText = attr;
        FMLinkLabelClickItem *item = [FMLinkLabelClickItem itemWithText:text range:range transmitBody:transmitBody];
        item.clickBlock = clickBlock;
        [self addClickItem:item];
        [attr setAttributes:@{NSFontAttributeName : self.font} range:NSMakeRange(0, attr.length)];
        self.textView.text = [attr string];
    }
}
- (void)addClickItem:(FMLinkLabelClickItem *)item{
    // 循环遍历每一个字符的范围, 防止换行导致的部分不能响应
    for (int i = 0; i < item.range.length; i++) {
        
        NSRange range = NSMakeRange(item.range.location + i, 1);
        // 设置TextView的选中范围
        self.textView.selectedRange = range;
        // 获取选中范围在textView上的尺寸
        CGRect rect = [self.textView firstRectForRange:self.textView.selectedTextRange];
        // 将选中范围清空
        self.textView.selectedRange = NSMakeRange(0, 0);
        // 转换坐标系到本身上来
        CGRect textRect = [self.textView convertRect:rect toView:self];
        
        // 有点不准确, textView设置内容的时候container有偏移量吧  具体不太清楚
        NSInteger remainder = (NSInteger)textRect.origin.y % (NSInteger)self.font.lineHeight;
        
        if (remainder > 0) {
            textRect.origin.y += (self.font.lineHeight - remainder);
        }
        // 加入这个尺寸到数组 方便判断
        [item.textRects addObject:[NSValue valueWithCGRect:textRect]];
        
    }
    
    [self.clickItems addObject:item];
}

label的交互已经开启, 监听点击事件就好了

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    // 遍历所有的点击事件
    [self.clickItems enumerateObjectsUsingBlock:^(FMLinkLabelClickItem *obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 遍历点击事件里的点击范围
        [obj.textRects enumerateObjectsUsingBlock:^(NSValue *rectValue, NSUInteger idx, BOOL * _Nonnull stop1) {
            if (CGRectContainsPoint([rectValue CGRectValue], point)) {
                if (obj.clickBlock) {
                    obj.clickBlock(obj.transmitBody);
                    isClickText = YES;
                    *stop = YES;
                    *stop1 = YES;
                }
            }
        }];
    }];
}

用的block传值的, 用代理回调也是可以的, 感觉没有block来的直观, (后续会加上代理), 上面代码的注释都有, 就不废话了

献上demo地址FMLinkLabel

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 最初在简书注册时,只是为了读煤老板有关孵化器的文集,于是在3月份开始实习的第一天,我就注册了简书。 一个互联网小白...
    柳三陌阅读 167评论 3 3
  • dispatch semaphore1.dispatch semaphore是持有计数的信号,该计数是多线程编程中...
    yehkong阅读 375评论 0 0