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

      对于IOS开发者来说,在自动布局出现前只能通过计算和设置frame的值来处理,这样设置位置时就会出现很多硬编码,同时在屏幕旋转和不同屏幕之间适配时需要编码重新调整位置和尺寸,我们也可以重载视图的layoutSubviews的函数来写代码重新布局。自动布局出现后确实在一定程度上解决了位置和尺寸硬编码的问题,但是通过代码来写自动布局非常的复杂和麻烦,而且代码量会增加很多。在自动布局领域android系统通过提供FrameLayout, LinearLayout, RelativeLayout, AbsoluteLayout等几个类来分别处理各种不同的布局需求,通过wrap_content,match_parent来自动计算尺寸。

     android系统的FrameLayout类用于进行上下左右居中填充方式的布局,而LinearLayout则是用于进行水平和垂直方向的流式布局,AbsoluteLayout则是硬编码方式的绝对布局。在我前面的2篇文章中分别介绍了MyFrameLayout, MyLinearLayout两种方式的布局,而这章我将继续介绍相对布局MyRelativeLayout.

       所谓相对布局就是指某个视图的位置和尺寸不是固定写死的而是依赖于其他关联的视图,比如一个视图在另外一个视图的左边,或者在另外一个视图的右下方,或者一个视图的宽度和另外一个视图宽度是相等的,或者视图是在父视图的顶部偏移一定的量,或者某一组视图的宽度要平分父视图等等功能。因此我们分别为子视图定义了如下的扩展属性:


@interface UIView(MyLayoutExt)


//位置

