IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> iOS富文本实现(二):输入框内放按钮效果实现 -> 正文阅读

[移动开发]iOS富文本实现(二):输入框内放按钮效果实现

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

?

目录:


一.功能实现说明

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

三.拓展思考方面细节


一.功能实现说明

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](https://gitee.com/httpfdajkfihdakdjhd/ddrich-text-demo)
以及上一篇关于富文本功能实现的代码地址:[iOS富文本实现(一):私密阅读效果](CSDN)

欢迎大家评论区交流哦!

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-26 10:17:51  更:2021-09-26 10:18:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:03:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码