美文网首页
MVC模式下代码自定义cell(自计算行高)

MVC模式下代码自定义cell(自计算行高)

作者: 与佳期 | 来源:发表于2015-10-16 14:22 被阅读925次

前言

上一篇文章总结了通过xib自定义cell。当cell中的内容不定,cell的高度也就不定。此时就要动态计算cell的高度,也就是采用本篇文章要讲的代码自定义cell中的方式。

本篇文章就以Demo中 图书馆->查找书籍 的界面为例

首先看一下我们最终实现的样子

屏幕快照 2015-10-16 11.57.27.png

通过代码自定义cell

  • 1.新建模型类Query继承自NSObject。Query.h文件中添加cell中对应的数据属性
    @property (nonatomic, copy) NSString *image; // 封面
    @property (nonatomic, copy) NSString *title; // 书名
    @property (nonatomic, copy) NSString *author; // 作者
    @property (nonatomic, copy) NSString *publish; // 出版社
    @property (nonatomic, copy) NSString *number; // 索书号
    @property (nonatomic, copy) NSString *place; // 馆藏地
    @property (nonatomic, copy) NSString *desc; // 简介
    Query.m中重写init方法
    - (id)initWithDict:(NSDictionary *)dict {
    if (self = [super init]) {
    // 解析字典属性
    self.image = dict[@"image"];
    self.title = dict[@"title"];
    self.author = dict[@"author"];
    self.publish = dict[@"publish"];
    self.number = dict[@"number"];
    self.place = dict[@"place"];
    self.desc = dict[@"desc"];
    }
    return self;
    }
    并在Query.m中提供一个类方法
    + (id)queryWithDict:(NSDictionary *)dict {
    return [[self alloc] initWithDict:dict];
    }

  • 2.新建QueryFrame类继承自UIView。QueryFrame.h文件中添加cell中的属性
    @property (nonatomic, copy) NSString *image; // 封面
    @property (nonatomic, copy) NSString *title; // 书名
    @property (nonatomic, copy) NSString *author; // 作者
    @property (nonatomic, copy) NSString *publish; // 出版社
    @property (nonatomic, copy) NSString *number; // 索书号
    @property (nonatomic, copy) NSString *place; // 馆藏地
    @property (nonatomic, copy) NSString *desc; // 简介
    并在QueryFrame.h添加另外两个属性
    // cellHeight用于计算cell的高度
    @property (nonatomic, assign, readonly) CGFloat cellHeight;
    // 拥有Query类的属性
    @property (nonatomic, strong) Query *query;
    在QueryFrame.m中重写setQuery方法,计算各个控件的frame及cell的高度
    - (void)setQuery:(Query *)query {
    _query = query;

        // 1.图片
        CGFloat imageX = kImageXY;
        CGFloat imageY = kImageXY;
        CGFloat imageW = kImageW;
        CGFloat imageH = kImageH;
        _imageF = CGRectMake(imageX, imageY, imageW, im    ageH);
    
        // 2.书名
        CGFloat titleX = CGRectGetMaxX(_imageF)+kCellBorder;
        CGFloat titleY = imageY;
        CGFloat titleW = 375-titleX-kCellBorder;
        CGFloat titleH = 41;
        _titleF = CGRectMake(titleX, titleY, titleW, titleH);
    
        // 3.作者
        CGFloat authorX = titleX + 46;
        CGFloat authorY = titleY + titleH + kCellBorder;
        CGFloat authorW = 189;
        CGFloat authorH = kLabelH;
        _authorF = CGRectMake(authorX, authorY, authorW, authorH);
    
        // 4.出版社
        CGFloat publishX = titleX + 60;
        CGFloat publishY = CGRectGetMaxY(_authorF) + kCellBorder;
        CGFloat publishW = 175;
        CGFloat publishH = kLabelH;
        _publishF = CGRectMake(publishX, publishY, publishW, publishH);
    
        // 5.索书号
        CGFloat numberX = imageX + 60;
        CGFloat numberY = CGRectGetMaxY(_imageF) + kCellBorder;
        CGFloat numberW = 120;
        CGFloat numberH = kLabelH;
        _numberF = CGRectMake(numberX, numberY, numberW, numberH);
    
        // 6.馆藏地
        CGFloat placeX = CGRectGetMaxX(_numberF)+60;
        CGFloat placeY = CGRectGetMaxY(_imageF) + kCellBorder;
        CGFloat placeW = 95;
        CGFloat placeH = kLabelH;
        _placeF = CGRectMake(placeX, placeY, placeW, placeH);
      
        // 7.简介
        CGFloat descX = imageX;
        CGFloat descY = CGRectGetMaxY(_numberF) + kCellBorder + kLabelH + kCellBorder;
        CGFloat descW = 375 - 2*imageX;
        CGSize contentSize = [_query.desc boundingRectWithSize:CGSizeMake(descW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17]} context:nil].size;
        _descF = CGRectMake(descX, descY, descW, contentSize.height);
    
        // 行高
        _cellHeight = descY + contentSize.height + kCellBorder;
    }   
    
  • 3.新建BorrowCell类继承自UITableViewCell
    拥有QueryFrame的属性
    @property (nonatomic, strong) QueryFrame *queryFrame;
    在QueryCell.m中的@interface中声明各个控件的属性
    @interface QueryCell () {
    // 1.封面
    UIImageView *_image;
    // 2.书名
    UILabel *_title;

      // 3.“作者”
      UILabel *_authorLabel;
      // 3.作者
      UILabel *_author;
    
      // 4.“出版社”
      UILabel *_publishLabel;
      // 4.出版社
      UILabel *_publish;
    
      // 5.“索书号”
      UILabel *_numberLabel;
      // 5.索书号
      UILabel *_number;
    
      // 6.“馆藏地”
      UILabel *_placeLabel;
      // 6.馆藏地
      UILabel *_place;
    
      // 7.“简介”
      UILabel *_descLabel;
      // 7.简介
      UILabel *_desc;
    
    }
    

