iOS富文本实现(二):输入框内放按钮效果实现

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


TextView输入框内实现放按钮功能.gif

目录:

一.功能实现说明

二.实现效果核心代码片段

三.拓展思考方面细节

一.功能实现说明

1.遇到的问题说明
不知大家有没有遇到这样的功能,即如开篇的图所示,在一个输入框中去实现放按钮的效果,也就是在同一个输入框无论是TextFeild还是TextView中,支持放按钮,也支持通过输入信息去搜索的功能实现。

2.对该问题的思考历程并抛出新问题
遇到这样的问题,首先我们的解决方案有2个,1个是把输入框作为假输入框,即按钮是真的,输入框中的要输入的光标是假的;
还有一个方案是把按钮做成假的,输入框是真的,然后把假按钮放入输入框内,即可。
我的思考是,如果采用方案1,首先如果输入框是固定不动的话,那么做一个光标闪一下现一下的动画,然后做一个自定义键盘配合监听其代理方法,即可解决这个问题。但是,光标可能会移动,且随时插入按钮之间,此时该方案就很明显要被pass掉了。

3.解决问题采取的方案
那么,如果采用方案二,假按钮从何而来。答案不言自明,即富文本中来操作。因为作为一个按钮要实现点击和点击后不同的效果,则可以用富文本的
NSLinkAttributeName属性,来设置不同颜色从而实现按钮的点击效果。

好了,方案通过思考并验证后,那么如何实现呢,接下来我们接着说说这块的思考。

二.实现效果核心代码片段

1.整体框架说明实现
首先,先来介绍说明一下整体实现该方案的框架,主要涉及2个管理类,即DDRTSelectedManager 和 DDRTGroupItemManager
其中DDRTSelectedManager主要实现的是在前一个页面中列表多选的功能,其实与本节富文本研究是作为配角而存在;
而DDRTGroupItemManager则是实现本节中TextView中富文本点击事件的核心管理类。
接下来,我们逐个来说说!

2.扩展选择实现类逻辑
首先,先来说说DDRTSelectedManager中主要实现的是前面页面选项卡选择的问题,即类似于购物车的问题。
如下代码所示:

// 获取数据源的方法
-(void)initAllDatas;

// 更新某一行选中状态
-(NSMutableArray *)updateItemManager:(NSIndexPath *)indexPath;


// 遍历到所有选中的数据
-(NSMutableArray *)getAllSelectedDatas;

主要目标是通过数据源来解决TableViewCell的重用问题,只是通过改变数据源来实现列表的及时刷新实现。

3.Model包装类的实现
在Model包装类中的实现方式,是通过在Model普通属性之外,定义如下所示的属性

// 当前字符所处字符串实际开始位置
@property(nonatomic,assign)NSInteger nameLocation;
// 名字的实际长度
@property(nonatomic,assign)NSInteger nameLength;
// 名字的实际长度 + 3(2个空格 + 1个逗号)
@property(nonatomic,assign)NSInteger nameAppendAfterLength;

// 是否被点击,即将删除的情形
@property(nonatomic,assign)BOOL isClick;

从而实现所有群组数组中记录的Mode中当前的name字段拼接后其所处的字符串中的长度和位置,以方便后面渲染到页面中可以精确控制每个假按钮的分隔点。

大自然的礼物,养眼哟!

4.群组成员按钮管理类的实现
很明显群组成员按钮管理类是本次实现功能的核心代码,如下所示,
4.1.首先我们先来分析一下,在该管理类中实现的几个接口方法。

// 选中的状态
-(void)selectedItemState:(DDRTGroupMemberModel *)model;
// 未选中的状态
-(void)cancleItemState:(DDRTGroupMemberModel *)model;

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

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

// 返回表示有没有可删除的内容,没有的话,把最后一个选中,并返回NO,默认返回YES
-(BOOL)deleteSelectedGroupArr;

// 根据传入的下标,获得到对应数组的位置
-(NSInteger)indexOfGroupLoctaion:(NSInteger)location;

// 获取被处理过的富文本字符串
-(NSMutableAttributedString *)getAttributeAllMemberStr;

// 第一次进来时,更新所有数据源的点击状态为普通状态
-(void)updateAllItemStateToNomal;

