iOS富文本實(shí)現(xiàn)(二):輸入框內(nèi)放按鈕效果實(shí)現(xiàn)

效果直接放這了,先看效果再上菜。


TextView輸入框內(nèi)實(shí)現(xiàn)放按鈕功能.gif

目錄:

一.功能實(shí)現(xiàn)說明

二.實(shí)現(xiàn)效果核心代碼片段

三.拓展思考方面細(xì)節(jié)

一.功能實(shí)現(xiàn)說明

1.遇到的問題說明
不知大家有沒有遇到這樣的功能剑逃,即如開篇的圖所示,在一個(gè)輸入框中去實(shí)現(xiàn)放按鈕的效果官辽,也就是在同一個(gè)輸入框無論是TextFeild還是TextView中蛹磺,支持放按鈕,也支持通過輸入信息去搜索的功能實(shí)現(xiàn)同仆。

2.對(duì)該問題的思考?xì)v程并拋出新問題
遇到這樣的問題萤捆,首先我們的解決方案有2個(gè),1個(gè)是把輸入框作為假輸入框俗批,即按鈕是真的鳖轰,輸入框中的要輸入的光標(biāo)是假的;
還有一個(gè)方案是把按鈕做成假的扶镀,輸入框是真的蕴侣,然后把假按鈕放入輸入框內(nèi),即可臭觉。
我的思考是昆雀,如果采用方案1,首先如果輸入框是固定不動(dòng)的話蝠筑,那么做一個(gè)光標(biāo)閃一下現(xiàn)一下的動(dòng)畫狞膘,然后做一個(gè)自定義鍵盤配合監(jiān)聽其代理方法,即可解決這個(gè)問題什乙。但是挽封,光標(biāo)可能會(huì)移動(dòng),且隨時(shí)插入按鈕之間臣镣,此時(shí)該方案就很明顯要被pass掉了辅愿。

3.解決問題采取的方案
那么智亮,如果采用方案二,假按鈕從何而來点待。答案不言自明阔蛉,即富文本中來操作。因?yàn)樽鳛橐粋€(gè)按鈕要實(shí)現(xiàn)點(diǎn)擊和點(diǎn)擊后不同的效果癞埠,則可以用富文本的
NSLinkAttributeName屬性状原,來設(shè)置不同顏色從而實(shí)現(xiàn)按鈕的點(diǎn)擊效果。

好了苗踪,方案通過思考并驗(yàn)證后颠区,那么如何實(shí)現(xiàn)呢,接下來我們接著說說這塊的思考通铲。

二.實(shí)現(xiàn)效果核心代碼片段

1.整體框架說明實(shí)現(xiàn)
首先毕莱,先來介紹說明一下整體實(shí)現(xiàn)該方案的框架,主要涉及2個(gè)管理類测暗,即DDRTSelectedManager 和 DDRTGroupItemManager央串。
其中DDRTSelectedManager主要實(shí)現(xiàn)的是在前一個(gè)頁面中列表多選的功能磨澡,其實(shí)與本節(jié)富文本研究是作為配角而存在碗啄;
而DDRTGroupItemManager則是實(shí)現(xiàn)本節(jié)中TextView中富文本點(diǎn)擊事件的核心管理類。
接下來稳摄,我們逐個(gè)來說說稚字!

2.擴(kuò)展選擇實(shí)現(xiàn)類邏輯
首先,先來說說DDRTSelectedManager中主要實(shí)現(xiàn)的是前面頁面選項(xiàng)卡選擇的問題厦酬,即類似于購(gòu)物車的問題胆描。
如下代碼所示:

// 獲取數(shù)據(jù)源的方法
-(void)initAllDatas;

// 更新某一行選中狀態(tài)
-(NSMutableArray *)updateItemManager:(NSIndexPath *)indexPath;


// 遍歷到所有選中的數(shù)據(jù)
-(NSMutableArray *)getAllSelectedDatas;

主要目標(biāo)是通過數(shù)據(jù)源來解決TableViewCell的重用問題,只是通過改變數(shù)據(jù)源來實(shí)現(xiàn)列表的及時(shí)刷新實(shí)現(xiàn)仗阅。

3.Model包裝類的實(shí)現(xiàn)
在Model包裝類中的實(shí)現(xiàn)方式昌讲,是通過在Model普通屬性之外,定義如下所示的屬性

// 當(dāng)前字符所處字符串實(shí)際開始位置
@property(nonatomic,assign)NSInteger nameLocation;
// 名字的實(shí)際長(zhǎng)度
@property(nonatomic,assign)NSInteger nameLength;
// 名字的實(shí)際長(zhǎng)度 + 3(2個(gè)空格 + 1個(gè)逗號(hào))
@property(nonatomic,assign)NSInteger nameAppendAfterLength;

