iOS實(shí)現(xiàn)一段文字中部分有下劃線怜瞒,并且可以點(diǎn)擊

項(xiàng)目中有一個(gè)需求就是實(shí)現(xiàn)一段文字中有幾個(gè)特殊的字符可以有下劃線,并且可以進(jìn)行點(diǎn)擊般哼。
首先可以實(shí)現(xiàn)下劃線效果吴汪,首先想到的是UILabel和UITextView控件的 NSMutableAttributedString 屬性,考慮到可能會(huì)有點(diǎn)擊事件效果的實(shí)現(xiàn)蒸眠,這里選擇UITextView控件,因?yàn)閁ITextView有一個(gè)功能就是能通過(guò)NSRange獲得文字的相應(yīng)的Frame漾橙。
最終實(shí)現(xiàn)這種效果,帶下劃線的可以點(diǎn)擊楞卡,點(diǎn)擊可以設(shè)置背景顏色霜运,也可以不設(shè)置背景顏色,可以設(shè)置下?lián)Q線以及下劃線上面文字的顏色蒋腮。


實(shí)現(xiàn)效果.png

1淘捡、首先創(chuàng)建UITextView類

創(chuàng)建UITextView.png

2、ClickTextView類中聲明點(diǎn)擊回調(diào)的block池摧,這里回調(diào)用block進(jìn)行回調(diào)

/** 點(diǎn)擊回調(diào)的block */
typedef void(^clickTextViewPartBlock)(NSString *clickText);

3焦除、介紹下主要的實(shí)現(xiàn)方法
1>、這個(gè)方法主要是將下劃線對(duì)用的文字的frame作彤,文字內(nèi)容膘魄,點(diǎn)擊效果背景顏色存儲(chǔ)起來(lái),以供點(diǎn)擊的時(shí)候查詢

/**
 *  設(shè)置textView的部分為下劃線竭讳,并且使之可以點(diǎn)擊
 *
 *  @param underlineTextRange 需要下劃線的文字范圍创葡,如果NSRange范圍超出總的內(nèi)容,將過(guò)濾掉
 *  @param color              下劃線的顏色绢慢,以及下劃線上面文字的顏色
 *  @param coverColor         是否有點(diǎn)擊的背景灿渴,如果設(shè)置相關(guān)顏色的話,將會(huì)有點(diǎn)擊效果胰舆,如果為nil將沒(méi)有點(diǎn)擊效果
 *  @param block              點(diǎn)擊文字的時(shí)候的回調(diào)
 */

- (void)setUnderlineTextWithRange:(NSRange)underlineTextRange withUnderlineColor:(UIColor *)color withClickCoverColor:(UIColor *)coverColor withBlock:(clickTextViewPartBlock)block
{
    if (self.text.length < underlineTextRange.location+underlineTextRange.length) {
        return;
    }
    
    // 設(shè)置下劃線
    [self.content addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:underlineTextRange];
    
    //設(shè)置文字顏色
    if (color) {
        [self.content addAttribute:NSForegroundColorAttributeName value:color range:underlineTextRange];
    }
    self.attributedText = self.content;
    
    // 設(shè)置下劃線文字的點(diǎn)擊事件
    // self.selectedRange  影響  self.selectedTextRange
    self.selectedRange = underlineTextRange;
    
    // 獲取選中范圍內(nèi)的矩形框
    NSArray *selectionRects = [self selectionRectsForRange:self.selectedTextRange];
    // 清空選中范圍
    self.selectedRange = NSMakeRange(0, 0);
    // 可能會(huì)點(diǎn)擊的范圍的數(shù)組
    NSMutableArray *selectedArray = [[NSMutableArray alloc] init];
    for (UITextSelectionRect *selectionRect in selectionRects) {
        CGRect rect = selectionRect.rect;
        if (rect.size.width == 0 || rect.size.height == 0) {
            continue;
        }
        // 將有用的信息打包<存放到字典中>存儲(chǔ)到數(shù)組中
        NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
        // 存儲(chǔ)文字對(duì)應(yīng)的frame骚露,一段文字可能會(huì)有兩個(gè)甚至多個(gè)frame,考慮到文字換行問(wèn)題
        [dic setObject:[NSValue valueWithCGRect:rect] forKey:@"rect"];
        // 存儲(chǔ)下劃線對(duì)應(yīng)的文字
        [dic setObject:[self.text substringWithRange:underlineTextRange] forKey:@"content"];
        // 存儲(chǔ)相應(yīng)的回調(diào)的block
        [dic setObject:block forKey:@"block"];
        // 存儲(chǔ)對(duì)應(yīng)的點(diǎn)擊效果背景顏色
        [dic setValue:coverColor forKey:@"coverColor"];
        [selectedArray addObject:dic];
    }
    // 將可能點(diǎn)擊的范圍的數(shù)組存儲(chǔ)到總的數(shù)組中
    [self.rectsArray addObject:selectedArray];
    
}

