美文网首页
项目回复评论-动态布局

项目回复评论-动态布局

作者: A_sura | 来源:发表于2016-06-29 16:42 被阅读0次

项目中要做块,发起事件着和粉丝互动,相互回复评论.在这块.用到动态布局.接下来就总结下自己写的这块.

一.首先网络请求接口是公司的..不能在这写了,我就自己本地写个 plist 文件,模拟网路请求吧

NSString*filePath = [[NSBundlemainBundle]pathForResource:@"Data"ofType:@"plist"];

NSArray*dataArr = [[NSArrayalloc]initWithContentsOfFile:filePath];

for(NSDictionary*dictindataArr) {

CommentModel*model = [[CommentModelalloc]init];

[modelsetValuesForKeysWithDictionary:dict];

[self.datasourceaddObject:model];

}

[_tableViewreloadData];

二.网络请求回来数据了,我们应该设计怎么把布局和数据联系在一起.这里难得就是计算文本的高度.应该在什么时候计算.什么时候调用.在这我的思路是在对数据进行处理的时候.顺便把高度计算出来.和数据模型绑定在一起.这样就能和布局的 View 绑定在一起.

@interface ReplayModel : NSObject

/** 评论方式 **/
@property (nonatomic, copy) NSString *CommentType;
/** 评论内容 **/
@property (nonatomic, copy) NSString *Content;
/** 来自评论者的 id **/
@property (nonatomic, strong) NSNumber *FromMemberId;

@property (nonatomic, strong) NSNumber *Id;
/** 被评论者的 id **/
@property (nonatomic, strong) NSNumber *ToMemberId;
/** 被评论者发表的任务 id **/
@property (nonatomic, strong) NSNumber *ToTaskId;


@end

@interface CommentModel : NSObject

/** 评论方式 **/
@property (nonatomic, copy) NSString *CommentType;
/** 内容 **/
@property (nonatomic, copy) NSString *Content;
/** 创建时间 **/
@property (nonatomic, copy) NSString *CreateDateStr;
/** 来自评论者的名称 **/
@property (nonatomic, copy) NSString *FromNickname;
/** 来自评论者的头像 **/
@property (nonatomic, copy) NSString *FromHeadImage;
/** 来自评论者的id **/
@property (nonatomic, strong) NSNumber *FromMemberId;

@property (nonatomic, strong) NSNumber *Id;
/** 回复 **/
@property (nonatomic, strong) NSArray <ReplayModel *> *Replay;


/** 内容高度 **/
@property (nonatomic, assign) CGFloat contentHeight;

@end

这是我的数据模型.在大的数据模型里面嵌套了一个专门关于评论的回复的数据模型.在. m 的才是中点.

- (void)setNilValueForKey:(NSString *)key{
    
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{

}

- (void)setValue:(id)value forKey:(NSString *)key{
    if ([key isEqualToString:@"Content"]) {
            //计算高度
        CGFloat replyHeight = [[value stringByAppendingString:@"回复: "] boundingRectWithSize:CGSizeMake(SCREENWIDTH - 80, MAXFLOAT) options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size.height;
            //拼接到数据中
        value = [NSString stringWithFormat:@"%f#回复: %@",replyHeight,value];
        [super setValue:value forKey:key];
    }else{
        [super setValue:value forKey:key];
    }
}

@end

@implementation CommentModel

- (void)setNilValueForKey:(NSString *)key{
    
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
    
}

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues{
    [super setValuesForKeysWithDictionary:keyedValues];
    
        //计算文本高度
    [keyedValues enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        
        if ([key isEqualToString:@"Content"]) {
            self.contentHeight =  [obj boundingRectWithSize:CGSizeMake(SCREENWIDTH - 103, MAXFLOAT) options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : [UIFont systemFontOfSize:16]} context:nil].size.height;
            
            return ;
        }
        
        
        if ([key isEqualToString:@"Replay"]) {
            NSMutableArray *relayArr = [NSMutableArray array];
            [obj enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                
                ReplayModel *replayModel = [[ReplayModel alloc] init];
                [replayModel setValuesForKeysWithDictionary:obj];
                [relayArr addObject:replayModel];
                
            }];
            self.Replay = relayArr;
        }
    }];
}
@end