// 是否被點(diǎn)擊减噪,即將刪除的情形
@property(nonatomic,assign)BOOL isClick;

從而實(shí)現(xiàn)所有群組數(shù)組中記錄的Mode中當(dāng)前的name字段拼接后其所處的字符串中的長(zhǎng)度和位置短绸,以方便后面渲染到頁面中可以精確控制每個(gè)假按鈕的分隔點(diǎn)。

大自然的禮物筹裕,養(yǎng)眼喲醋闭!

4.群組成員按鈕管理類的實(shí)現(xiàn)
很明顯群組成員按鈕管理類是本次實(shí)現(xiàn)功能的核心代碼,如下所示朝卒,
4.1.首先我們先來分析一下证逻,在該管理類中實(shí)現(xiàn)的幾個(gè)接口方法。

// 選中的狀態(tài)
-(void)selectedItemState:(DDRTGroupMemberModel *)model;
// 未選中的狀態(tài)
-(void)cancleItemState:(DDRTGroupMemberModel *)model;

-(void)addGroupItem:(DDRTGroupMemberModel *)model;

-(void)deleteGroupItem:(DDRTGroupMemberModel *)model;

// 返回表示有沒有可刪除的內(nèi)容抗斤,沒有的話囚企,把最后一個(gè)選中,并返回NO丈咐,默認(rèn)返回YES
-(BOOL)deleteSelectedGroupArr;

// 根據(jù)傳入的下標(biāo),獲得到對(duì)應(yīng)數(shù)組的位置
-(NSInteger)indexOfGroupLoctaion:(NSInteger)location;

// 獲取被處理過的富文本字符串
-(NSMutableAttributedString *)getAttributeAllMemberStr;

// 第一次進(jìn)來時(shí)洞拨,更新所有數(shù)據(jù)源的點(diǎn)擊狀態(tài)為普通狀態(tài)
-(void)updateAllItemStateToNomal;

4.2.富文本字符串處理的細(xì)節(jié)核心代碼
這里每次更新成員數(shù)組狀態(tài)扯罐,如個(gè)數(shù),群成員按鈕狀態(tài)時(shí)烦衣,都要調(diào)用該方法歹河,去重新獲取被渲染后的富文本。
圖中主要注意2點(diǎn)花吟,即第一點(diǎn)是要區(qū)分按鈕的點(diǎn)擊位置和實(shí)際所占用位置的不同秸歧,因?yàn)榫唧w實(shí)現(xiàn)時(shí),按鈕和按鈕之間還會(huì)有其他字符作為分隔衅澈;以及最后一個(gè)按鈕實(shí)現(xiàn)時(shí)是沒有字符分隔的键菱。
第二點(diǎn)是對(duì)按鈕的2種狀態(tài)進(jìn)行區(qū)分顯示的邏輯。

// 獲取被處理過的富文本字符串
-(NSMutableAttributedString *)getAttributeAllMemberStr {
    
    // 清空字符串
    [self.allGroupMemberStr replaceCharactersInRange:NSMakeRange(0, self.allGroupMemberStr.mutableString.length) withString:@""];
    
    if (self.groupMemberArr && self.groupMemberArr.count) {
        NSInteger totalLocation = 0;
        for (int index = 0; index < self.groupMemberArr.count; index ++) {
            DDRTGroupMemberModel *memberModel = self.groupMemberArr[index];
            memberModel.nameLocation = totalLocation;
            NSString *nameStr;
            NSMutableAttributedString *attrStr;
            BOOL isLast = NO;
            if (index == self.groupMemberArr.count - 1) {// 最后一位的話今布,不拼接對(duì)應(yīng)的字符串
                nameStr = memberModel.personName;
                isLast = YES;
            }
            else {
                
                nameStr = [NSString stringWithFormat:@"%@%@",memberModel.personName,kAppendStrSign];
                isLast = NO;
            }
            totalLocation = totalLocation + nameStr.length;
            
            
            if (memberModel.isClick == YES) {
                attrStr = [self getSubStringClick:nameStr andFont:kRichTextViewFont andIsLast:isLast];
            }
            else {
                attrStr = [self getSubStringNormal:nameStr andFont:kRichTextViewFont andIsLast:isLast];
            }
            
            [self.allGroupMemberStr appendAttributedString:attrStr];

        }
    }
    return self.allGroupMemberStr;
}