在QueryCell.m中重写initWithStyle: reuseIdentifier:方法(注意:只管创建添加子控件,先不要去管子控件的位置和尺寸)
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 添加内部的子控件
// 1.封面
_image = [[UIImageView alloc] init];
[self.contentView addSubview:_image];

          // 2.书名
          _title = [[UILabel alloc] init];
          _title.numberOfLines = 0;
          [self.contentView addSubview:_title];
    
          // 3.“作者”
          CGFloat authorLabelX = kImageXY + kImageW + kCellBorder;
          CGFloat authorLabelY = kImageXY + 41 + kCellBorder;
          CGFloat authorLabelW = 46;
          CGFloat authorLabelH = kLabelH;
          _authorLabel = [[UILabel alloc] initWithFrame:CGRectMake(authorLabelX, authorLabelY, authorLabelW, authorLabelH)];
          _authorLabel.text = @"作者:";
          _authorLabel.font = [UIFont systemFontOfSize:15];
          [self.contentView addSubview:_authorLabel];
          // 3.作者
          _author = [[UILabel alloc] init];
          [self.contentView addSubview:_author];
    
          // 4.“出版社”
          CGFloat publishLabelX = authorLabelX;
          CGFloat publishLabelY = authorLabelY + authorLabelH + kCellBorder;
          CGFloat publishLabelW = 60;
          CGFloat publishLabelH = kLabelH;
          _publishLabel = [[UILabel alloc] initWithFrame:CGRectMake(publishLabelX, publishLabelY, publishLabelW, publishLabelH)];
          _publishLabel.text = @"出版社:";
          _publishLabel.font = [UIFont systemFontOfSize:15];
          [self.contentView addSubview:_publishLabel];
          // 4.出版社
          _publish = [[UILabel alloc] init];
          [self.contentView addSubview:_publish];
    
          // 5.“索书号”
          CGFloat numberLabelX = kImageXY;
          CGFloat numberLabelY = kImageXY + kImageH + kCellBorder;
          CGFloat numberLabelW = 60;
          CGFloat numberLabelH = kLabelH;
          _numberLabel = [[UILabel alloc] initWithFrame:CGRectMake(numberLabelX, numberLabelY, numberLabelW, numberLabelH)];
          _numberLabel.text = @"索书号:";
          _numberLabel.font = [UIFont systemFontOfSize:15];
          [self.contentView addSubview:_numberLabel];
          // 5.索书号
          _number = [[UILabel alloc] init];
          [self.contentView addSubview:_number];
    
          // 6.“馆藏地”
          CGFloat placeLabelX = kImageXY + numberLabelW + 120;
          CGFloat placeLabelY = numberLabelY;
          CGFloat placeLabelW = 60;
          CGFloat placeLabelH = kLabelH;
          _placeLabel = [[UILabel alloc] initWithFrame:CGRectMake(placeLabelX, placeLabelY, placeLabelW, placeLabelH)];
          _placeLabel.text = @"馆藏地:";
          _placeLabel.font = [UIFont systemFontOfSize:15];
          [self.contentView addSubview:_placeLabel];
          // 6.馆藏地
          _place = [[UILabel alloc] init];
          [self.contentView addSubview:_place];

          // 7.“简介”
          CGFloat descX = kImageXY;
          CGFloat descY = numberLabelY + numberLabelH + kCellBorder;
          CGFloat descW = 80;
          CGFloat descH = kLabelH;
          _descLabel = [[UILabel alloc] initWithFrame:CGRectMake(descX, descY, descW, descH)];
          _descLabel.text = @"图书简介:";
          _descLabel.font = [UIFont systemFontOfSize:15];
          [self.contentView addSubview:_descLabel];
          // 简介
          _desc = [[UILabel alloc] init];
          _desc.numberOfLines = 0;
          [self.contentView addSubview:_desc];
      }
      return self;
  }

