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(MyRelativeLayoutEx)
//位置
@property(nonatomic,readonly) MyRelativePos *leftPos;
@property(nonatomic,readonly) MyRelativePos *topPos;
@property(nonatomic,readonly) MyRelativePos *rightPos;
@property(nonatomic,readonly) MyRelativePos *bottomPos;
@property(nonatomic,readonly) MyRelativePos *centerXPos;
@property(nonatomic,readonly) MyRelativePos *centerYPos;
//尺寸
@property(nonatomic,readonly) MyRelativeDime *widthDime;
@property(nonatomic,readonly) MyRelativeDime *heightDime;
@end
@interface MyRelativePos :NSObject
//偏移
-(MyRelativePos* (^)(CGFloat val))offset;
//NSNumber, MyRelativePos对象,如果是centerXPos或者centerYPos则可以传NSArray,数组里面里面也必须是centerXPos,表示指定的视图数组
//在父视图中居中,比如: A.centerXPos.equalTo(@[B.centerXPos.offset(20)].offset(20)
//表示A和B在父视图中居中往下偏移20,B在A的右边,间隔20。
-(MyRelativePos* (^)(id val))equalTo;
@end
@interface MyRelativeDime :NSObject
//乘
-(MyRelativeDime* (^)(CGFloat val))multiply;
//加,用这个和equalTo的数组功能可以实现均分子视图宽度以及间隔的设定。
-(MyRelativeDime* (^)(CGFloat val))add;
//NSNumber, MyRelativeDime以及MyRelativeDime数组,数组的概念就是所有数组里面的子视图的尺寸平分父视图的尺寸。
-(MyRelativeDime* (^)(id val))equalTo;
@end
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.padding = UIEdgeInsetsMake(10, 10, 10, 10);
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
- UILabel *lb1 = [UILabel new];
- [rl addSubview:lb1];
- lb1.text = @"你好";
- [lb1 sizeToFit];
- lb1.backgroundColor = [UIColor blueColor];
- lb1.leftPos.equalTo(rl.leftPos); //和父视图左边一致
- lb1.topPos.equalTo(rl.topPos).offset(10); //和父视图顶部一致并偏移10
- lb1.widthDime.equalTo(@60); //固定宽度
- UILabel *lb2 = [UILabel new];
- [rl addSubview:lb2];
- lb2.text = @"我好 hello";
- lb2.backgroundColor = [UIColor redColor];
- lb2.leftPos.equalTo(lb1.rightPos);
- lb2.topPos.equalTo(lb1.bottomPos);
- lb2.widthDime.equalTo(lb1.widthDime).add(30); //宽度是lb1的宽度加30
- lb2.heightDime.equalTo(lb1.heightDime).multiply(2).add(-10); //高度是lb1高度的2倍再-10
- UILabel *lb3 = [UILabel new];
- lb3.text = @"中间";
- lb3.backgroundColor = [UIColor greenColor];
- [rl addSubview:lb3];
- lb3.centerXPos.equalTo(rl.centerXPos);
- lb3.centerYPos.equalTo(rl.centerYPos);
- lb3.widthDime.equalTo(rl.widthDime).multiply(0.2);
- lb3.heightDime.equalTo(rl.heightDime).multiply(0.1);
- UILabel *lb4 = [UILabel new];
- lb4.text = @"他好";
- [lb4 sizeToFit];
- lb4.backgroundColor = [UIColor orangeColor];
- [rl addSubview:lb4];
- //宽度和高度由左右决定
- lb4.leftPos.equalTo(rl.leftPos);
- lb4.rightPos.equalTo(rl.rightPos);
- lb4.topPos.equalTo(@100);
- }
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.padding = UIEdgeInsetsMake(0, 0, 0, 10);
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
- /**水平平分3个子视图**/
- UIView *v1 = [UIView new];
- v1.backgroundColor = [UIColor redColor];
- v1.heightDime.equalTo(@40);
- [rl addSubview:v1];
- UIView *v2 = [UIView new];
- v2.backgroundColor = [UIColor redColor];
- v2.heightDime.equalTo(@40);
- [rl addSubview:v2];
- UIView *v3 = [UIView new];
- v3.backgroundColor = [UIColor redColor];
- v3.heightDime.equalTo(@40);
- [rl addSubview:v3];
- //v1,v2,v3平分父视图的宽度。在平分前减去了30用作间距
- v1.widthDime.equalTo(@[v2.widthDime.add(-10), v3.widthDime.add(-10)]).add(-10);
- v1.leftPos.offset(10);
- v2.leftPos.equalTo(v1.rightPos).offset(10);
- v3.leftPos.equalTo(v2.rightPos).offset(10);
- /**某个视图固定其他平分**/
- UIView *v4 = [UIView new];
- v4.backgroundColor = [UIColor greenColor];
- v4.topPos.equalTo(v1.bottomPos).offset(80);
- v4.heightDime.equalTo(@40);
- v4.widthDime.equalTo(@260); //第一个视图宽度固定
- [rl addSubview:v4];
- UIView *v5 = [UIView new];
- v5.backgroundColor = [UIColor greenColor];
- v5.topPos.equalTo(v4.topPos);
- v5.heightDime.equalTo(@40);
- [rl addSubview:v5];
- UIView *v6 = [UIView new];
- v6.backgroundColor = [UIColor greenColor];
- v6.topPos.equalTo(v4.topPos);
- v6.heightDime.equalTo(@40);
- [rl addSubview:v6];
- //v1,v2,v3平分父视图的宽度。在平分前减去了30用作间距
- v5.widthDime.equalTo(@[v4.widthDime.add(-10), v6.widthDime.add(-10)]).add(-10);
- v4.leftPos.offset(10);
- v5.leftPos.equalTo(v4.rightPos).offset(10);
- v6.leftPos.equalTo(v5.rightPos).offset(10);
- /**子视图按比例平分**/
- UIView *v7 = [UIView new];
- v7.backgroundColor = [UIColor blueColor];
- v7.topPos.equalTo(v4.bottomPos).offset(80);
- v7.heightDime.equalTo(@40);
- [rl addSubview:v7];
- UIView *v8 = [UIView new];
- v8.backgroundColor = [UIColor blueColor];
- v8.topPos.equalTo(v7.topPos);
- v8.heightDime.equalTo(@40);
- [rl addSubview:v8];
- UIView *v9 = [UIView new];
- v9.backgroundColor = [UIColor blueColor];
- v9.topPos.equalTo(v7.topPos);
- v9.heightDime.equalTo(@40);
- [rl addSubview:v9];
- v7.widthDime.equalTo(@[v8.widthDime.multiply(0.3).add(-10),v9.widthDime.multiply(0.5).add(-10)]).multiply(0.2).add(-10);
- v7.leftPos.offset(10);
- v8.leftPos.equalTo(v7.rightPos).offset(10);
- v9.leftPos.equalTo(v8.rightPos).offset(10);
- //请分别设置每个视图.hidden = YES 并且设置布局的@property(nonatomic, assign) BOOL flexOtherViewWidthWhenSubviewHidden为YES和NO的效果
- }
看代码我们发现,在分配视图时指定了视图之间的间距这需要借助offset的调用来指定间距,因为是均分视图我们又需要为视图的宽度留有间隔,因此我们需要借助add的方法来将计算出的宽度减去间距的值,而同时我们为布局视图的padding的值,我们设置了10的间距来控制最右边的间距为10。
//均分宽度时当有隐藏子视图,是否参与宽度计算,这个属性只有在参与均分视图的子视图隐藏时才有效,默认是NO
@property(nonatomic,assign)BOOL flexOtherViewWidthWhenSubviewHidden;
//均分高度时当有隐藏子视图,是否参与高度计算,这个属性只有在参与均分视图的子视图隐藏时才有效,默认是NO
@property(nonatomic,assign)BOOL flexOtherViewHeightWhenSubviewHidden;
@property(nonatomic,assign)BOOL wrapContentWidth;
@property(nonatomic,assign)BOOL wrapContentHeight;
- -(void)loadView
- {
- [super loadView];
- MyRelativeLayout *rl = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(10, 10, 0, 0)];
- rl.padding = UIEdgeInsetsMake(10, 10, 10, 10);
- [self.view addSubview:rl];
- rl.wrapContentWidth = YES;
- rl.wrapContentHeight = YES; //设置宽度和高度由所有子视图包裹
- rl.backgroundColor = [UIColor grayColor];
- UILabel *lb1 = [UILabel new];
- lb1.leftPos.equalTo(rl.leftPos).offset(20);
- lb1.text = @"aaaa";
- lb1.backgroundColor = [UIColor redColor];
- [lb1 sizeToFit];
- lb1.rightPos.offset(20);
- [rl addSubview:lb1];
- UILabel *lb3 = [UILabel new];
- lb3.rightPos.equalTo(rl.rightPos).offset(5); //虽然这时候父视图的宽度为0,但还是可以设置离父视图的距离
- lb3.topPos.equalTo(rl.topPos).offset(30);
- lb3.bottomPos.offset(10);
- lb3.text = @"ccc";
- lb3.backgroundColor = [UIColor redColor];
- [lb3 sizeToFit];
- [rl addSubview:lb3];
- UILabel *lb2 = [UILabel new];
- lb2.text = @"bbbb";
- lb2.backgroundColor = [UIColor blueColor];
- lb2.leftPos.equalTo(lb1.centerXPos);
- lb2.topPos.equalTo(lb1.bottomPos).offset(40);
- lb2.widthDime.equalTo(@50);
- lb2.heightDime.equalTo(@50);
- lb2.bottomPos.offset(40);
- [rl addSubview:lb2];
- }
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- rl.backgroundColor = [UIColor grayColor];
- self.view = rl;
- //一组视图水平居中。
- UILabel *lb1 = [UILabel new];
- lb1.text = @"abcdefg";
- [lb1 sizeToFit];
- lb1.backgroundColor = [UIColor redColor];
- lb1.topPos.offset(100);
- [rl addSubview:lb1];
- UILabel *lb2 = [UILabel new];
- lb2.text = @"abcdefgfd";
- [lb2 sizeToFit];
- lb2.backgroundColor = [UIColor blueColor];
- lb2.topPos.offset(100);
- [rl addSubview:lb2];
- UILabel *lb3 = [UILabel new];
- lb3.text = @"abc";
- [lb3 sizeToFit];
- lb3.backgroundColor = [UIColor greenColor];
- lb3.topPos.offset(100);
- [rl addSubview:lb3];
- //lb1, lb2, lb3 三个视图组成一个组在父视图,lb2离lb15的间隔,lb3离lb210的间隔。如果要3个整体往右移则设置
- //lb1的offset。
- lb1.centerXPos.equalTo(@[lb2.centerXPos.offset(5), lb3.centerXPos.offset(10)]);
- //对照。
- UILabel *lb4 = [UILabel new];
- lb4.text = @"你好";
- [lb4 sizeToFit];
- lb4.backgroundColor = [UIColor orangeColor];
- [rl addSubview:lb4];
- lb4.leftPos.equalTo(lb1.leftPos);
- lb4.topPos.equalTo(lb2.bottomPos).offset(10);
- //一组视图垂直居中
- UILabel *lb5 = [UILabel new];
- lb5.text = @"abcdefg";
- [lb5 sizeToFit];
- lb5.backgroundColor = [UIColor redColor];
- lb5.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb5];
- UILabel *lb6 = [UILabel new];
- lb6.text = @"abcdefgfd";
- [lb6 sizeToFit];
- lb6.backgroundColor = [UIColor blueColor];
- lb6.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb6];
- UILabel *lb7 = [UILabel new];
- lb7.text = @"abc";
- [lb7 sizeToFit];
- lb7.backgroundColor = [UIColor greenColor];
- lb7.centerXPos.equalTo(rl.centerXPos);
- [rl addSubview:lb7];
- lb5.centerYPos.equalTo(@[lb6.centerYPos.offset(5), lb7.centerYPos.offset(10)]);
- }
- -(void)loadView
- {
- MyRelativeLayout *rl = [MyRelativeLayout new];
- self.view = rl;
- UILabel *lb1up = [UILabel new];
- lb1up.text = @"左上面";
- lb1up.backgroundColor = [UIColor greenColor];
- lb1up.font = [UIFont systemFontOfSize:17];
- [lb1up sizeToFit];
- [rl addSubview:lb1up];
- UILabel *lb1down = [UILabel new];
- lb1down.text = @"我左在下面";
- lb1down.backgroundColor = [UIColor greenColor];
- [lb1down sizeToFit];
- [rl addSubview:lb1down];
- UILabel *lb2up = [UILabel new];
- lb2up.text = @"我在中间上面";
- lb2up.backgroundColor = [UIColor greenColor];
- lb2up.font = [UIFont systemFontOfSize:12];
- [lb2up sizeToFit];
- [rl addSubview:lb2up];
- UILabel *lb2down = [UILabel new];
- lb2down.text = @"中";
- lb2down.backgroundColor = [UIColor greenColor];
- [lb2down sizeToFit];
- [rl addSubview:lb2down];
- UILabel *lb3up = [UILabel new];
- lb3up.text = @"右上";
- lb3up.backgroundColor = [UIColor greenColor];
- [lb3up sizeToFit];
- [rl addSubview:lb3up];
- UILabel *lb3down = [UILabel new];
- lb3down.text = @"右边的下方";
- lb3down.backgroundColor = [UIColor greenColor];
- lb3down.font = [UIFont systemFontOfSize:16];
- [lb3down sizeToFit];
- [rl 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);
- }
通过代码我们可以看出来虽然是有上下两排视图,但是我们可以通过centerYPos和centerXPos的值设置数组的方式来实现一组视图的居中显示。