這樣一解釋后经备,大家是不是明顯清晰多了哈!
4.3.如何監(jiān)聽到點(diǎn)擊點(diǎn)擊位置改變其按鈕狀態(tài)
如下代碼所示部默,在textView點(diǎn)擊鏈接的代理方法中侵蒙,通過點(diǎn)擊傳入點(diǎn)擊位置,如下的location傅蹂,
到我們的DDRTGroupItemManager 中的indexOfGroupLoctaion方法獲得到其點(diǎn)擊位置在群成員數(shù)組中的下標(biāo)纷闺,然后去更改對(duì)應(yīng)的富文本字符串即可。

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    DDLog(@"%lu ------ %lu",(unsigned long)characterRange.location,(unsigned long)characterRange.length);
    if (textView == self.groupTextView) {
        NSInteger location = [[NSNumber numberWithUnsignedInteger:characterRange.location] integerValue];
        NSInteger clickIndex = [self.groupItemManager indexOfGroupLoctaion:location];
        if (clickIndex < self.groupItemManager.groupMemberArr.count) {
            DDRTGroupMemberModel *model = self.groupItemManager.groupMemberArr[clickIndex];
            if (model.isClick == YES) {
                [self.groupItemManager cancleItemState:model];
            }
            else {
                [self.groupItemManager selectedItemState:model];
            }
            self.groupTextView.attributedText = [self.groupItemManager getAttributeAllMemberStr];
            
        }
        
    }
  
    return YES;
}

indexOfGroupLoctaion方法實(shí)現(xiàn)如下

 NSInteger nowIndex = 0;
    if (self.groupMemberArr && self.groupMemberArr.count) {
        for (int index = 0; index < self.groupMemberArr.count; index ++ ) {
            DDRTGroupMemberModel *model = self.groupMemberArr[index];
            if (model.nameLocation >= location) {
                nowIndex = index;
                break;
            }
        }
    }
    return nowIndex;

這么多方法看下來份蝴,注釋寫的也很明白犁功,其實(shí)核心點(diǎn)主要是對(duì)每個(gè)成員選項(xiàng)卡按鈕的增刪改查工作,以及準(zhǔn)確定位到當(dāng)前選擇的成員的功能實(shí)現(xiàn)婚夫。
具體里面內(nèi)容怎么實(shí)現(xiàn)呢浸卦,詳情可看本文末尾處的連接。
原創(chuàng)不易案糙,如果幫助到了朋友們限嫌,歡迎star哈!

5.實(shí)現(xiàn)的相關(guān)功能匯總
這里就對(duì)該上面4里面所涉及的所有按鈕功能進(jìn)行一個(gè)簡(jiǎn)要說明:
5.1.在群組中的每個(gè)成員作為一個(gè)假按鈕處于TextView控件中展示侍筛;
5.2.當(dāng)用戶點(diǎn)擊對(duì)應(yīng)按鈕時(shí)萤皂,選中取消選中功能實(shí)現(xiàn)
5.3.當(dāng)用戶點(diǎn)擊最后一個(gè)按鈕末尾處,可看到光標(biāo)顯示(但目前無法輸入文字)匣椰。此時(shí)點(diǎn)擊鍵盤刪除圖標(biāo)時(shí)裆熙,倘若有按鈕被選中,則選中的按鈕全部倍刪除,未選中的按鈕位置依次向左移動(dòng)入录;倘若按鈕沒有被選中蛤奥,則默認(rèn)點(diǎn)擊一次刪除圖標(biāo),最后一個(gè)按鈕被選中僚稿,再點(diǎn)擊一次刪除圖標(biāo)刪除最后一個(gè)按鈕凡桥。
5.4.用戶可以在成員列表界面選擇對(duì)應(yīng)的成員后跳轉(zhuǎn)到富文本成員頁面。而在富文本成員頁面中刪除對(duì)應(yīng)的成員后蚀同,點(diǎn)擊返回或者右側(cè)的添加按鈕缅刽,其成員列表中該成員也被刪除。
即實(shí)現(xiàn)成員列表和富文本成員頁面的實(shí)時(shí)更新功能蠢络。

三.拓展思考方面細(xì)節(jié)