注:当存在固定内容时, 在init方法中直接写定内容、计算出高度就好了。而对于需要根据模型数据显示内容的标签暂时alloc,添加到cell的contentView上就好,在后边的setter方法中会计算高度、添加数据。

  • 4.重写setQueryFrame方法,并该方法中调用设置数据和设置子控件frame的方法
    - (void)setQueryFrame:(QueryFrame *)queryFrame {
    _queryFrame = queryFrame;
    // 1.设置数据
    [self settingData];

        // 2.设置子控件的frame(x, y, width, height)
        [self settingSubviewFrame];
    }
    

设置数据的方法
- (void)settingData{
Query *query = _queryFrame.query;

       // 封面
       NSString *imageStr = [query.image stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
       NSURL *url = [NSURL URLWithString:imageStr];
       [_image sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"book.png"]];

       // 2.书名
       _title.text = query.title;
       _title.textColor = [UIColor grayColor];
    
       // 3.作者
       _author.text = query.author;
       _author.textColor = [UIColor grayColor];

       // 4.出版社
       _publish.text = query.publish;
       _publish.textColor = [UIColor grayColor];

       // 5.索书号
       _number.text = query.number;
       _number.textColor = [UIColor grayColor];

       // 6.馆藏地
       _place.text = query.place;
       _place.textColor = [UIColor grayColor];

       // 简介
       _desc.text = query.desc;
       _desc.textColor = [UIColor grayColor];

  }