4.2.富文本字符串处理的细节核心代码
这里每次更新成员数组状态,如个数,群成员按钮状态时,都要调用该方法,去重新获取被渲染后的富文本。
图中主要注意2点,即第一点是要区分按钮的点击位置和实际所占用位置的不同,因为具体实现时,按钮和按钮之间还会有其他字符作为分隔;以及最后一个按钮实现时是没有字符分隔的。
第二点是对按钮的2种状态进行区分显示的逻辑。

// 获取被处理过的富文本字符串
-(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) {// 最后一位的话,不拼接对应的字符串
                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.如何监听到点击点击位置改变其按钮状态
如下代码所示,在textView点击链接的代理方法中,通过点击传入点击位置,如下的location,
到我们的DDRTGroupItemManager 中的indexOfGroupLoctaion方法获得到其点击位置在群成员数组中的下标,然后去更改对应的富文本字符串即可。

- (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方法实现如下

 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;

这么多方法看下来,注释写的也很明白,其实核心点主要是对每个成员选项卡按钮的增删改查工作,以及准确定位到当前选择的成员的功能实现。
具体里面内容怎么实现呢,详情可看本文末尾处的连接。
原创不易,如果帮助到了朋友们,欢迎star哈!

5.实现的相关功能汇总
这里就对该上面4里面所涉及的所有按钮功能进行一个简要说明:
5.1.在群组中的每个成员作为一个假按钮处于TextView控件中展示;
5.2.当用户点击对应按钮时,选中取消选中功能实现
5.3.当用户点击最后一个按钮末尾处,可看到光标显示(但目前无法输入文字)。此时点击键盘删除图标时,倘若有按钮被选中,则选中的按钮全部倍删除,未选中的按钮位置依次向左移动;倘若按钮没有被选中,则默认点击一次删除图标,最后一个按钮被选中,再点击一次删除图标删除最后一个按钮。
5.4.用户可以在成员列表界面选择对应的成员后跳转到富文本成员页面。而在富文本成员页面中删除对应的成员后,点击返回或者右侧的添加按钮,其成员列表中该成员也被删除。
即实现成员列表和富文本成员页面的实时更新功能。

三.拓展思考方面细节

1.解题思维说明
首先,需要说明的是,完成对本篇富文本功能实现流程后,我们会对底层的UI如按钮,Label等实现有了更清晰的理解。更接近其底层实现原理。
另外一层的思考是对于Model管理类的理解需要进一步加深,即其主要处理的不仅仅是与数据有关,也有很多业务逻辑。特别是如同本文所示的数据之间层层嵌套,且层与层之间还略有不同,如按钮有2种状态,最后一个按钮的没有分隔符,每个按钮的点击范围和渲染范围不同等等逻辑时。
我们应转变思路,通过对Model的共同数据可以处理,那么不同的数据也可以通过Model来记录。之后再配合数据源管理类来实现数据的改变,然后让底层UI去根据数据改变刷新为我们需要的UI的思考逻辑。

2.回到基本面向对象突围
对于面向对象的理解,我们需要进一步突围发展,扩张基本面。即面向对象是在我们解题过程中必备的思想,是需要我们深入其核心去理解。
比如像本篇中的面向Model和面向2个管理类的处理逻辑。每一个管理类去管理对应的分工。面向自己所解决的对象,去实现对应的方法。
而我们的控制器呢,只是拿其优势为其所用,最终实现一个个功能块。
功能块就是我们的面向对象的对象,那么我们的面向对象需要拆分给其他擅长该类的管理者,Model,帮助类,第三方库等去实现,这就是我们的面向对象思想。
好了,本篇废话有点小多,最后呢附上本文代码的仓库地址:我的富文本之DDRichTextDemo

以及上一篇关于富文本功能实现的代码地址:iOS富文本实现(-):私密阅读效果

欢迎大家评论区交流哦!

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

推荐阅读更多精彩内容

  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,473评论 28 53
  • 首先介绍下自己的背景: 我11年左右入市到现在,也差不多有4年时间,看过一些关于股票投资的书籍,对于巴菲特等股神的...
    瞎投资阅读 5,588评论 3 8
  • ![Flask](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAW...
    极客学院Wiki阅读 7,188评论 0 3
  • 不知不觉易趣客已经在路上走了快一年了,感觉也该让更多朋友认识知道易趣客,所以就谢了这篇简介,已做创业记事。 易趣客...
    Physher阅读 3,382评论 1 2