这里处理是在

- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues

这个方法里面做操作.将作者文本高度计算出来.赋值给事先定义的contentHeight这个属性.好处之后说.在判断在key 是Replay.那就是嵌套的回复评论的 model. 所以这时要对它处理,一一赋值.
在ReplayModel的

- (void)setValue:(nullable id)value forKey:(NSString *)key;

方法里面计算高度.这时候因为是不固定的高度,所以不能为这个 model 设置属性.所以想到直接将计算好的高度拼接到评论下面.中间件个#标示符.这时.数据模型算是处理完了.

三.UITableViewCell 里面的操作

这里主要有几个东西.

/** 存放评论lable 高度的数组 **/
@property (nonatomic, strong) NSMutableArray *replayLabelHeights;

/** 存放评论lable文本的数组 **/
@property (nonatomic, strong) NSMutableArray *replayLabelTexts;

用两个数据去存储所有缓存的高度和文本的内容

接下来就是重新设置头条问题的高度.和动态添加回复评论

   //如果是没回复的,返回
        if (commentModel.Replay == nil) {
            return ;
        }
            //存放评论lable 高度的数组
        _replayLabelHeights = [NSMutableArray array];
        _replayLabelTexts = [NSMutableArray array];
        
        
            //回复
        [commentModel.Replay enumerateObjectsUsingBlock:^(ReplayModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
            ReplayModel *replayModel = obj;
                //分割数据.收集评论lable的高度和文本
            [_replayLabelHeights addObject:[replayModel.Content componentsSeparatedByString:@"#"].firstObject];
            
            NSString *textStr = @"";
            int i = 0;
            
            for (NSString *text in [replayModel.Content componentsSeparatedByString:@"#"]) {
                i ++;

                if (i == 1) {
                    continue;
                }
                
               textStr = [textStr stringByAppendingString:text];
            }
            [_replayLabelTexts addObject:textStr];
        }];
        
            //动态添加回复 label
        for (int i = 0; i < _replayLabelHeights.count; i ++) {
            
            CGFloat height = [self addHeightWithI:i - 1];
            UILabel *replyLabel = [[UILabel alloc] initWithFrame:CGRectMake(76, _contentLabel.bottomY + height, SCREENWIDTH - 80, [_replayLabelHeights[i] floatValue])];
            replyLabel.numberOfLines = 0;
            replyLabel.layer.borderColor = [UIColor lightGrayColor].CGColor;
            replyLabel.layer.borderWidth = 1;
            replyLabel.backgroundColor = [UIColor colorWithRed:200 / 255.0 green:205 / 255.0 blue:246 / 255.0 alpha:1];
            replyLabel.font = [UIFont systemFontOfSize:16];
            replyLabel.text = _replayLabelTexts[i];
            [self.contentView addSubview:replyLabel];
        }

动态计算设置 Y 时,拥到这样个方法.

- (CGFloat)addHeightWithI:(int)i{
    __block CGFloat height = 0;
    
        //第一行直接返回,不取值
    if (i < 0) {
        return height;
    }
    
    [_replayLabelHeights enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        height += [obj floatValue];
        height += 8;
        
            //取到对应行后返回值
        if (idx == i) {
            *stop = YES;
        }
    }];
    return height;
}

来确定动态添加的回复评论的 lable 的位置

四.最后就是UIViewController的协议方法的东西了.这里说以下两个方法

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    
    if (indexPath.row == 0) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifierNormal forIndexPath:indexPath];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.textLabel.text = @"评论";
        return cell;
    }
    CommmentTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier forIndexPath:indexPath];
    cell.assignment(self.datasource[indexPath.row - 1]);
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if (indexPath.row == 0) {
        return 44;
    }

    CommentModel *commentModel = self.datasource[indexPath.row - 1];
        //51为第一个内容剧 cell 上的距离
    CGFloat cellHeight = 51 + (commentModel.Replay.count * 8) + commentModel.contentHeight;
    
        //如果没有回复,直接返回
    if (commentModel.Replay == nil) {
            //8为据底部的边距
        return cellHeight + 8;
    }
        //计算回复的评论高度
    for (ReplayModel *replayModel in commentModel.Replay) {
        NSArray *replayArr = [replayModel.Content componentsSeparatedByString:@"#"];
        cellHeight += [replayArr.firstObject floatValue];
    }
    return cellHeight;
}

