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)

欢迎大家评论区交流哦!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
iOS开发中,实现一个字一个框的作文布局可以通过以下步骤实现: 1. 创建一个UIView作为文本容器,设置其背景色和边框样式。 2. 将文本内容按字分割成一个个字符,并遍历每个字符,创建一个UILabel作为每个字符的框容器,并设置其位置、大小、背景色和边框样式。 3. 将每个字符的框容器添加到文本容器中,使用NSAttributedString来设置每个字符的文本样式。 示例代码: ```swift // 创建文本容器 let containerView = UIView(frame: CGRect(x: 20, y: 100, width: 300, height: 300)) containerView.backgroundColor = UIColor.white containerView.layer.borderWidth = 1 containerView.layer.borderColor = UIColor.black.cgColor self.view.addSubview(containerView) // 设置文本内容 let text = "这是一篇中文作文,要求实现一个字一个框的作文布局。" // 计算每个字符的大小和位置 let font = UIFont.systemFont(ofSize: 16) let size = CGSize(width: 10000, height: 20) var x: CGFloat = 10 let y: CGFloat = 10 let padding: CGFloat = 2 for char in text { let label = UILabel(frame: CGRect(x: x, y: y, width: 20, height: 20)) label.backgroundColor = UIColor.white label.layer.borderWidth = 1 label.layer.borderColor = UIColor.black.cgColor label.textAlignment = .center containerView.addSubview(label) let attributedString = NSAttributedString(string: String(char), attributes: [.font: font]) label.attributedText = attributedString let charSize = attributedString.boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).size label.frame.size.width = charSize.width + padding * 2 label.frame.size.height = charSize.height + padding * 2 x += label.frame.size.width } // 调整文本容器大小 containerView.frame.size.width = x + 10 ``` 在这个示例中,我们创建了一个300x300的文本容器,并将文本内容按字分割成一个个字符。我们遍历每个字符,创建一个UILabel作为每个字符的框容器,并设置其位置、大小、背景色和边框样式。我们使用NSAttributedString来设置每个字符的文本样式。最后,我们调整文本容器大小,使得所有字符的框容器能够完整地显示在文本容器中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值