注:settingData方法中若是直接加载封面图片,界面会卡顿。所以这里采用第三方库SDWebImage中的方法,如果不想使用第三方库,也可以采用下面的多线程方法:
// 封面
_image.image = [UIImage imageNamed:@"book.png"]; // 放置一张加载完成前的默认图片
// 1.获取一个全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.把任务添加到队列中执行
dispatch_async(queue, ^{
// 3.从网络上下载图片
NSURL *urlstr = [NSURL URLWithString: query.image];
NSData *data = [NSData dataWithContentsOfURL:urlstr];
UIImage *image = [UIImage imageWithData:data];
// 4.回到主线程,展示图片
dispatch_async(dispatch_get_main_queue(), ^{
_image.image = image;
});
});
设置总控件frame的方法
- (void) settingSubviewFrame {
// 1.封面
_image.frame = _queryFrame.imageF;

      // 2.书名
      _title.frame = _queryFrame.titleF;

      // 3.作者
      _author.frame = _queryFrame.authorF;

      // 4.出版社
      _publish.frame = _queryFrame.publishF;

      // 5.索书号
      _number.frame = _queryFrame.numberF;

      // 6.馆藏地
      _place.frame = _queryFrame.placeF;

      // 简介
      _desc.frame = _queryFrame.descF;
  }

QueryViewController中显示的内容

  • 1.添加UITableView
    _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kLabelY+90, self.view.frame.size.width, self.view.frame.size.height-140) style:UITableViewStyleGrouped];
    _tableView.dataSource = self;
    _tableView.delegate = self;
    [self.view addSubview:_tableView];

  • 2.添加本地数据
    NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Query.plist" ofType:nil]];
    _queryFrames = [NSMutableArray array];
    for (NSDictionary *dict in array) {
    // 创建frame对象
    QueryFrame *queryF = [[QueryFrame alloc] init];
    queryF.query = [Query queryWithDict:dict];
    [_queryFrames addObject:queryF];
    }

  • 4.遵守UITableViewDataSource、UITableViewDelegate
    实现UITableViewDataSource方法
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1; // 此方法默认为1,可省略
    }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return _queryFrames.count;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          // 设置cell的重用标识
          static NSString *ID = @"cell";
          // 到缓存池中根据标识取cell
          QueryCell *cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID];
          // 如果缓存池中没有cell,就用QueryCell创建
          if (!cell) {
          cell = [[QueryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
          }
         // 设置cell不能选中
          cell.selectionStyle = UITableViewCellSelectionStyleNone;
          // 传递模型数据
          cell.queryFrame = _queryFrames[indexPath.row];
          return cell;
    }
    

实现UITableViewDelegate方法,计算行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [_queryFrames[indexPath.row] cellHeight];
}
如此,遵从了MVC模式,减轻了controller的工作量,交给它的模型去做。并可以根据cell内容计算其高度

不需要计算cell高度时通过代码自定义cell

以上是需要计算cell的高度时才需要新建QueryFrame类。而当cell中控件的位置和尺寸固定时,采用代码方式创建cell时将省去计算cell的高度,要比上边的过程简单一些,这里大概罗列一下步骤:

  • 1.新建一个模型类(Query)增加对应的数据属性
  • 2.新建一个UITableViewCell的子类(QueryCell)
  • 3.在创建cell的时候,重写initWithStyle: reuseIdentifier:方法,添加cell内部需要使用的子控件(注意:只管创建添加子控件,先不要去管控件的位置和尺寸)
  • 4.给QueryCell增加一个Query模型属性,在拿到Query模型数据的同时设置子控件的属性
  • 5.重写setQuery:方法,在这里取出Query模型的数据显示到子控件上

解释为什么计算cell高度时才需要新建QueryFrame类?

数据源方法与代理方法的调用顺序:
先调用:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
再调用:且总共有多少行cell一次性全部算出
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
最后调用:
- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath )indexPath;
QueryCell是到
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; **方法中才调用的,所以,如果在QueryCell的set方法中计算frame是无效的。装有模型数据的数组中的Query并没有cell高度的数据。故需要新建QueryFrame类,拥有cellHeight属性,将其添加到拥有数据模型的数组中。

后记

小白出手,请多指教。
如言有误,还望斧正!

相关文章

网友评论

      本文标题:MVC模式下代码自定义cell(自计算行高)

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