2>思瘟、通過(guò)一個(gè)點(diǎn)擊的點(diǎn)荸百,去查找有沒(méi)有點(diǎn)在下劃線對(duì)用的文字范圍內(nèi),并且返回之前打包<存儲(chǔ)的字典>的數(shù)據(jù)模型

- (NSArray *)touchingSpecialWithPoint:(CGPoint)point
{
    // 從所有的特殊的范圍中找到點(diǎn)擊的那個(gè)點(diǎn)
    for (NSArray *selecedArray in self.rectsArray) {
        for (NSDictionary *dic in selecedArray) {
            CGRect myRect = [dic[@"rect"] CGRectValue];
            if(CGRectContainsPoint(myRect, point) ){
                return selecedArray;
            }
        }
    }
    return nil;
}

3>滨攻、通過(guò)touchesBegan的方法够话,獲取點(diǎn)擊的點(diǎn)蓝翰,并且去查詢相關(guān)數(shù)據(jù)模型,并且根據(jù)參數(shù)是不是展示相應(yīng)的點(diǎn)擊效果女嘲,并且通過(guò)blcok進(jìn)行回調(diào)

// 點(diǎn)擊textView的 touchesBegan 方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 獲取觸摸對(duì)象
    UITouch *touch = [touches anyObject];
    
    // 觸摸點(diǎn)
    CGPoint point = [touch locationInView:self];
    // 通過(guò)一個(gè)觸摸點(diǎn)畜份,查詢點(diǎn)擊的是不是在下劃線對(duì)應(yīng)的文字的frame
    NSArray *selectedArray = [self touchingSpecialWithPoint:point];
    for (NSDictionary *dic in selectedArray) {
        if(dic && dic[@"coverColor"]){
            UIView *cover = [[UIView alloc] init];
            cover.backgroundColor = dic[@"coverColor"];
            cover.frame = [dic[@"rect"] CGRectValue];
            cover.layer.cornerRadius = 5;
            cover.tag = kCoverViewTag;
            [self insertSubview:cover atIndex:0];
        }
    }
    if (selectedArray.count) {
        // 如果說(shuō)有點(diǎn)擊效果的話,加個(gè)延時(shí)欣尼,展示下點(diǎn)擊效果,如果沒(méi)有點(diǎn)擊效果的話爆雹,直接回調(diào)
        NSDictionary *dic = [selectedArray firstObject];
        clickTextViewPartBlock block = dic[@"block"];
        if (dic[@"coverColor"]) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                block(dic[@"content"]);
            });
        }else{
            block(dic[@"content"]);
        }
    }
}

4>、點(diǎn)擊結(jié)束的時(shí)候取消點(diǎn)擊效果愕鼓,也就是刪除點(diǎn)擊的時(shí)候創(chuàng)建的view

/** 點(diǎn)擊結(jié)束的時(shí)候 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        for (UIView *subView in self.subviews) {
            if (subView.tag == kCoverViewTag) {
                [subView removeFromSuperview];
            }
        }
    });
}

/**
 *  取消點(diǎn)擊的時(shí)候,清除相關(guān)的陰影
 */
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    for (UIView *subView in self.subviews) {
        if (subView.tag == kCoverViewTag) {
            [subView removeFromSuperview];
        }
    }
}

