Masonry/MyLinearLayout/SDAutoLayout

iOS中的代码自动布局一直用的Masonry,最近发现MyLinearLayout,SDAutoLayout功能更强大,Masonry更多的只是NSLayoutConstraint的简易书写版本,没有更多的封装功能,这里实践下用三个库写实现同样的界面。

属性 Masonry MyLinearLayout SDAutoLayout
左侧 left myLeftMargin leftSpaceToView
右侧 right myRightMargin rightSpaceToView
上侧 top myTopMargin topSpaceToView
下侧 bottom myBottomMargin bottomSpaceToView
横向中点 centerX myCenterXOffset centerXIs
纵向中点 centerY myCenterYOffset centerYIs

本文会用三种框架实现两个常用的情景 :UIScrollView自动计算contentSize,UITableview高度自适应 来体验下

scrollview.gif

这里借用下forkingdog的数据源与图片

timeline.gif

Masonry

这个没什么好说,具体会体现在代码里,贴两个写的很好的用法教程

这个直接拿链接里的例子,加上自己的理解注释

  UIScrollView *scrollView = [UIScrollView new];
  scrollView.backgroundColor = [UIColor whiteColor];
  [self.view addSubview:scrollView];

/*先加到父视图上,再设置约束,这个步骤必须在前!!否则会崩溃
  SDAutoLayout一样也是必须先加到父视图上,才能设置自动布局
 */

[scrollView mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(self.view).with.insets(UIEdgeInsetsMake(5,5,5,5));//设置scrollView大小与父视图内边距为5
}];


/*
 创建一个视图容器container加到scrollView里
 container的edges与scrollView相同,这个决定container的位置,与scrollView各边距为0
 
 container的width与height才是决定它的大小,而这个也决定了scrollView的contentSize
 
 在这里(伪代码)
 scrollView.contentSize = CGSizeMake(container.witdth + container.左边距 + container.右边距,
                                     container.height + container.顶部边距 + container.底部边距,)
 
 而container的width与scrollView相同,且左边距右边距为0,所以实际上横向不能滑动
 container的height由其子视图决定,具体分析下for循环里的代码
 
 */

UIView *container = [UIView new];
[scrollView addSubview:container];
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(scrollView);
    make.width.equalTo(scrollView);
}];

int count = 5;

//自上而下的顺序依次排列视图
UIView *lastView = nil;
for ( int i = 1 ; i <= count ; ++i ){
    UIView *subv = [UIView new];
    subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                      saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                      brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                           alpha:1];
    
    //先加入到父视图,然后再设置其约束
    [container addSubview:subv];
    
    [subv mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.left.and.right.equalTo(container);//左右边距为0,相当于宽度与父视图与相等
        
        make.height.mas_equalTo(@(100+(i*20)));//设置高度
        
        if ( lastView ){
            make.top.mas_equalTo(lastView.mas_bottom);//设置排列在上一视图的下端
        }
        else{
            make.top.mas_equalTo(container.mas_top);//设置在顶端的子视图与父视图顶部边距相同
        }
    }];
    
    lastView = subv;
}


/*最后设置一下最底部的lastView与父视图container底部的距离,以此能自动计算出container的高度,
 也就算出了scrollView的contentSize,确定了能滑动的范围
 */
[container mas_makeConstraints:^(MASConstraintMaker *make) {
    make.bottom.equalTo(lastView.mas_bottom);
}];
  • UITableview 高度自适应

Masonry搭配UITableView-FDTemplateLayoutCell使用


MyLinearLayout

一套基于对frame属性的设置,并通过重载layoutSubviews函数来实现对子视图进行布局的布局框架。

  • #######在UIScrollView顺序排列一些view并自动计算contentSize
/*这样写居然没有任何效果...
UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];
scrollView.myMargin = 5;
[self.view addSubview:scrollView];
*/

/*第一种方式 就正常设置frame
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
scrollView.backgroundColor = [UIColor whiteColor];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; //让uiscrollView的尺寸总是保持和父视图一致。
[self.view addSubview:scrollView];
 */


//第二种方式 反正就要滚动,设置self.view = scrollView
UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];
self.view = scrollView;

/*由于我们是要实现从上而下按顺序添加视图 所以这里涉及到线性布局MyLinearLayout
 线性布局是一种里面的子视图按添加的顺序从上到下或者从左到右依次排列的单列(单行)布局视图
 */

