天天看點

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種情況。

架構布局

左上 中上 右上
左中 居中 右中
左下 中下 右下

架構布局中的子視圖隻跟容器視圖之間産生關系,子視圖之間沒有任何關聯關系。

一、表格布局的介紹

    在一些實際的應用界面中,我們希望我們的子視圖以表格的形式展示出來,這些表格展示可以是正規的幾行幾列并且固定高寬的形式,也可能是每一行的列數都不同,也可能是每行的高度不一樣,也可能是一行内的各列的寬度也不一樣,

               水準表格布局

IOS不用AutoLayout也能實作自動布局的類(4)----MyTableLayout橫空出世

                                                                                                                                                 垂直表格布局

IOS不用AutoLayout也能實作自動布局的類(4)----MyTableLayout橫空出世

要實作上面的兩種界面風格,我們可以借助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;

上面就是我們對表格布局的所有函數的介紹,使用起來也很簡單,步驟就是先添加行,然後在每行中依次一個個的添加列視圖,也就是單元格視圖。

我們将分别建立兩個風格的表格進行例子的說明。

二、垂直表格

   所謂垂直表格就是行是從上往下,一行一行布局起來的,所有行内的單元格視圖都是從左往右一列一列布局起來的,建立垂直表格是預設的表格。先看界面布局效果。

IOS不用AutoLayout也能實作自動布局的類(4)----MyTableLayout橫空出世

我們分别建立了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中進行從上到下的滾動以便展示所有内容。先看界面布局效果:

IOS不用AutoLayout也能實作自動布局的類(4)----MyTableLayout橫空出世

這個例子中我們建立了一個三行平均寬度的水準表格,然後每添加一個列前都先算出哪行的高度最低,然後找到最低高度的行再插入進去,這樣就形成了一個瀑布流的效果。

-(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聯系我。