項目回復評論-動態布局

項目中要做塊,發起事件著和粉絲互動,相互回復評論.在這塊.用到動態布局.接下來就總結下自己寫的這塊.

一.首先網絡請求接口是公司的..不能在這寫了,我就自己本地寫個 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 的地址:.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,252評論 1 23
  • 2017.02.22 可以練習,每當這個時候,腦袋就犯困,我這腦袋真是神奇呀,一說讓你做事情,你就犯困,你可不要太...
    Carden閱讀 1,373評論 0 1
  • 說起網游,似乎在我們70年代是一種荒廢學業,荒蕪人生的代名詞。然而現在這個時代,似乎不會玩幾個網游,都融不入...
    福咖閱讀 160評論 0 2
  • 1.畫畫篇 作為一個美術生自然少不了畫畫,然而畫畫需要各種鉛筆,炭筆,顏料,最痛苦的莫過于你把自己費了九牛二虎之力...
    清風又藍閱讀 664評論 19 9
  • 很多女性到現在還會質疑:干嘛要自己買花給自己啊?那樣多沒面子啊,鮮花不都是別人送的嗎?這時候往往我會上前跟她解釋一...
    世源生活閱讀 1,232評論 0 0