这里直接从计算好的数据源中取高度..因此这里不会造成性能的影响.
现在差不多就完成了.这里是效果图,虽然有点丑.😄

F43F761F-5301-4C73-8A81-E11E08F978EC.png

总结:

1.之所以把计算耗时的操作放到数据处理的里面去.一是为了与数据模型绑定,因而和 View 来绑定.达到高聚合.二是这是一个费时的操作.应为网络请求会等待时间.这个是无法改变的.所以,把他放这和网络请求中增加那么一小点时间还是可以的.三是放在模型转换的方法中,只会计算一次.达到高效率.起到缓存的效果.有人会问,为啥不放到子线程.这个数据回来就要刷新界面.意味这就得布局界面,这时不能保证已经算好.所以不能放到子线程.

2.也调研了一些,说可以吧计算耗时的东西方法 RUNloop 的空闲时候.这个应该不错,但是水平有限,以后补上.

3.其实这里犯了个错误,就是尽量不要动态在 UITableViewCell 上添加子控件.但是,这地方没办法.以后优化补上.

最后.这里说下作者测得的性能:

FA9741B6-B4B1-42D6-9374-6FDEEF48A87E.png E7CF80F5-2ED5-4BFB-9535-BEB112FAF955.png
3DC68657-D3FE-4DB1-945B-1ADF3E85191B.png

在这可以看出.在计算方法里面是好了点时间.但是在

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

这个方法里面耗时.这个方法是调用时机是很频繁的.在 tableView 初始化的时候,要调用多少个cell个这个方法.来确定 contenetSize ,在 每次调用cellForRow方法又得调用一次,所以说.这样写的话,能稍微好点.

这里留个 demo 的地址:.

相关文章

  • 项目回复评论-动态布局

    项目中要做块,发起事件着和粉丝互动,相互回复评论.在这块.用到动态布局.接下来就总结下自己写的这块. 一.首先网络...

  • 突然想不喜欢你了

    喜欢你第三年了,看了你发的动态,评论了你发的动态,你会回复我,但是你会回复嗯嗯,我就没有了评论的欲望。或许...

  • Android 动态添加布局的两种方式

    前言 大多数时候我们布局都是用xml来布局的,但有些时候也是会用到动态布局的,尤其是在一些大项目中,动态布局更是体...

  • 酷安7.3、新增黑名单和举报

    发现应用的乐趣 日志: 新增 黑名单 新增 举报,可举报动态、回复、用户 新增 应用详情页最热评论、最新评论 优化...

  • 评论回复

    十分荣幸能得到你的认真评论。你所说的试错确实是否定法的一个重要部分。同时你也启发了我,如果我能从相对容易理解的试错...

  • Android评论回复 布局 遇见的坑 BaseExpand

    先给出解决方案: 在调用notifyDataSetChanged()后,使用一下方法收缩一次。 // i 为更新的...

  • android showSoftInput方法调用软键盘不显示

    项目中有这样一个需求,在某帖子里点击某一条评论的时候,弹出对话框让用户选择是删除评论还是回复评论,当用户点击回复评...

  • IOS xib 或 Storyboard xib view 创建

    前言 在项目中用 xib或 Storyboard 布局 完成后,因为 动态适配等各种原因经常会遇到,修改布局的限制...

  • 评论的小小套路

    评论结构类型 一、只能评论,不能对评论进行回复 不让回复的评论结构是最简单的结构,评论一扯上回复,就会涉及到回复的...

  • 瀑布流布局columns

    瀑布流布局其核心是基于一个网格的布局,而且每行包含的项目列表高度是随机的(随着自己内容动态变化高度),同时每个项目...

网友评论

      本文标题:项目回复评论-动态布局

      本文链接:https://www.haomeiwen.com/subject/vkdxjttx.html