欧阳大哥的专栏

坚持原创,以造轮子为乐

IOS不用AutoLayout也能实现自动布局的类(4)----MyTableLayout横空出世

界面库下载地址:

https://github.com/youngsoft/MyLinearLayout      

        前面的几篇文章里我分别介绍了线性布局(MyLinearLayout),相对布局(MyRelativeLayout),框架布局(MyFrameLayout)这三种布局。这三种布局中 :

线性布局主要应用于容器视图里面的所有子视图依次从上往下排列或者从左往右排列的场景。

垂直线性布局
子视图1
子视图2
子视图3
子视图4

水平线性布局
子视图1 子视图2 子视图3 子视图4

当然我们也可以用线性布局嵌套线性布局的方法来实现一些复杂的界面布局,比如(这个例子如果用MyTableLayout实现将更加简单):


复杂布局
水平线性布局子视图1 水平线性布局子视图2

子视图1 子视图2 子视图3

子视图1
子视图2
子视图3


相对布局主要用于容器视图中的各个子视图之间的位置和高宽以及子视图和容器视图之间具有一定的依赖和约束关系的场景。比如说子视图1的位置在子视图2的右下角,并且宽度等于子视图3的宽度,而子视图3的底部又在容器视图的底部。

           

相对布局
子视图1(等宽3)    
  子视图2  
    子视图3(等宽1)

相对布局因为需要指定各子视图之间的依赖关系,因此如果设置不当就会产生递归死循环的情况,而且在某种程度上不利于子视图之间的位置的更新和变化等等,其中IOS自带的AutoLayout其实就是一套相对布局的实现,相对布局功能很强大也可以很容易布局复杂的界面,缺点是使用不当的话就容易造成约束死循环的情况。


框架布局主要用于容器视图中的个子视图在容器视图的上,中,下,左,中,右,拉升填充,居中显示等11种情况。

框架布局
左上 中上 右上
左中 居中 右中
左下 中下 右下

框架布局中的子视图只跟容器视图之间产生关系,子视图之间没有任何关联关系。


一、表格布局的介绍


    在一些实际的应用界面中,我们希望我们的子视图以表格的形式展示出来,这些表格展示可以是正规的几行几列并且固定高宽的形式,也可能是每一行的列数都不同,也可能是每行的高度不一样,也可能是一行内的各列的宽度也不一样,


               水平表格布局

                                                                                                                                       

                                                                                                                                                 垂直表格布局






















要实现上面的两种界面风格,我们可以借助MyTableLayout来实现。


MyTableLayout是从MyLinearLayout中继承而来,因此表格布局也分为垂直表格布局和水平表格布局,样式请参考上面的图例的展示风格。而表格的风格样式同样通过

orientation属性来设置。不管是垂直表格布局还是水平表格布局。我们在建立了表格布局视图并指定了表格风格后,我们首先的步骤是要为表格添加行(如果是水平表格其实就是添加列,下面如果为说明都是如此概念),那这个步骤可以通过MyTableLayout的方法:


-(void)addRow:(CGFloat)rowSize colSize:(CGFloat)colSize;

-(void)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex;


来实现,前者是往表格布局尾部添加一行,而后者则是在指定的位置插入一行。这里需要说明的参数是rowSize,colSize的意义,我们知道只要我们插入一行时我们总是需要指出插入的这一行的行高是多少,同时要指出插入的这行的列单元格的宽度是如何指定的(每列的宽度固定,还是有单元格自己指定等等)


 rowSize为MTLSIZE_WRAPCONTENT表示由最高的单元格视图决定本行高度,每个单元格视图需要自己设置高度;为MTLSIZE_AVERAGE表示均分高度,单元格视图不需要设置高度;大于0表示固定高度,单元格视图不需要设置高度.


 colSize 为MTLSIZE_MATCHPARENT表示每个单元格视图需要自己指定宽度,整体行宽和表格布局一致;为MTLSIZE_WRAPCONTENT表示每个单元格视图需要自己指定宽度,整个行宽包裹所有子视图;为MTLSIZE_AVERAGE表示均分宽度,这时候单元格视图不必设置宽度;大于0表示单元格视图固定宽度,这时候单元格视图可以不必设置宽度。


