AutoLayout初战----Masonry与FDTemplateLayoutCell实践

来源:互联网 发布:北京匡恩网络的董事 编辑:程序博客网 时间:2024/06/07 02:04

          学iOS也有几个月了,一直都是纯代码开发,菜鸟入门,到今天还处在Frame时代。刚好最近项目在提审,有点时间可以学学传说中的AutoLayout,其实,就是android的相对布局(RelativeLayout),没了解之前一直觉得很神秘,今天学习了一下,才发现AutoLayout也不是那么神秘不可触碰.

          在frame时代,一切数据在我们手中都是一个个坐标,我们所要做的,就是用数据反推控件的大小,然后显示出来,不过,自从i6出了之后,屏幕的宽度也不再是固定的了,AutoLayout是大势所趋。

          在AutoLayout时代,我们不需要用数据去反推控件大小,而是我们给控件添加约束,告诉控件,你该在大概哪个地方,比如,距离SuperVIew的左边20个点,距离SuperView的上边10个点,接触过android开发的人应该会对这个概念比较熟悉,然后系统帮我们自动计算出frame。

          最近也研究过用Storyboard为控件加约束,相对于纯代码来说简单很多,不过,还是怕多人开发出现问题,还是用了纯代码来实现了。

          如题,这次我是使用了Masonry来完成AutoLayout,这里有一篇关于Masonry的介绍Masonry介绍与使用实践:快速上手Autolayout,这个开源项目已经帮我们将iOS比较复杂的AutoLayout封装起来,使用起来也比较方便。

          FDTemplateLayoutCell可以帮助我们计算cell的高度并且缓存起来,使用也是非常方便,关于FDTemplateLayoutCell的介绍可以看这篇文章:优化UITableViewCell高度计算的那些事

          本篇文章所用demo所用到的数据和图片来自于FDTemplateLayoutCell的demo,本demo也是参考FDTemplateLayoutCell demo的Storyboard布局,自己用纯代码加上了约束。

ViewController.m

////  ViewController.m//  结合Masonry和FDTemplateLayoutCell,自己第一个autolayout小demo,数据来自FDTemplateLayoutCell的demo,整个demo是参考FDTemplateLayoutCell demo的Storyboard布局自己用Masonry添加约束////  Created by crw on 15/8/13.//  Copyright (c) 2015年 crw. All rights reserved.//  原文出处https://github.com/forkingdog/UITableView-FDTemplateLayoutCell#import "ViewController.h"#import "UITableView+FDTemplateLayoutCell.h"#import "FDFeedEntity.h"#import "AutoTableViewCell.h"@interface ViewController ()<UITableViewDataSource,UITableViewDelegate>{    UITableView *mTableView;}@property (nonatomic, strong) NSMutableArray *feedEntitySections;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    mTableView = [[UITableView alloc] initWithFrame:self.view.frame];    [self.view addSubview:mTableView];        mTableView.dataSource = self;    mTableView.delegate   = self;    [mTableView registerClass:[AutoTableViewCell class] forCellReuseIdentifier:@"AutoTableViewCell"];        mTableView.estimatedRowHeight = 200;//预算行高    mTableView.fd_debugLogEnabled = YES;//开启log打印高度    [self buildTestDataThen:^{        [mTableView reloadData];    }];}- (void)buildTestDataThen:(void (^)(void))then{    // Simulate an async request    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                // Data from `data.json`        NSString *dataFilePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"json"];        NSData *data = [NSData dataWithContentsOfFile:dataFilePath];        NSDictionary *rootDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];        NSArray *feedDicts = rootDict[@"feed"];                // Convert to `FDFeedEntity`        NSMutableArray *entities = @[].mutableCopy;        [feedDicts enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {            [entities addObject:[[FDFeedEntity alloc] initWithDictionary:obj]];        }];        self.feedEntitySections = entities;                // Callback        dispatch_async(dispatch_get_main_queue(), ^{            !then ?: then();        });    });}-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    AutoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AutoTableViewCell" forIndexPath:indexPath];    [self configureCell:cell atIndexPath:indexPath];    return cell;}- (void)configureCell:(AutoTableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{    cell.fd_enforceFrameLayout = NO; // Enable to use "-sizeThatFits:"    if (indexPath.row % 2 == 0) {        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;    } else {        cell.accessoryType = UITableViewCellAccessoryCheckmark;    }    cell.entity = self.feedEntitySections[indexPath.row];}-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{    //高度计算并且缓存    return [tableView fd_heightForCellWithIdentifier:@"AutoTableViewCell" cacheByIndexPath:indexPath configuration:^(AutoTableViewCell *cell) {        [self configureCell:cell atIndexPath:indexPath];    }];}-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    return self.feedEntitySections.count;}-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{    [tableView deselectRowAtIndexPath:indexPath animated:YES];    FDFeedEntity *obj = self.feedEntitySections[indexPath.row];    obj.title = @"OH,NO,TITLE CLICK";    obj.content = @"Let our rock!!!";    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    // Dispose of any resources that can be recreated.}@end
mTableView.estimatedRowHeight = 200;//预算行高

          根据FDTemplateLayoutCell,启动估算行高可以加速高度的计算,以下是原文:

About estimatedRowHeight

          estimatedRowHeight helps to delay all cells' height calculation from load time to scroll time. 

Feel free to set it or not when you're using FDTemplateLayoutCell.If you use "cacheByIndexPath" API,

setting this estimatedRowHeight property is a better practice for imporve load time, and it DOES NO LONGER 

affect scroll performance because of "precache".

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{    //高度计算并且缓存    return [tableView fd_heightForCellWithIdentifier:@"AutoTableViewCell" cacheByIndexPath:indexPath configuration:^(AutoTableViewCell *cell) {        [self configureCell:cell atIndexPath:indexPath];    }];}

          FDTemplateLayoutCell提供的计算cell高的代码,轻松解决行高计算,并且缓存起来.

          接下来,重头戏都在我们的AutoTableViewCell.m

////  AutoTableViewCell.m//  TableViewAuto////  Created by crw on 15/8/13.//  Copyright (c) 2015年 crw. All rights reserved.//#import "AutoTableViewCell.h"#import "Masonry.h"#define margin 10#define WS(weakSelf)  __weak __typeof(&*self)weakSelf = self;@interface AutoTableViewCell(){    MASConstraint *constraint_content;/**<内容上边距为5的约束,没内容时将边距设置为0 */    MASConstraint *constraint_mainImageView;    MASConstraint *constraint_userNameLabel;}@end@implementation AutoTableViewCell- (void)awakeFromNib {    [super awakeFromNib];    // Initialization code    self.contentView.bounds = [UIScreen mainScreen].bounds;}- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{    if (self == [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {        [self setAutoLayout];    }    return self;}- (void)addView:(UIView *)view{    [self.contentView addSubview:view];}- (void)setAutoLayout{    WS(ws);    _titleLabel                    = [[UILabel alloc] init];    _titleLabel.numberOfLines = 0;    //_titleLabel.backgroundColor    = [UIColor redColor];    [self addView:_titleLabel];        _contentLabel                  = [[UILabel alloc] init];    _contentLabel.numberOfLines    = 0;    _contentLabel.font             = [UIFont systemFontOfSize:14];    _contentLabel.textColor        = [UIColor grayColor];    //_contentLabel.backgroundColor  = [UIColor purpleColor];    [self addView:_contentLabel];        _mainImageView                 = [[UIImageView alloc] init];    _mainImageView.contentMode     = UIViewContentModeScaleAspectFill;    _mainImageView.clipsToBounds   = YES;    //_mainImageView.backgroundColor = [UIColor orangeColor];    [self addView:_mainImageView];        _userNameLabel                 = [[UILabel alloc] init];    //_userNameLabel.backgroundColor = [UIColor greenColor];    _userNameLabel.textColor       = [UIColor orangeColor];    _userNameLabel.font            = [UIFont systemFontOfSize:12];    [self addView:_userNameLabel];        _timeLabel                     = [[UILabel alloc] init];    _timeLabel.textColor           = [UIColor blueColor];    _timeLabel.font                = [UIFont systemFontOfSize:12];    //_timeLabel.backgroundColor     = [UIColor blueColor];    [self addView:_timeLabel];        [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {        make.leading.equalTo(ws.contentView).offset(margin);        make.trailing.equalTo(ws.contentView.mas_trailing).offset(-margin);        make.top.equalTo(ws.contentView).offset(margin);    }];        [_contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {        make.leading.equalTo(_titleLabel.mas_left);        make.right.equalTo(ws.contentView.mas_right).offset(-margin);        //以下设置距离title的边距,设置两条优先度不同的约束,内容为空时将优先度高的约束禁用        make.top.equalTo(_titleLabel.mas_bottom).priorityLow();//优先度低,会被优先度高覆盖        constraint_content = make.top.equalTo(_titleLabel.mas_bottom).offset(5).priorityHigh();    }];        [_mainImageView mas_makeConstraints:^(MASConstraintMaker *make) {        make.left.equalTo(_titleLabel.mas_left);        make.height.greaterThanOrEqualTo(@0);        make.right.lessThanOrEqualTo(ws.contentView.mas_right).offset(-margin);        make.top.equalTo(_contentLabel.mas_bottom).priorityLow();        constraint_mainImageView = make.top.equalTo(_contentLabel.mas_bottom).offset(5).priorityHigh();    }];        [_userNameLabel mas_makeConstraints:^(MASConstraintMaker *make) {        make.left.equalTo(_titleLabel.mas_left);        make.top.equalTo(_mainImageView.mas_bottom).priorityLow();        constraint_userNameLabel = make.top.equalTo(_mainImageView.mas_bottom).offset(5).priorityHigh();    }];        [_timeLabel mas_makeConstraints:^(MASConstraintMaker *make) {        make.right.equalTo(ws.contentView.mas_right).offset(-margin);        make.top.equalTo(_userNameLabel.mas_top);        make.bottom.equalTo(self.contentView.mas_bottom).offset(-margin);    }];}- (void)setSelected:(BOOL)selected animated:(BOOL)animated {    [super setSelected:selected animated:animated];    // Configure the view for the selected state}- (void)setEntity:(FDFeedEntity *)entity{    _entity = entity;        self.titleLabel.text     = entity.title;    self.contentLabel.text   = entity.content;    self.mainImageView.image = entity.imageName.length > 0 ? [UIImage imageNamed:entity.imageName] : nil;    self.userNameLabel.text  = entity.username;    self.timeLabel.text      = entity.time;        self.contentLabel.text.length ==  0 ?[constraint_content deactivate]:[constraint_content activate];    self.mainImageView.image      == nil?[constraint_mainImageView deactivate]:[constraint_mainImageView activate];    self.userNameLabel.text.length==  0 ?[constraint_userNameLabel deactivate]:[constraint_userNameLabel activate];}#if 0// If you are not using auto layout, override this method- (CGSize)sizeThatFits:(CGSize)size{    CGFloat totalHeight = 0;    totalHeight += [self.titleLabel sizeThatFits:size].height;    totalHeight += [self.contentLabel sizeThatFits:size].height;    totalHeight += [self.mainImageView sizeThatFits:size].height;    totalHeight += [self.userNameLabel sizeThatFits:size].height;    totalHeight += 40; // margins    return CGSizeMake(size.width, totalHeight);}#endif@end
          在setAutoLayout里面,是我们AutoLayout的主要代码,加需要的view加到contentView,用Masonry给每个view添加了约束,代码和原生的相比,比较好理解。

- (CGSize)sizeThatFits:(CGSize)size
          FDTemplateLayoutCell支持两种模式的算高,AutoLayout和Frame.以下是官方原文:

Frame layout mode

FDTemplateLayoutCell offers 2 modes for asking cell's height.

  1. Auto layout mode using "-systemLayoutSizeFittingSize:"
  2. Frame layout mode using "-sizeThatFits:"

Generally, no need to care about modes, it will automatically choose a proper mode by whether you have set auto layout constrants on cell's content view. If you want to enforce frame layout mode, enable this property in your cell's configuration block:

cell.fd_enforceFrameLayout = YES;

And if you're using frame layout mode, you must override -sizeThatFits: in your customized cell and return content view's height (separator excluded)

- (CGSize)sizeThatFits:(CGSize)size{    return CGSizeMake(size.width, A+B+C+D+E+....);}

          FDTemplateLayoutCell有两种计算高度的模式

            1.一种是AutoLayout使用的-systemLayoutSizeFittingSize:         

            2.另一种是Frame使用的-sizeThatFits:

           可以通过fd_enforceFrameLayout = YES 开启Frame模式,注意,开启Frame模式需要重写- (CGSize)sizeThatFits,如下:

- (CGSize)sizeThatFits:(CGSize)size{    return CGSizeMake(size.width, A+B+C+D+E+....);}

         本文demo点此下载,也可以前往github下载



0 0
原创粉丝点击