/*MyLayoutViewOrientation_Vert = 0,  //整体从上到下    
  MyLayoutViewOrientation_Horz = 1,  //整体从左到右
 */

MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyLayoutViewOrientation_Vert];
contentLayout.padding = UIEdgeInsetsMake(5, 5, 5 ,5); //设置布局内的子视图离自己的边距.
contentLayout.myLeftMargin = 0;
contentLayout.myRightMargin = 0;                          //同时指定左右边距为0表示宽度和父视图一样宽

/*
 heightDime  这种加上Dime的属性,是相当于设置自动布局尺寸,
 而myHeight则是直接设置frame里的height
 */
contentLayout.heightDime.lBound(scrollView.heightDime, 10, 1); //高度虽然是wrapContentHeight的。但是最小的高度不能低于父视图的高度加10.
[scrollView addSubview:contentLayout];

int count = 5;

//自上而下的顺序依次排列视图

for ( int i = 1 ; i <= count ; ++i ){
    UIView *subv = [UIView new];
    subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                      saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                      brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                           alpha:1];
    
    /*
        由于MyLinearLayout都帮我们处理好了上下图关系,
        只需要设置下位置大小就好
     */
    subv.myHeight = 100+(i*20);
    subv.myTopMargin = 0;
    subv.myLeftMargin = 0;
    subv.myRightMargin = 0;
    [contentLayout addSubview:subv];
}
  • UITableview 高度自适应

代码片段

     MyLayoutFeedCell *cell = (MyLayoutFeedCell*)[tableView dequeueReusableCellWithIdentifier:kMyLayoutFeedCellIdentify];
     if (cell == nil) {
          cell = [[MyLayoutFeedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kMyLayoutFeedCellIdentify];
     }
     cell.entity = self.dataSource[indexPath.row]; 
     return cell;


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

        MyLayoutFeedCell *cell = (MyLayoutFeedCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
        //通过布局视图的estimateLayoutRect函数能够评估出UITableViewCell的动态高度。estimateLayoutRect并不会进行布局
        //而只是评估布局的尺寸,这里的宽度不传0的原因是上面的UITableViewCell在建立时默认的宽度是320(不管任何尺寸都如此),因此如果我们
        //传递了宽度为0的话则会按320的宽度来评估UITableViewCell的动态高度,这样当在375和414的宽度时评估出来的高度将不会正确,因此这里需要
        //指定出真实的宽度尺寸;而高度设置为0的意思是表示高度不是固定值需要评估出来。
        //UITableViewCell的动态高度评估不局限于线性布局,相对布局也是同样适用的。
        CGRect rect = [cell.rootLayout estimateLayoutRect:CGSizeMake(tableView.frame.size.width, 0)];
        return rect.size.height + 1;  //如果使用系统自带的分割线,请返回rect.size.height+1
    }

SDAutoLayout

实际上一样也是通过对frame的设置,为UIView写了一个SDAutoLayout分类,在+(void)load交换layoutSubviews方法,实现自己的sd_layoutSubviews

@implementation UIView (SDAutoLayout)

+ (void)load
{
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
    
          NSArray *selStringsArray = @[@"layoutSubviews"];
    
          [selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) {
              NSString *mySelString = [@"sd_" stringByAppendingString:selString];
        
              Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString));
              Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString));
        method_exchangeImplementations(originalMethod, myMethod);
    }];
});
}
  • #######在UIScrollView顺序排列一些view并自动计算contentSize
 UIScrollView *scrollView = [UIScrollView new];
scrollView.backgroundColor = [UIColor whiteColor];

// SDAutoLayout 与 Masonry相同,先加入到父视图,后设置布局
[self.view addSubview:scrollView];
scrollView.sd_layout.spaceToSuperView(UIEdgeInsetsMake(5,5,5,5));//设置scrollView与父视图各边距为0,即确定了大小与位置



int count = 5;