@property(nonatomic,readonlyMyLayoutPos *leftPos;

@property(nonatomic,readonlyMyLayoutPos *topPos;

@property(nonatomic,readonlyMyLayoutPos *rightPos;

@property(nonatomic,readonlyMyLayoutPos *bottomPos;

@property(nonatomic,readonlyMyLayoutPos *centerXPos;

@property(nonatomic,readonlyMyLayoutPos *centerYPos;



//尺寸

@property(nonatomic,readonlyMyLayoutSize *widthSize;

@property(nonatomic,readonlyMyLayoutSize *heightSize;



@end


分别来定义视图的左,上,右,下,水平中心,垂直中心,宽度,高度8个方位和尺寸的相对依赖对象,其中MyLayoutPos用来定义相对的依赖位置类。它的定义如下:

@interface MyLayoutPos :NSObject<NSCopying>


//设置偏移量

-(MyLayoutPos* (^)(CGFloat val))offset;


//最小的偏移量。如果设置了则最小不能低于这个值。

-(MyLayoutPos* (^)(CGFloat val))min;


//最大偏移量。如果设置了则最大不能超过这个值

-(MyLayoutPos* (^)(CGFloat val))max;



/*

 val的取值可以是NSNumber,MyLayoutPos,NSArray类型的对象。

 **如果是NSNumber类型的值表示在这个方向相对父视图或者兄弟视图(线性布局)的偏移值,比如:

 v.leftPos.equalTo(@10) 表示视图v左边偏移父视图或者兄弟视图10个点的位置。

 v.centerXPos.equalTo(@10) 表示视图v的水平中心点在父视图的水平中心点并偏移10个点的位置

 v.leftPos.equalTo(@0.1) 如果值被设置为大于0小于1则只在框架布局和线性布局里面有效表示左边距的值占用父视图宽度的10%

 **如果是MyLayoutPos类型的值则表示这个方向的值是相对于另外一个视图的边界值,比如:

 v1.leftPos.equal(v2.rightPos) 表示视图v1的左边边界值等于v2的右边边界值

 **如果是NSArray类型的值则只能用在相对布局的centerXPos,centerYPos中,数组里面里面也必须是centerXPos,表示指定的视图数组在父视图中居中,比如: A.centerXPos.equalTo(@[B.centerXPos.offset(20)].offset(20)  表示AB在父视图中居中往下偏移20BA的右边,间隔20

 */

-(MyLayoutPos* (^)(id val))equalTo;



//通过如下属性获取上面的设置结果。

@property(nonatomic,assign,readonly)CGFloat offsetVal;

@property(nonatomic,assign,readonly)CGFloat minVal;

@property(nonatomic,assign,readonly)CGFloat maxVal;

@property(nonatomic,strong,readonly)id posVal;



@end



这个类中的offset用来指定某个位置的偏移值,而equalTo则用来指定依赖的某个位置和值,比如:

1.A视图的左边等于B视图的右边并偏移30个点: A.leftPos.equalTo(B.rightPos).offset(30)

2.A视图的顶部和父视图的顶部相等:A.topPos.equalTo(A.superView.topPos)

3.A视图的中间在父视图的中间:   A.centerXPos.equalTo(A.superView.centerXPos); A.centerYPos.equalTo(A.superView.centerYPos)

4.A视图的左边偏移20: A.topPos.equalTo(@0).offset(20)
5.A,B,C三个视图要整体在布局视图中水平居中:A.centerXPos.equalTo(@[B.centerXPos,C.centerXPos])


而对于相对尺寸则定义了MyLayoutSize类来定义宽度和高度,这个类的定义如下:

@interface MyLayoutSize :NSObject<NSCopying>


//

-(MyLayoutSize* (^)(CGFloat val))multiply;


//,用这个和equalTo的数组功能可以实现均分子视图宽度以及间隔的设定。

-(MyLayoutSize* (^)(CGFloat val))add;



//最小的尺寸。如果设置了则最小不能低于这个值。

-(MyLayoutSize* (^)(CGFloat val))min;


//最大的尺寸。如果设置了则最大不能超过这个值

-(MyLayoutSize* (^)(CGFloat val))max;




/*NSNumber, MyLayoutSize以及MyLayoutSize数组,

 ***NSNumber值表示指定具体的宽度或者高度

 ***MyLayoutSize值表示宽度和高度与设置的对象有依赖关系

 ***MyLayoutSize数组的概念就是所有数组里面的子视图的尺寸平分父视图的尺寸。只有相对布局里面的子视图才支持这种设置。

 */

-(MyLayoutSize* (^)(id val))equalTo;



//上面方法设置的属性的获取。

@property(nonatomic,assign,readonly)CGFloat addVal;

@property(nonatomic,assign,readonly)CGFloat minVal;

@property(nonatomic,assign,readonly)CGFloat maxVal;

@property(nonatomic,assign,readonly)CGFloat mutilVal;

@property(nonatomic,strong,readonly)id dimeVal;




@end



尺寸定义中equalTo用来指定尺寸的值,可以是NSNumber型指定绝对的尺寸,MyLayoutSize型指定相对的尺寸,NSArray[MyLayoutSize]来指定按比例分配父视图的尺寸,mutiply用来指定在equalTo上指定的值的倍数比例,add指定在equalTo上指定的值的增量值(add方法更多是用来指定间距)。

1.A视图的宽度等于B视图的宽度,A视图的高度等于B视图的高度的一半:  A.widthSize.equalTo(B.widthSize); A.heightSize.equalTo(B.heightSize).multiply(0.5)

2.A视图的高度等于B视图的高度,并增加20:   A.heightSize.equalTo(B.heightSize).add(20)

3.A,B,C三个视图平分父视图的宽度:  A.widthSize.equalTo(@[B.widthSize, C.widthSize])

4.A视图固定宽度为20, B,C视图按4:6平分剩余的宽度:   A.widthSize.equal(@20)  B.widthSize.equalTo(@[A.widthSize, C.widthSize.multiply(0.6)]).multiply(0.4)


好了介绍了上述扩展的子视图的扩展属性后,我们需要的只是建立一个MyRelativeLayout布局视图,然后设置好子视图之间的相对依赖,然后添加进去就OK了。


一、相对布局的子视图的依赖



上述的布局就是用相对布局实现,代码很简单,请参考如下:

-(void)loadView
{
    
    MyRelativeLayout *rootLayout = [MyRelativeLayout new];
    rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10);
    rootLayout.backgroundColor = [UIColor grayColor];
    self.view = rootLayout;

    UILabel *todayLabel = [UILabel new];
    todayLabel.text = @"Today";
    
    [todayLabel sizeToFit];
    todayLabel.centerXPos.equalTo(@0);
    todayLabel.topPos.equalTo(@20);
    [rootLayout addSubview:todayLabel];
    
    
    
    UIView *greenCircle = [UIView new];
    greenCircle.backgroundColor = [UIColor greenColor];
    greenCircle.layer.cornerRadius = 100;
    [rootLayout addSubview:greenCircle];

    
    
    greenCircle.widthSize.equalTo(rootLayout.widthSize).multiply(3/5.0).max(200);
    greenCircle.heightSize.equalTo(greenCircle.widthSize);
    greenCircle.leftPos.equalTo(@10);
    greenCircle.topPos.equalTo(@90);
    
    
    UILabel *walkLabel = [UILabel new];
    walkLabel.text = @"Walk";
    walkLabel.textColor = [UIColor greenColor];
    
    [walkLabel sizeToFit];
    walkLabel.centerXPos.equalTo(greenCircle.centerXPos);
    walkLabel.bottomPos.equalTo(greenCircle.topPos).offset(5);
    [rootLayout addSubview:walkLabel];
    
    
    UILabel *walkSteps = [UILabel new];
    walkSteps.text = @"9,362";
    walkSteps.textColor = [UIColor whiteColor];
    walkSteps.font = [UIFont systemFontOfSize:30];

    [walkSteps sizeToFit];
    walkSteps.centerXPos.equalTo(greenCircle.centerXPos);
    walkSteps.centerYPos.equalTo(greenCircle.centerYPos);
    [rootLayout addSubview:walkSteps];
    
    UILabel *steps = [UILabel new];
    steps.text = @"steps";
    steps.textColor = [UIColor whiteColor];
    steps.font = [UIFont systemFontOfSize:15];
    
    [steps sizeToFit];
    steps.centerXPos.equalTo(walkSteps.centerXPos);
    steps.topPos.equalTo(walkSteps.bottomPos);
    [rootLayout addSubview:steps];
    
    
    
    UIView *blueCircle = [UIView new];
    blueCircle.backgroundColor = [UIColor blueColor];
    blueCircle.layer.cornerRadius = 60;
    
    blueCircle.topPos.equalTo(greenCircle.topPos).offset(-10);
    blueCircle.rightPos.equalTo(rootLayout.rightPos).offset(10);
    blueCircle.widthSize.equalTo(@120);
    blueCircle.heightSize.equalTo(blueCircle.widthSize);
    [rootLayout addSubview:blueCircle];
    
    
    UILabel *cycleLabel = [UILabel new];
    cycleLabel.text = @"Cycle";
    cycleLabel.textColor = [UIColor blueColor];
    
    [cycleLabel sizeToFit];
    cycleLabel.centerXPos.equalTo(blueCircle.centerXPos);
    cycleLabel.bottomPos.equalTo(blueCircle.topPos).offset(5);
    [rootLayout addSubview:cycleLabel];
    
    
    UILabel *cycleMin = [UILabel new];
    cycleMin.text = @"39 Min";
    cycleMin.textColor = [UIColor whiteColor];
    cycleMin.font = [UIFont systemFontOfSize:18];
    
    [cycleMin sizeToFit];
    cycleMin.leftPos.equalTo(blueCircle.leftPos);
    cycleMin.centerYPos.equalTo(blueCircle.centerYPos);
    [rootLayout addSubview:cycleMin];
    
 
    UIView *lineView1 = [UIView new];
    lineView1.backgroundColor = [UIColor redColor];
    lineView1.leftPos.equalTo(@0);
    lineView1.rightPos.equalTo(@0);
    lineView1.heightSize.equalTo(@2);
    lineView1.centerYPos.equalTo(@0);
    [rootLayout addSubview:lineView1];
    
    UIView *lineView2 = [UIView new];
    lineView2.backgroundColor = [UIColor greenColor];
    lineView2.widthSize.equalTo(rootLayout.widthSize).add(-20);
    lineView2.heightSize.equalTo(@2);
    lineView2.topPos.equalTo(lineView1.bottomPos).offset(2);
    lineView2.centerXPos.equalTo(rootLayout.centerXPos);
    [rootLayout addSubview:lineView2];
    
    
    UIView *bottomHalfCircleView = [UIView new];
    bottomHalfCircleView.backgroundColor = [UIColor whiteColor];
    bottomHalfCircleView.layer.cornerRadius = 25;
    
    bottomHalfCircleView.widthSize.equalTo(@50);
    bottomHalfCircleView.heightSize.equalTo(bottomHalfCircleView.widthSize);
    bottomHalfCircleView.centerYPos.equalTo(rootLayout.bottomPos).offset(10); //因为rootLayout设置了bottomPadding为10,所以这里要偏移10,否则不需要设置偏移。
    bottomHalfCircleView.leftPos.equalTo(rootLayout.leftPos).offset(50);
    [rootLayout addSubview:bottomHalfCircleView];

    
    UIView *lineView3 = [UIView new];
    lineView3.backgroundColor = [UIColor greenColor];
    
    lineView3.widthSize.equalTo(@5);
    lineView3.heightSize.equalTo(@50);
    lineView3.bottomPos.equalTo(bottomHalfCircleView.topPos);
    lineView3.centerXPos.equalTo(bottomHalfCircleView.centerXPos);
    [rootLayout addSubview:lineView3];
    
    
    UILabel *walkLabel2 = [UILabel new];
    walkLabel2.text = @"Walk";
    walkLabel2.textColor = [UIColor greenColor];
    
    [walkLabel2 sizeToFit];
    walkLabel2.leftPos.equalTo(lineView3.rightPos).offset(15);
    walkLabel2.centerYPos.equalTo(lineView3.centerYPos);
    [rootLayout addSubview:walkLabel2];
    
    
    UILabel *walkLabel3 = [UILabel new];
    walkLabel3.text = @"18 Min";
    walkLabel3.textColor = [UIColor whiteColor];
    
    [walkLabel3 sizeToFit];
    walkLabel3.leftPos.equalTo(walkLabel2.rightPos).offset(5);
    walkLabel3.centerYPos.equalTo(walkLabel2.centerYPos);
    [rootLayout addSubview:walkLabel3];

    UILabel *timeLabel1 = [UILabel new];
    timeLabel1.text = @"9:12";
    timeLabel1.textColor = [UIColor lightGrayColor];
    
    [timeLabel1 sizeToFit];
    timeLabel1.rightPos.equalTo(lineView3.leftPos).offset(25);
    timeLabel1.centerYPos.equalTo(lineView3.topPos);
    [rootLayout addSubview:timeLabel1];
    
    
    UILabel *timeLabel2 = [UILabel new];
    timeLabel2.text = @"9:30";
    timeLabel2.textColor = [UIColor lightGrayColor];
    
    [timeLabel2 sizeToFit];
    timeLabel2.rightPos.equalTo(timeLabel1.rightPos);
    timeLabel2.centerYPos.equalTo(lineView3.bottomPos);
    [rootLayout addSubview:timeLabel2];
    
    
    
    UIView *lineView4 = [UIView new];
    lineView4.backgroundColor = [UIColor whiteColor];
    lineView4.layer.cornerRadius = 25;
    
    lineView4.widthSize.equalTo(bottomHalfCircleView.widthSize);
    lineView4.heightSize.equalTo(lineView4.widthSize).add(30);
    lineView4.bottomPos.equalTo(lineView3.topPos);
    lineView4.centerXPos.equalTo(lineView3.centerXPos);
    [rootLayout addSubview:lineView4];
    
    UIImageView *imageView4 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"user"]];
    [imageView4 sizeToFit];
    
    imageView4.widthSize.equalTo(lineView4.widthSize).multiply(1/3.0);
    imageView4.heightSize.equalTo(imageView4.widthSize);
    imageView4.centerXPos.equalTo(lineView4.centerXPos);
    imageView4.centerYPos.equalTo(lineView4.centerYPos);
    [rootLayout addSubview:imageView4];
    
    
    UILabel *homeLabel = [UILabel new];
    homeLabel.text = @"Home";
    homeLabel.textColor = [UIColor whiteColor];
    
    [homeLabel sizeToFit];
    homeLabel.leftPos.equalTo(lineView4.rightPos).offset(10);
    homeLabel.centerYPos.equalTo(lineView4.centerYPos);
    [rootLayout addSubview:homeLabel];
    
    
    UIImageView *bottomRightImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head1"]];
    bottomRightImageView.backgroundColor = [UIColor whiteColor];
    
    [bottomRightImageView sizeToFit];
    bottomRightImageView.rightPos.equalTo(rootLayout.rightPos);
    bottomRightImageView.bottomPos.equalTo(rootLayout.bottomPos);
    [rootLayout addSubview:bottomRightImageView];


    
    return;
    
    

  
   
}




二、相对布局的子视图的平均分配
 
 有时候我们在明确知道了布局视图的尺寸后,我们希望布局视图里面的某些子视图的尺寸按照一定的规则依赖父布局来进行分配,比如希望某几个子视图的宽度来均分布局父视图的宽度,或者某几个子视图的宽度是按一定的比例来分配布局父视图的宽度等等。比如下面的界面效果图:




上面的视图中,第一排的3个子视图平分父视图的宽度。第二排子视图中第一个视图宽度固定,剩余的两个平分剩余的宽度。第三排的子视图按0.2 0.3 0.5的比例来平分父视图,而下面的两个则是均分布局父视图的高度。代码如下:

-(void)loadView
{
    
    MyRelativeLayout *rootLayout = [MyRelativeLayout new];
    rootLayout.padding = UIEdgeInsetsMake(0, 0, 0, 10);
    rootLayout.backgroundColor = [UIColor grayColor];
    self.view = rootLayout;
    
    UISwitch *hiddenSwitch = [UISwitch new];
    [hiddenSwitch addTarget:self action:@selector(handleSwitch:) forControlEvents:UIControlEventValueChanged];
    hiddenSwitch.rightPos.equalTo(@0);
    hiddenSwitch.topPos.equalTo(@10);
    [rootLayout addSubview:hiddenSwitch];
    
    UILabel *hiddenSwitchLabel = [UILabel new];
    hiddenSwitchLabel.text = @"隐藏时伸缩其他子视图:";
    [hiddenSwitchLabel sizeToFit];
    hiddenSwitchLabel.leftPos.equalTo(@10);
    hiddenSwitchLabel.centerYPos.equalTo(hiddenSwitch.centerYPos);
    [rootLayout addSubview:hiddenSwitchLabel];
    
    

    /**水平平分3个子视图**/
    UIButton *v1 = [UIButton new];
    v1.backgroundColor = [UIColor redColor];
    [v1 setTitle:@"平均1/3宽度\n切换上面开关试试" forState:UIControlStateNormal];
    v1.titleLabel.numberOfLines = 2;
    v1.titleLabel.adjustsFontSizeToFitWidth = YES;
    v1.titleLabel.textAlignment = NSTextAlignmentCenter;

    
    v1.heightSize.equalTo(@40);
    v1.topPos.equalTo(@60);
    v1.leftPos.equalTo(@10);
    [rootLayout addSubview:v1];
    
    
    UIButton *v2 = [UIButton new];
    v2.backgroundColor = [UIColor redColor];
    [v2 setTitle:@"平均1/3宽度\n点击我隐藏" forState:UIControlStateNormal];
    [v2 addTarget:self action:@selector(handleHidden:) forControlEvents:UIControlEventTouchUpInside];
    v2.titleLabel.numberOfLines = 2;
    v2.titleLabel.adjustsFontSizeToFitWidth = YES;
    v2.titleLabel.textAlignment = NSTextAlignmentCenter;
    
    v2.heightSize.equalTo(v1.heightSize);
    v2.topPos.equalTo(v1.topPos);
    v2.leftPos.equalTo(v1.rightPos).offset(10);
    [rootLayout addSubview:v2];
    

    UIButton *v3 = [UIButton new];
    v3.backgroundColor = [UIColor redColor];
    [v3 setTitle:@"平均1/3宽度\n点击我恢复" forState:UIControlStateNormal];
    [v3 addTarget:self action:@selector(handleShow:) forControlEvents:UIControlEventTouchUpInside];
    v3.titleLabel.numberOfLines = 2;
    v3.titleLabel.adjustsFontSizeToFitWidth = YES;
    v3.titleLabel.textAlignment = NSTextAlignmentCenter;
    
    v3.heightSize.equalTo(v1.heightSize);
    v3.topPos.equalTo(v1.topPos);
    v3.leftPos.equalTo(v2.rightPos).offset(10);
    [rootLayout addSubview:v3];
    

    //v1,v2,v3平分父视图的宽度。在平分前减去了30用作间距。这里的宽度通过设置等于数组来完成均分。
    v1.widthSize.equalTo(@[v2.widthSize.add(-10), v3.widthSize.add(-10)]).add(-10);
    

    
    /**某个视图宽度固定其他平分**/
    UILabel *v4 = [UILabel new];
    v4.backgroundColor = [UIColor greenColor];
    v4.text = @"固定宽度为260";
    v4.adjustsFontSizeToFitWidth = YES;
    v4.textAlignment = NSTextAlignmentCenter;
    
    v4.topPos.equalTo(v1.bottomPos).offset(30);
    v4.leftPos.equalTo(@10);
    v4.heightSize.equalTo(@40);
    v4.widthSize.equalTo(@160); //第一个视图宽度固定
    [rootLayout addSubview:v4];
    
    
    UILabel *v5 = [UILabel new];
    v5.backgroundColor = [UIColor greenColor];
    v5.text = @"剩余1/2宽度";
    v5.adjustsFontSizeToFitWidth = YES;
    v5.textAlignment = NSTextAlignmentCenter;

    v5.topPos.equalTo(v4.topPos);
    v5.leftPos.equalTo(v4.rightPos).offset(10);
    v5.heightSize.equalTo(v4.heightSize);
    [rootLayout addSubview:v5];
    

    UILabel *v6 = [UILabel new];
    v6.backgroundColor = [UIColor greenColor];
    v6.text = @"剩余1/2宽度";
    v6.adjustsFontSizeToFitWidth = YES;
    v6.textAlignment = NSTextAlignmentCenter;

    v6.topPos.equalTo(v4.topPos);
    v6.leftPos.equalTo(v5.rightPos).offset(10);
    v6.heightSize.equalTo(v4.heightSize);
    [rootLayout addSubview:v6];
    
    
    //v1,v2,v3平分父视图的宽度。在平分前减去了30用作间距
    v5.widthSize.equalTo(@[v4.widthSize.add(-10), v6.widthSize.add(-10)]).add(-10);
    
    
    
    
    /**子视图按比例平分**/
    UILabel *v7 = [UILabel new];
    v7.backgroundColor = [UIColor blueColor];
    v7.text = @"2/10比例宽度";
    v7.adjustsFontSizeToFitWidth = YES;
    v7.textAlignment = NSTextAlignmentCenter;
    
    v7.topPos.equalTo(v4.bottomPos).offset(30);
    v7.leftPos.equalTo(@10);
    v7.heightSize.equalTo(@40);
    [rootLayout addSubview:v7];
    
    
    UILabel *v8 = [UILabel new];
    v8.backgroundColor = [UIColor blueColor];
    v8.text = @"3/10比例宽度";
    v8.adjustsFontSizeToFitWidth = YES;
    v8.textAlignment = NSTextAlignmentCenter;

    v8.topPos.equalTo(v7.topPos);
    v8.leftPos.equalTo(v7.rightPos).offset(10);
    v8.heightSize.equalTo(v7.heightSize);
    [rootLayout addSubview:v8];
    
    
    UILabel *v9 = [UILabel new];
    v9.backgroundColor = [UIColor blueColor];
    v9.text = @"5/10比例宽度";
    v9.adjustsFontSizeToFitWidth = YES;
    v9.textAlignment = NSTextAlignmentCenter;

    v9.topPos.equalTo(v7.topPos);
    v9.leftPos.equalTo(v8.rightPos).offset(10);
    v9.heightSize.equalTo(v7.heightSize);
    [rootLayout addSubview:v9];
    
    v7.widthSize.equalTo(@[v8.widthSize.multiply(0.3).add(-10),v9.widthSize.multiply(0.5).add(-10)]).multiply(0.2).add(-10);
    
    
    //垂直布局看看高度均分。
    MyRelativeLayout * bottomLayout = [MyRelativeLayout new];
    bottomLayout.backgroundColor = [UIColor lightGrayColor];
    
    bottomLayout.leftPos.equalTo(@10);
    bottomLayout.rightPos.equalTo(@0);
    bottomLayout.topPos.equalTo(v7.bottomPos).offset(30);
    bottomLayout.bottomPos.equalTo(@10);
    [rootLayout addSubview:bottomLayout];
    
    
    UIView *v10 = [UIView new];
    v10.backgroundColor = [UIColor redColor];
    
    v10.widthSize.equalTo(@40);
    v10.rightPos.equalTo(bottomLayout.centerXPos).offset(50);
    v10.topPos.equalTo(@10);
    [bottomLayout addSubview:v10];
    
    UIView *v11 = [UIView new];
    v11.backgroundColor = [UIColor redColor];
    v11.widthSize.equalTo(v10.widthSize);
    v11.rightPos.equalTo(v10.rightPos);
    v11.topPos.equalTo(v10.bottomPos).offset(10);
    [bottomLayout addSubview:v11];
    
    v10.heightSize.equalTo(@[v11.heightSize.add(-20)]).add(-10);
    
    
    UIView *v12 = [UIView new];
    v12.backgroundColor = [UIColor greenColor];
    
    v12.widthSize.equalTo(@40);
    v12.leftPos.equalTo(bottomLayout.centerXPos).offset(50);
    v12.topPos.equalTo(@10);
    [bottomLayout addSubview:v12];
    
    UIView *v13 = [UIView new];
    v13.backgroundColor = [UIColor greenColor];
    v13.widthSize.equalTo(v12.widthSize);
    v13.leftPos.equalTo(v12.leftPos);
    v13.topPos.equalTo(v12.bottomPos).offset(10);
    [bottomLayout addSubview:v13];
    
    
    UIView *v14 = [UIView new];
    v14.backgroundColor = [UIColor greenColor];
    v14.widthSize.equalTo(v12.widthSize);
    v14.leftPos.equalTo(v12.leftPos);
    v14.topPos.equalTo(v13.bottomPos).offset(10);
    [bottomLayout addSubview:v14];
    
    //注意这里最后一个偏移-20,也能达到和底部边距的效果。
    v12.heightSize.equalTo(@[v13.heightSize.add(-10),v14.heightSize.add(-20)]).add(-10);
    
    
}


看代码我们发现,在分配视图时指定了视图之间的间距这需要借助offset的调用来指定间距,因为是均分视图我们又需要为视图的宽度留有间隔,因此我们需要借助add的方法来将计算出的宽度减去间距的值,而同时我们为布局视图的padding的值,我们设置了10的间距来控制最右边的间距为10。

  有的时候我们在均分子视图时,当某个子视图隐藏时其他的剩余的子视图的宽度会进行调整,比如某个子视图设置为隐藏后,右边的子视图向左边靠拢。而有的时候我们希望当某个子视图隐藏时,剩余的部分重新填充慢布局视图的某个方位的尺寸,因此我们可以为布局视图设置开关:

//均分宽度时当有隐藏子视图,是否参与宽度计算,这个属性只有在参与均分视图的子视图隐藏时才有效,默认是NO

@property(nonatomic,assign)BOOL flexOtherViewWidthWhenSubviewHidden;


//均分高度时当有隐藏子视图,是否参与高度计算,这个属性只有在参与均分视图的子视图隐藏时才有效,默认是NO

@property(nonatomic,assign)BOOL flexOtherViewHeightWhenSubviewHidden;



这两个布局视图的属性分别标明当某个子视图数组均分父视图时,而其中某个子视图隐藏时,是否其他视图会重新分配宽度和高度。

三、相对布局的高宽由子视图决定

我在线性布局的文章中有说明可以通过wrapContent来决定是否布局视图的非方向是否由子视图来决定。这时候我们就不需要手动的指定布局视图的高度和宽度,而是由布局视图里面的子视图来决定布局的尺寸,在android系统中我们可以设置wrapContent来设置布局视图的尺寸。同样我们在布局中也分别提供了两个属性:


@property(nonatomic,assign)BOOL wrapContentWidth;

@property(nonatomic,assign)BOOL wrapContentHeight;



从上面的定义可以看出,wrapContentWidth, wrapContentHeight则是指定布局视图的宽度和高度由子视图决定,对于线性布局来说如果是垂直方向的话wrapContentHeight是默认设置为YES的,而水平方向则wrapContentWidth设置为YES。而对于相对布局来说两者默认都设置为NO。

而当我们设置了布局视图属性wrapContentWidth = YES, wrapContentHeight = YES 时 那么布局视图的高度和宽度是怎么计算出来的呢。我们是通过计算出所有子视图的位置和尺寸的最大高度和宽度来得到布局视图的高度和宽度的。

同时通过wrapContentXXX的布局视图的属性我们可以动态的调整布局视图本身的高度和宽度,因此我们也很适合将布局视图放入到一个UIScrollView中去。


四、一组视图在布局视图中居中
 
     有时候我们希望布局中的某些子视图整体居中,一个解决的方法是我们为这些视图建立一个父视图,然后让这个父视图居中,但这个前提是我们需要新建立一个父视图来包围这批视图。采用相对布局的方法是不需要再新建一个附加的父视图的。我们先看界面。




界面中我们看到上面的2个视图整体是在父视图的水平中间的,而中间的2个视图整体是在父视图的垂直中间的,而下面6个则是在父视图的水平和垂直都是居中的,这个功能的代码实现很简单:

-(MyRelativeLayout*)createLayout1
{
    MyRelativeLayout *layout = [MyRelativeLayout new];
    
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = @"子视图整体水平居中";
    [titleLabel sizeToFit];
    [layout addSubview:titleLabel];
    
    
    UIView *v1 = [UIView new];
    v1.backgroundColor = [UIColor greenColor];
    v1.widthSize.equalTo(@100);
    v1.heightSize.equalTo(@50);
    v1.centerYPos.equalTo(@0);
    [layout addSubview:v1];
    
    
    UIView *v2 = [UIView new];
    v2.backgroundColor = [UIColor blueColor];
    v2.widthSize.equalTo(@50);
    v2.heightSize.equalTo(@50);
    v2.centerYPos.equalTo(@0);
    [layout addSubview:v2];
    
    //通过为centerXPos等于一个数组值,表示他们之间整体居中,还可以设置其他视图的偏移量。
    v1.centerXPos.equalTo(@[v2.centerXPos.offset(20)]);

    
    
    
    
    
    return layout;
}

-(MyRelativeLayout*)createLayout2
{
    MyRelativeLayout *layout = [MyRelativeLayout new];
    
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = @"子视图整体垂直居中";
    [titleLabel sizeToFit];
    [layout addSubview:titleLabel];
    
    
    UIView *v1 = [UIView new];
    v1.backgroundColor = [UIColor redColor];
    v1.widthSize.equalTo(@200);
    v1.heightSize.equalTo(@50);
    v1.centerXPos.equalTo(@0);
    [layout addSubview:v1];
    
    
    UIView *v2 = [UIView new];
    v2.backgroundColor = [UIColor blueColor];
    v2.widthSize.equalTo(@200);
    v2.heightSize.equalTo(@30);
    v2.centerXPos.equalTo(@0);
    [layout addSubview:v2];
    
    //通过为centerYPos等于一个数组值,表示他们之间整体居中,还可以设置其他视图的偏移量。
    v1.centerYPos.equalTo(@[v2.centerYPos.offset(10)]);

    return layout;
}


-(MyRelativeLayout*)createLayout3
{
    MyRelativeLayout *layout = [MyRelativeLayout new];
    
    UILabel *titleLabel = [UILabel new];
    titleLabel.text = @"子视图整体居中";
    [titleLabel sizeToFit];
    [layout addSubview:titleLabel];
    
    UILabel *lb1up = [UILabel new];
    lb1up.text = @"左上面";
    lb1up.backgroundColor = [UIColor redColor];
    lb1up.font = [UIFont systemFontOfSize:17];
    [lb1up sizeToFit];
    [layout addSubview:lb1up];
    
    UILabel *lb1down = [UILabel new];
    lb1down.text = @"我左在下面";
    lb1down.backgroundColor = [UIColor greenColor];
    [lb1down sizeToFit];
    [layout addSubview:lb1down];
    
    
    UILabel *lb2up = [UILabel new];
    lb2up.text = @"我在中间上面";
    lb2up.backgroundColor = [UIColor redColor];
    lb2up.font = [UIFont systemFontOfSize:12];
    [lb2up sizeToFit];
    [layout addSubview:lb2up];
    
    UILabel *lb2down = [UILabel new];
    lb2down.text = @"中";
    lb2down.backgroundColor = [UIColor greenColor];
    [lb2down sizeToFit];
    [layout addSubview:lb2down];
    
    
    UILabel *lb3up = [UILabel new];
    lb3up.text = @"右上";
    lb3up.backgroundColor = [UIColor redColor];
    [lb3up sizeToFit];
    [layout addSubview:lb3up];
    
    UILabel *lb3down = [UILabel new];
    lb3down.text = @"右边的下方";
    lb3down.backgroundColor = [UIColor greenColor];
    lb3down.font = [UIFont systemFontOfSize:16];
    [lb3down sizeToFit];
    [layout addSubview:lb3down];
    
    //左,中,右三组视图分别垂直居中显示,并且下面和上面间隔10
    lb1up.centerYPos.equalTo(@[lb1down.centerYPos.offset(10)]);
    lb2up.centerYPos.equalTo(@[lb2down.centerYPos.offset(10)]);
    lb3up.centerYPos.equalTo(@[lb3down.centerYPos.offset(10)]);
    
    //上面的三个视图水平居中显示并且间隔60
    lb1up.centerXPos.equalTo(@[lb2up.centerXPos.offset(60),lb3up.centerXPos.offset(60)]);
    
    //下面的三个视图的水平中心点和上面三个视图的水平中心点对齐
    lb1down.centerXPos.equalTo(lb1up.centerXPos);
    lb2down.centerXPos.equalTo(lb2up.centerXPos);
    lb3down.centerXPos.equalTo(lb3up.centerXPos);
    

    
    
    return layout;
}


-(void)loadView
{
    
    
    MyRelativeLayout *rootLayout = [MyRelativeLayout new];
    self.view = rootLayout;
    
    MyRelativeLayout *layout1 =  [self createLayout1]; //[MyRelativeLayout new];
    MyRelativeLayout *layout2 = [self createLayout2];
    MyRelativeLayout *layout3 = [self createLayout3];
   
    layout1.backgroundColor = [UIColor redColor];
    layout2.backgroundColor = [UIColor greenColor];
    layout3.backgroundColor = [UIColor blueColor];
    
    
    layout1.widthSize.equalTo(rootLayout.widthSize);
    layout2.widthSize.equalTo(rootLayout.widthSize);
    layout3.widthSize.equalTo(rootLayout.widthSize);
    layout1.heightSize.equalTo(@[layout2.heightSize, layout3.heightSize]);
    layout2.topPos.equalTo(layout1.bottomPos);
    layout3.topPos.equalTo(layout2.bottomPos);
    
    
    [rootLayout addSubview:layout1];
    [rootLayout addSubview:layout2];
    [rootLayout addSubview:layout3];

}



五、总结

 好了,相对视图的介绍就布局到这里了,到这里我分别为你介绍了框架布局,线性布局和相对布局方面的东西,通过这三个布局的使用我们完全可以摆脱对IOS的自动布局的使用,而用更加简单清晰的方法来布局您的界面,希望我的库能对您提供非常有利的帮助,如果您需要用我的库来编码,那么就请到:

https://github.com/youngsoft/MyLinearLayout 中下载代码和DEMO吧。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值