1.解題思維說明
首先衰猛,需要說明的是,完成對(duì)本篇富文本功能實(shí)現(xiàn)流程后刹孔,我們會(huì)對(duì)底層的UI如按鈕啡省,Label等實(shí)現(xiàn)有了更清晰的理解。更接近其底層實(shí)現(xiàn)原理髓霞。
另外一層的思考是對(duì)于Model管理類的理解需要進(jìn)一步加深卦睹,即其主要處理的不僅僅是與數(shù)據(jù)有關(guān),也有很多業(yè)務(wù)邏輯方库。特別是如同本文所示的數(shù)據(jù)之間層層嵌套结序,且層與層之間還略有不同,如按鈕有2種狀態(tài)薪捍,最后一個(gè)按鈕的沒有分隔符笼痹,每個(gè)按鈕的點(diǎn)擊范圍和渲染范圍不同等等邏輯時(shí)配喳。
我們應(yīng)轉(zhuǎn)變思路酪穿,通過對(duì)Model的共同數(shù)據(jù)可以處理,那么不同的數(shù)據(jù)也可以通過Model來記錄晴裹。之后再配合數(shù)據(jù)源管理類來實(shí)現(xiàn)數(shù)據(jù)的改變被济,然后讓底層UI去根據(jù)數(shù)據(jù)改變刷新為我們需要的UI的思考邏輯。

2.回到基本面向?qū)ο笸粐?/strong>
對(duì)于面向?qū)ο蟮睦斫饨牛覀冃枰M(jìn)一步突圍發(fā)展只磷,擴(kuò)張基本面。即面向?qū)ο笫窃谖覀兘忸}過程中必備的思想泌绣,是需要我們深入其核心去理解钮追。
比如像本篇中的面向Model和面向2個(gè)管理類的處理邏輯。每一個(gè)管理類去管理對(duì)應(yīng)的分工阿迈。面向自己所解決的對(duì)象元媚,去實(shí)現(xiàn)對(duì)應(yīng)的方法。
而我們的控制器呢,只是拿其優(yōu)勢(shì)為其所用刊棕,最終實(shí)現(xiàn)一個(gè)個(gè)功能塊炭晒。
功能塊就是我們的面向?qū)ο蟮膶?duì)象,那么我們的面向?qū)ο笮枰鸱纸o其他擅長(zhǎng)該類的管理者甥角,Model网严,幫助類,第三方庫(kù)等去實(shí)現(xiàn)嗤无,這就是我們的面向?qū)ο笏枷搿?/strong>
好了震束,本篇廢話有點(diǎn)小多,最后呢附上本文代碼的倉(cāng)庫(kù)地址:我的富文本之DDRichTextDemo

以及上一篇關(guān)于富文本功能實(shí)現(xiàn)的代碼地址:iOS富文本實(shí)現(xiàn)(-):私密閱讀效果

歡迎大家評(píng)論區(qū)交流哦当犯!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驴一,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子灶壶,更是在濱河造成了極大的恐慌肝断,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驰凛,死亡現(xiàn)場(chǎng)離奇詭異胸懈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恰响,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門趣钱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胚宦,你說我怎么就攤上這事首有。” “怎么了枢劝?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵井联,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我您旁,道長(zhǎng)烙常,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任鹤盒,我火速辦了婚禮蚕脏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘侦锯。我一直安慰自己驼鞭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布尺碰。 她就那樣靜靜地躺著挣棕,像睡著了一般汇竭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上穴张,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天细燎,我揣著相機(jī)與錄音,去河邊找鬼皂甘。 笑死玻驻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的偿枕。 我是一名探鬼主播璧瞬,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼渐夸!你這毒婦竟也來了嗤锉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤墓塌,失蹤者是張志新(化名)和其女友劉穎瘟忱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苫幢,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡访诱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了韩肝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片触菜。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖哀峻,靈堂內(nèi)的尸體忽然破棺而出涡相,到底是詐尸還是另有隱情,我是刑警寧澤剩蟀,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布催蝗,位于F島的核電站,受9級(jí)特大地震影響喻旷,放射性物質(zhì)發(fā)生泄漏生逸。R本人自食惡果不足惜牢屋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一且预、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烙无,春花似錦锋谐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春三热,著一層夾襖步出監(jiān)牢的瞬間鼓择,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工就漾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呐能,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓抑堡,卻偏偏與公主長(zhǎng)得像摆出,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子首妖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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

  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月偎漫,有人笑有人哭,有人歡樂有人憂愁有缆,有人驚喜有人失落异雁,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,536評(píng)論 28 53
  • 首先介紹下自己的背景: 我11年左右入市到現(xiàn)在熄守,也差不多有4年時(shí)間,看過一些關(guān)于股票投資的書籍灌曙,對(duì)于巴菲特等股神的...
    瞎投資閱讀 5,731評(píng)論 3 8
  • ![Flask](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW...
    極客學(xué)院Wiki閱讀 7,249評(píng)論 0 3
  • 不知不覺易趣客已經(jīng)在路上走了快一年了在刺,感覺也該讓更多朋友認(rèn)識(shí)知道易趣客逆害,所以就謝了這篇簡(jiǎn)介,已做創(chuàng)業(yè)記事蚣驼。 易趣客...
    Physher閱讀 3,420評(píng)論 1 2