//自上而下的顺序依次排列视图
UIView *lastView = nil;
for ( int i = 1 ; i <= count ; ++i ){
    UIView *subv = [UIView new];
    subv.backgroundColor = [UIColor colorWithHue:( arc4random() % 256 / 256.0 )
                                      saturation:( arc4random() % 128 / 256.0 ) + 0.5
                                      brightness:( arc4random() % 128 / 256.0 ) + 0.5
                                           alpha:1];
    
    [scrollView addSubview:subv];
    subv.sd_layout
    .leftSpaceToView(scrollView,0)
    .rightSpaceToView(scrollView,0)
    .heightIs(100+(i*20));
    
    
    if ( lastView ){
        subv.sd_layout.topSpaceToView(lastView,0);//设置排列在上一视图的下端
    }
    else{
        subv.sd_layout.topSpaceToView(scrollView,0);//设置在顶端的子视图与父视图顶部边距相同
    }
    
    lastView = subv;
}


// scrollview自动contentsize
[scrollView setupAutoContentSizeWithBottomView:lastView bottomMargin:0];
  • UITableview 高度自适应

代码片段
SDAutoLayoutFeedCell *cell = [tableView dequeueReusableCellWithIdentifier:kSDAutoLayoutFeedCellIdentify forIndexPath:indexPath];
cell.entity = self.dataSource[indexPath.row];
// 此步设置用于实现cell的frame缓存,可以让tableview滑动更加流畅
[cell useCellFrameCacheWithIndexPath:indexPath tableView:tableView];
return cell;

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

     //  cell自适应步骤2      model 为模型实例, keyPath 为 model 的属性名,通过 kvc 统一赋值接口 
    return [self.tableView cellHeightForIndexPath:indexPath model:self.dataSource[indexPath.row] keyPath:@"entity" cellClass:[SDAutoLayoutFeedCell class] contentViewWidth:[self cellContentViewWith]];

    }

总结

这两个库都有丰富的代码例子和教程,都还在迭代更新中
MyLinearLayout
SDAutoLayout

MyLinearLayout的入门稍微高点,一来就是基于线性布局构建,还多达6种布局,相对布局,框架布局,表格布局,流式布局,浮动布局,当然它的功能是最强大的,布局思想也是很好,甚至可以替代UITableView,UICollectionView,也加入了IBInspectable,可以在界面上布局。虽然如此,学习成本较高,而且自然不能完全替代UITableView,UICollectionView,很多一些动画效果手势交互体验是无法做到的。

SDAutoLayout 看介绍是已经有很多项目用上了,仔细看发现并没有封装太多的功能,顶多就是等距或者等宽排列一组视图,一些基本用法思想基本与NSLayoutConstraint类似,易上手。

Masonry 加上UITableView,UICollectionView,UIStackView布局还是我的首选吧,很多复杂的界面只要用这几个配合好都能很好实现。

但是还是愿意尝试下新功能,分享MyLinearLayout的官方例子中对于流标签的实现,就非常简洁

Simulator Screen Shot 2016年8月8日 23.35.59.png

代码片段

self.flowLayout = [[MyFlowLayout alloc] initWithOrientation:MyLayoutViewOrientation_Vert arrangedCount:0];
self.flowLayout.myMargin = 0;
self.flowLayout.myTopMargin = 64;
self.flowLayout.subviewMargin = 10;

/**
 * 设置布局视图四周的内边距值。所谓内边距是指布局视图内的所有子视图离布局视图四周的边距。通过为布局视图设置内边距可以减少为所有子视图设置外边距的工作,而外边距则是指视图离父视图四周的距离。
 */
self.flowLayout.topPadding = 20;
self.flowLayout.leftPadding = 20;
self.flowLayout.rightPadding = 15;

[self.view addSubview:self.flowLayout];

[[DataManager shareManager].tagTexts enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    
    [self createTagButton:obj];
    
}];

整篇的实践代码戳这
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,971评论 3 38
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,010评论 4 62
  • 沁园春. 赞中国女排奥运夺冠 (白定稳) 里约球场,群雄逐鹿,搏击巨浪。望当今排界,列强环伺;中华靓女,又出锋芒。...
    白定稳阅读 295评论 2 1
  • ​ “多吃蔬菜,少喝饮料” “早睡早起,少玩手机” “别抽烟,别熬夜, 多锻炼,多读书,少看电视”…… 这些耳熟能...
    皮皮鲁总动员阅读 237评论 0 0
  • 那冰冷的大网沉入无垠的海洋 只捞起瘦骨凌旬的鲸鱼和潜艇 亿万年前燃烧的孤独 在宇宙尽头发出回音 像在时间长廊上刻下...
    鲟余阅读 184评论 4 4