同时我们也提供了对行操作的其他方法:


//删除指定的行

-(void)removeRowAt:(NSInteger)rowIndex;

//交行两个行的内容

-(void)exchangeRowAt:(NSInteger)rowIndex1 withRow:(NSInteger)rowIndex2;

//得到行视图,从返回我们可以看出,我们调用插入行操作时,系统内部会自动建立一个MyLinearLayout线性布局视图作为行视图,如果是垂直表格则默认是水平线性布局,而如果是水平表格则默认是垂直线性布局,因此我们可以通过这个方法来设置行的其他的各种属性,比如说行间距。

-(MyLinearLayout*)viewAtRowIndex:(NSInteger)rowIndex;

//返回当前有多少行

-(NSUInteger)countOfRow;



当我们插入了一行后,我们就需要为这一行添加单元格视图(列视图),每一行都可以无限制的添加单元格视图,也就是说一行可以有很多的列,每一行的列数都可以不一样,我们可以通过如下的方法来添加或者删除列:

-(void)addCol:(UIView*)colView atRow:(NSInteger)rowIndex;

-(void)insertCol:(UIView*)colView atIndexPath:(NSIndexPath*)indexPath;

-(void)removeColAt:(NSIndexPath*)indexPath;

-(void)exchangeColAt:(NSIndexPath*)indexPath1 withCol:(NSIndexPath*)indexPath2;


注意上面的添加列时,需要指定在哪一行添加列,在添加列之前必须要把行添加进去,也就是说行索引rowIndex不能越界,为了简单的描述行索引和列索引的关系我们使用了NSIndexPath这个对象来描述,我们对NSIndexPath进行了扩展,以便用于方便的指定行和列的索引:

@interface NSIndexPath(MyTableLayoutEx)


+(instancetype)indexPathForCol:(NSInteger)col inRow:(NSInteger)row;


@property(nonatomic,assign,readonly)NSInteger col;


@end



同时我们也方便的提供了单元格列视图的获取和数量的获取的方法

//返回列视图

-(UIView*)viewAtIndexPath:(NSIndexPath*)indexPath;

//返回指定行的列的数量。

-(NSUInteger)countOfColInRow:(NSInteger)rowIndex;



上面就是我们对表格布局的所有函数的介绍,使用起来也很简单,步骤就是先添加行,然后在每行中依次一个个的添加列视图,也就是单元格视图。

我们将分别建立两个风格的表格进行例子的说明。


二、垂直表格

   所谓垂直表格就是行是从上往下,一行一行布局起来的,所有行内的单元格视图都是从左往右一列一列布局起来的,建立垂直表格是默认的表格。先看界面布局效果。






我们分别建立了6行不同尺寸的表格,上可以看出每一行的列数不一样,每一行的高度不一样,每一行的总体列宽不一样,每一行的每一列的宽度都不一样,而这些我们都是通过在插入行时通过设置不同的rowSize,colSize来达到效果的。具体代码参考如下:

-(void)loadView
{
    [super loadView];
    
    /*
     有的时候我们希望让一个布局视图放入到非布局视图中去,但又希望布局视图的宽高和非布局父视图宽高一致。
     这时候我们可以设置myHeight,myWidth来指定自身的高宽,我们也可以通过myLeft = 0,myRight = 0来让其跟父视图保持一样的宽度,但如果是这样的话还需要设置wrapContentWidth = NO. 设置高度同理。
     */
    MyTableLayout *tbll = [MyTableLayout tableLayoutWithOrientation:MyLayoutViewOrientation_Vert];
    tbll.myLeft = tbll.myRight = 0;  //宽度和非布局父视图一样宽
    tbll.myTop = tbll.myBottom = 0;  //高度和非布局父视图一样高
    [self.view addSubview:tbll];

    
    //第一行固定高度固定宽度
    [tbll addRow:30 colSize:70];
    [tbll viewAtRowIndex:0].backgroundColor = [UIColor colorWithWhite:0.1 alpha:1];
    
    UILabel *colView = [UILabel new];
    colView.text = @"Cell00";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.myLeft = 10; //可以使用myLeft,myTop,myRight,myBottom来调整间隔
    colView.myTop = 5;
    colView.myBottom = 5;
    colView.myRight = 40;
    
    colView.backgroundColor = [UIColor redColor];
    [tbll addCol:colView atRow:0];
    
    colView = [UILabel new];
    colView.text = @"Cell01";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    colView.myLeft = 20;
    [tbll addCol:colView atRow:0];
    
    colView = [UILabel new];
    colView.text = @"Cell02";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor blueColor];
    [tbll addCol:colView atRow:0];
    
    //第二行固定高度,均分宽度
    [tbll addRow:40 colSize:MTLSIZE_AVERAGE];
    [tbll viewAtRowIndex:1].backgroundColor = [UIColor colorWithWhite:0.2 alpha:1];

    
    colView = [UILabel new];
    colView.text = @"Cell10";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor redColor];
    [tbll addCol:colView atRow:1];
    
    colView = [UILabel new];
    colView.text = @"Cell11";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    [tbll addCol:colView atRow:1];
    
    
    colView = [UILabel new];
    colView.text = @"Cell12";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor blueColor];
    [tbll addCol:colView atRow:1];
    
    colView = [UILabel new];
    colView.text = @"Cell13";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor yellowColor];
    [tbll addCol:colView atRow:1];
    
    //第三行固定高度,子视图自己决定宽度。
    [tbll addRow:30 colSize:MTLSIZE_WRAPCONTENT];
    [tbll viewAtRowIndex:2].backgroundColor = [UIColor colorWithWhite:0.3 alpha:1];
    colView = [UILabel new];
    colView.text = @"Cell20";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor redColor];
    colView.myWidth = 100;
    [tbll addCol:colView atRow:2];

    colView = [UILabel new];
    colView.text = @"Cell21";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    colView.myWidth = 200;
    [tbll addCol:colView atRow:2];
    
    //第四行固定高度,子视图自己决定宽度。
    [tbll addRow:30 colSize:MTLSIZE_MATCHPARENT];
    [tbll viewAtRowIndex:3].backgroundColor = [UIColor colorWithWhite:0.4 alpha:1];
    colView = [UILabel new];
    colView.text = @"Cell30";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor redColor];
    colView.myWidth = 80;
    [tbll addCol:colView atRow:3];
    
    colView = [UILabel new];
    colView.text = @"Cell31";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    colView.myWidth = 200;
    [tbll addCol:colView atRow:3];
    
    //第五行高度均分.这里设置为0表示剩余高度再均分。宽度均分,
    [tbll addRow:MTLSIZE_AVERAGE colSize:MTLSIZE_AVERAGE];
    MyLinearLayout *row4 = [tbll viewAtRowIndex:4];
    //可以设置行的属性.比如padding, 线条颜色,
    row4.padding = UIEdgeInsetsMake(3, 3, 3, 3);
    row4.topBorderline = [[MyBorderline alloc] initWithColor:[UIColor blackColor]];
    row4.topBorderline.thick = 2;
    row4.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1];

    colView = [UILabel new];
    colView.text = @"Cell40";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor redColor];
    [tbll addCol:colView atRow:4];
    
    colView = [UILabel new];
    colView.text = @"Cell41";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    [tbll addCol:colView atRow:4];
    
    //第六行高度由子视图决定,均分宽度
    [tbll addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_AVERAGE];
    [tbll viewAtRowIndex:5].backgroundColor = [UIColor colorWithWhite:0.6 alpha:1];
    
    colView = [UILabel new];
    colView.text = @"Cell50";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor redColor];
    colView.myHeight = 80;
    [tbll addCol:colView atRow:5];
    
    colView = [UILabel new];
    colView.text = @"Cell51";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor greenColor];
    colView.myHeight = 120;
    [tbll addCol:colView atRow:5];
    
    colView = [UILabel new];
    colView.text = @"Cell52";
    colView.textAlignment = NSTextAlignmentCenter;
    colView.backgroundColor = [UIColor blueColor];
    colView.myHeight = 70;
    [tbll addCol:colView atRow:5];





    
    
}