5钙态、在ViewController中進(jìn)行測(cè)試

 ClickTextView *clickTextView = [[ClickTextView alloc] initWithFrame:CGRectMake(50, 50, 300, 300)];
    [self.view addSubview:clickTextView];
    
    // 方便測(cè)試,設(shè)置textView的邊框已經(jīng)背景
    clickTextView.backgroundColor = [UIColor cyanColor];
    clickTextView.layer.borderWidth = 1;
    clickTextView.layer.borderColor = [UIColor redColor].CGColor;
    clickTextView.font = [UIFont systemFontOfSize:30];
    //clickTextView.textColor = [UIColor redColor];
    
    
    NSString *content = @"1234567890承諾書(shū)都差不多歲尺布斗粟CBD死UC收不到催上半場(chǎng)低俗";
    // 設(shè)置文字
    clickTextView.text = content;
    
    // 設(shè)置期中的一段文字有下劃線菇晃,下劃線的顏色為藍(lán)色册倒,點(diǎn)擊下劃線文字有相關(guān)的點(diǎn)擊效果
    NSRange range1 = [content rangeOfString:@"承諾書(shū)都差"];
    [clickTextView setUnderlineTextWithRange:range1 withUnderlineColor:[UIColor blueColor] withClickCoverColor:[UIColor greenColor] withBlock:^(NSString *clickText) {
        NSLog(@"clickText = %@",clickText);
    }];
    
    // 設(shè)置期中的一段文字有下劃線,下劃線的顏色沒(méi)有設(shè)置磺送,點(diǎn)擊下劃線文字沒(méi)有點(diǎn)擊效果
    NSRange range2 = [content rangeOfString:@"不到催上半場(chǎng)低俗"];
    [clickTextView setUnderlineTextWithRange:range2 withUnderlineColor:nil withClickCoverColor:nil withBlock:^(NSString *clickText) {
        NSLog(@"clickText = %@",clickText);
    }];

如有失誤請(qǐng)各位路過(guò)大神即時(shí)指點(diǎn)驻子,或有更好的做法,也請(qǐng)指點(diǎn)一二估灿。
詳情Demo可參考

擴(kuò)展:iOS 設(shè)置下劃線與文字之間的距離

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末崇呵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子馅袁,更是在濱河造成了極大的恐慌域慷,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件司顿,死亡現(xiàn)場(chǎng)離奇詭異芒粹,居然都是意外死亡兄纺,警方通過(guò)查閱死者的電腦和手機(jī)大溜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)估脆,“玉大人钦奋,你說(shuō)我怎么就攤上這事「碓” “怎么了付材?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)圃阳。 經(jīng)常有香客問(wèn)我厌衔,道長(zhǎng),這世上最難降的妖魔是什么捍岳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任富寿,我火速辦了婚禮睬隶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘页徐。我一直安慰自己苏潜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布变勇。 她就那樣靜靜地躺著恤左,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搀绣。 梳的紋絲不亂的頭發(fā)上飞袋,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音链患,去河邊找鬼授嘀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锣险,可吹牛的內(nèi)容都是我干的蹄皱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼芯肤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼巷折!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起崖咨,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤锻拘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后击蹲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體署拟,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年歌豺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了推穷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡类咧,死狀恐怖馒铃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痕惋,我是刑警寧澤区宇,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站值戳,受9級(jí)特大地震影響议谷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜堕虹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一卧晓、第九天 我趴在偏房一處隱蔽的房頂上張望叶洞。 院中可真熱鬧,春花似錦禀崖、人聲如沸衩辟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)艺晴。三九已至,卻和暖如春掸屡,著一層夾襖步出監(jiān)牢的瞬間封寞,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工仅财, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狈究,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓盏求,卻偏偏與公主長(zhǎng)得像抖锥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碎罚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Text Kit學(xué)習(xí)(入門(mén)和進(jìn)階): http://www.cocoachina.com/industry/201...
    F麥子閱讀 4,035評(píng)論 1 13
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)磅废、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,103評(píng)論 4 62
  • 相比以前荆烈,更加的喜歡自己拯勉,才是最大的夢(mèng)想,精神的釋放
    alialiali閱讀 142評(píng)論 0 0
  • 別后不知君遠(yuǎn)近,觸目凄涼多少悶憔购! 漸行漸遠(yuǎn)漸無(wú)書(shū)宫峦,水闊魚(yú)沉何處問(wèn)? 武漢·晴 今天陽(yáng)光微醺玫鸟,陽(yáng)光偷偷爬近書(shū)桌导绷,溫...
    特別的貓呢閱讀 651評(píng)論 0 5
  • 不管別人做了什么,我可以不生氣嗎鞋邑? 曾經(jīng)年輕的花姐诵次,一年之中總會(huì)有那么幾次情緒失控而生氣账蓉,甚至氣到歇斯底里枚碗,有時(shí)是...
    花姐瞎掰掰閱讀 1,806評(píng)論 2 9