上面的代码中我们在插入行时分别为rowSize,colSize设置了6种不同的参数的组合,我们看到其中有些行中需要指定每个单元格的列宽和行高,而有的则不需要。是否需要单元格指定行高和列宽则是有rowSize,colSize的不同的参数值决定的。 有时候我们的表格可能需要指定行间距和列间距,而这些都可以通过行视图的myXXX, 和列视图的myXXX的设置来进行个性化的定制,这里需要强调一下rowSize,colSize都等于MTLSIZE_AVERAGE的情况,他们的意义是表示行和列会均分高度和宽度。

  举例来说,假设我们建立了一个宽高为100*100的表格布局,而我们第一行的rowSize,colSize设置为MTLSIZE_AVERAGE时,则当插入第一行时则这一行的高度就是100,而这一行插入第一列时则这一列的宽度就是100,而如果再插入一列时则两列的宽度都会调整为50,同样当我们再次插入一行时则两行的高度都将会调整为50. (这个像不像HTML中的表格的行列的宽高指定的风格)




三、水平表格(瀑布流)

   所谓水平表格就是行是从左往右,一行一行布局起来的,所有行内的单元格视图都是从上往下一列一列布局起来的,建立水平表格需要将

orientation =MyLayoutViewOrientation_Horz 水平表格也就是一个瀑布流风格的表格,我们可以通过将表格放入到UIScrollView中进行从上到下的滚动以便展示所有内容。先看界面布局效果:





这个例子中我们建立了一个三行平均宽度的水平表格,然后每添加一个列前都先算出哪行的高度最低,然后找到最低高度的行再插入进去,这样就形成了一个瀑布流的效果。


-(void)loadView
{    
    [super loadView];
    self.view.backgroundColor = [UIColor blackColor];
    UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    scrollView.autoresizingMask =  UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    [self.view addSubview:scrollView];
    
    /*
       创建一个水平的表格布局,水平表格布局主要用于建立瀑布流视图。需要注意的是水平表格中row也就是行是从左到右排列的,而每行中的col也就是列是从上到下排列的。
     */
    
    _rootLayout = [MyTableLayout tableLayoutWithOrientation:MyLayoutViewOrientation_Horz];
    _rootLayout.wrapContentWidth = NO;
    _rootLayout.rowSpacing = 5;
    _rootLayout.colSpacing = 10;
    _rootLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5);  //分别设置表格布局里面的行间距、列间距、内部padding边距。
    
    _rootLayout.widthSize.equalTo(scrollView.widthSize);
    _rootLayout.wrapContentHeight = YES; //布局宽度和父视图一致,高度则由内容包裹。这是实现将布局视图加入滚动条视图并垂直滚动的标准方法。
    [scrollView addSubview:_rootLayout];
    
    //为瀑布流建立3个平均分配的行,每行的列的尺寸由内容决定。
    [_rootLayout addRow:MTLSIZE_AVERAGE colSize:MTLSIZE_WRAPCONTENT];
    [_rootLayout addRow:MTLSIZE_AVERAGE colSize:MTLSIZE_WRAPCONTENT];
    [_rootLayout addRow:MTLSIZE_AVERAGE colSize:MTLSIZE_WRAPCONTENT];
    
}

上面的代码是我们建立水平表格的代码。代码中我们建立了3个平均分配宽度的行,而每列的高度则是由子视图决定。为了演示动态添加瀑布流的效果,我们通过一个按钮每次增加一个列。代码如下:

-(void)handleAddColLayout:(id)sender
{
    //获取表格布局中的每行的高度,找到高度最小的一行,如果高度都相等则选择索引号小的行。
    CGFloat minHeight = CGFLOAT_MAX;
    NSInteger rowIndex = 0;
    for (NSInteger i = 0; i < self.rootLayout.countOfRow; i++)
    {
        UIView *rowView = [self.rootLayout viewAtRowIndex:i];
        if (CGRectGetMaxY(rowView.frame) < minHeight)
        {
            minHeight = CGRectGetMaxY(rowView.frame);
            rowIndex = i;
        }
    }
    
    NSArray *images = @[@"p1-11",
                        @"p1-12",
                        @"p1-21",
                        @"p1-31",
                        @"p1-32",
                        @"p1-33",
                        @"p1-34",
                        @"p1-35",
                        @"p1-36",
                        @"image1",
                        @"image2",
                        @"image3"
                        ];
    
    static NSInteger sTag = 1000;
    
    
    UIView *colLayout = [self createColLayout:images[arc4random_uniform((uint32_t)images.count)]
                                          title:[NSString stringWithFormat:@"单元格标题:%03ld", (long)sTag]];
    colLayout.tag = sTag++;
    [self.rootLayout addCol:colLayout atRow:rowIndex];
}

上面的代码是每次点击按钮随机添加一个单元格视图的方法,在代码的最开始处,我们算出高度最低的行,然后添加的单元格视图就放入那一行中。


下面代码是用来创建单元格视图的代码:


-(UIView*)createColLayout:(NSString*)image title:(NSString*)title
{
    MyLinearLayout *colLayout = [MyLinearLayout linearLayoutWithOrientation:MyLayoutViewOrientation_Vert];
    colLayout.gravity = MyGravity_Horz_Fill;  //里面所有子视图的宽度都跟父视图保持一致,这样子视图就不需要设置宽度了。
    colLayout.wrapContentHeight = YES;
    colLayout.subviewSpace = 5;  //设置布局视图里面子视图之间的间距为5个点。
    colLayout.backgroundColor = [UIColor whiteColor];
    [colLayout setTarget:self action:@selector(handleColLayoutTap:)];
    colLayout.highlightedOpacity = 0.3; //设置触摸事件按下时的不透明度,来响应按下状态。
    
    
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:image]];
    imageView.wrapContentHeight = YES;   //这个属性重点注意!! 对于UIImageView来说,如果我们设置了这个属性为YES的话,表示视图的高度会根据视图的宽度进行等比例的缩放来确定,从而防止图片显示时出现变形的情况。
    [colLayout addSubview:imageView];
    
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = title;
    titleLabel.font = [UIFont systemFontOfSize:13];
    titleLabel.textAlignment = NSTextAlignmentCenter;
    titleLabel.adjustsFontSizeToFitWidth = YES;
    titleLabel.myBottom = 2;
    [titleLabel sizeToFit];
    [colLayout addSubview:titleLabel];
    
    
    
    return colLayout;
}


可以看出我们通过一个水平表格就可以很轻松的实现瀑布流的效果。


四、总结

    好了,表格布局的内容就介绍到这里了,表格布局的内部实现其实就是一个线性布局套线性布局的封装,但是他简化了我们插入视图的方法,从而很容易的布局出各种风格的布局,我们可以从上往下依次布局,也可以从左往右依次布局。如果您觉得这篇文章能够帮助到您,或者能成为您界面布局的解决方案,那么请到我的github:


https://github.com/youngsoft/MyLinearLayout 中下载这套界面解决框架库,如果您觉得好用就记得给我点赞哦,如果有什么不明确的可以加我QQ:156355113联系我。

















阅读更多
版权声明:本文为博主原创文章,转载时请说明出处和作者信息 https://blog.csdn.net/yangtiang/article/details/48011431
个人分类: 前端开发
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