- Swift:TangramKit: https://github.com/youngsoft/TangramKit
- OC:MyLayout: https://github.com/youngsoft/MyLinearLayout
简介
MyLayout是一套iOS界面视图布局框架。其内核是基于对UIView的layoutSubviews方法的重载以及对子视图的bounds和center属性的设置而实现的。MyLayout功能强大而且简单易用,它集成了iOS Autolayout和Size Classes、android的5大布局体系、HTML/CSS的浮动定位技术以及flex-box和bootstrap框架等市面上主流的平台的界面布局功能,同时提供了一套非常简单和完备的多屏幕尺寸适配的解决方案。MyLayout还提供了Swift版本TangramKit
MyLayout的优势
- MyLayout的实现内核是基于frame的设置,而不是对AutoLayout的封装。因此在使用上不会受到任何操作系统版本的限制。
- 有文章表明用frame进行布局的性能要高于用AutoLayout进行布局的性能,尤其是当界面内视图数量增加时效果更加明显。
- AutoLayout的思想是通过视图之间的约束依赖来完成布局,但是约束依赖的结果是造成视图之间的耦合性高而增大了界面更新的成本。而MyLayout则除了提供约束依赖外,还提供了根据视图添加顺序自动建立约束的功能,从而减少了这种显示依赖关系建立的问题,最终的结果是简化了布局的代码量,以及减少了布局更新时的代码修改量。
- AutoLayout只是一种相对约束的布局,而MyLayout除了同时提供具有和AutoLayout相同能力的相对布局外、还提供了线性布局、框架布局、表格布局、流式布局、浮动布局、路径布局7大布局体系,你完全可以根据你的界面需求来选择一种最简易的布局容器来实现你的功能,同时MyLayout还支持Size classes的机制,以及提供了一些实现屏幕尺寸完美适配的方法。
- MyLayout主要是一种通过代码进行布局的解决方案,但是框架一样可以支持和XIB以及SB结合布局的方式。并提供了视图隐藏和显示时会自动激发布局、布局视图的高度自适应(UITableviewCell动态高度)、标签云实现、左右内容宽度自适应、按比例分配尺寸和间距、整体停靠控制等等各种强大的功能。
AutoLayout和frame布局的性能比较
参考的文章地址:http://floriankugler.com/2013/04/22/auto-layout-performance-on-ios/
应用场景
举例下面一个应用场景:
- 有一个容器视图S的宽度是100而高度则是由四个从上到下依次排列的子视图A,B,C,D的高度总和。
- 视图A的左边距占用父视图宽度的20%,而右边距则占用父视图宽度的30%,高度则等于自身的宽度。
- 视图B的左边距是40,宽度则占用父视图的剩余宽度,高度是40。
- 视图C的宽度占用父视图的所有宽度,高度是40。
- 视图D的右边距是20,宽度是父视图宽度的50%,高度是40。
最终的效果图如下:
MyLinearLayout *S = [MyLinearLayout linearLayoutWithOrientation:MyLayoutViewOrientation_Vert];
S.subviewMargin = ;
S.myWidth = ; UIView *A = UIView.new;
A.leftPos.equalTo(@0.2);
A.rightPos.equalTo(@0.3);
A.heightDime.equalTo(A.widthDime);
[S addSubview:A]; UIView *B = UIView.new;
B.leftPos.equalTo(@);
B.widthDime.equalTo(@);
B.heightDime.equalTo(@);
[S addSubview:B]; UIView *C = UIView.new;
C.widthDime.equalTo(S.widthDime);
C.heightDime.equalTo(@);
[S addSubview:C]; UIView *D = UIView.new;
D.rightPos.equalTo(@);
D.widthDime.equalTo(S.widthDime).multiply(0.5);
D.heightDime.equalTo(@);
[S addSubview:D];
系统结构
布局位置类MyLayoutPos
MyLayoutPos类是用来描述一个视图所在的位置的类。UIView中扩展出了leftPos,topPos,bottomPos,rightPos,centerXPos,centerYPos这六个变量来实现视图的定位操作。您可以用这些变量的equalTo
方法来设置视图之间的边距和间距。 equalTo
方法可以设置NSNumber, MyLayoutPos, NSArray<MyLayoutPos*>这几种值,分别用于不同的场景。同时系统提供了6个简单的变量myLeftMargin, myTopMargin, myBottomMargin, myRightMargin, myCenterXOffset, mYCenterYOffset来设置NSNumber类型的值,比如 A.leftPos.equalTo(@10); 等价于 A.myLeftMargin = 10;
.
布局尺寸类MyLayoutSize
MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出了widthDime,heightDime这两个变量来实现视图的宽度和高度尺寸的设置。您可以用其中的equalTo
方法来设置视图的宽度和高度。equalTo
方法可以设置NSNumber, MyLayoutSize, NSArray<MyLayoutSize*>这几种值,分别用于不同的场景。同时系统提供了2个简单的变量myWidth,myHeight来设置NSNumber类型的值,比如A.widthDime.equalTo(@10); 等价于A.myWidth = 10;
.
线性布局MyLinearLayout
等价于iOS的UIStackView和android的LinearLayout布局。
线性布局是一种里面的子视图按添加的顺序从上到下或者从左到右依次排列的单列(单行)布局视图,因此里面的子视图是通过添加的顺序建立约束和依赖关系的。 子视图从上到下依次排列的线性布局视图称为垂直线性布局视图,而子视图从左到右依次排列的线性布局视图则称为水平线性布局。
示例代码:
-(void)loadView
{
[super loadView]; MyLinearLayout *S = [MyLinearLayout linearLayoutWithOrientation:MyLayoutViewOrientation_Vert];
S.myWidth = ;
S.subviewMargin = ; UIView *A = [UIView new];
A.myLeftMargin = A.myRightMargin = ;
A.myHeight = ;
[S addSubview:A]; UIView *B = [UIView new];
B.myLeftMargin = ;
B.myWidth = B.myHeight = ;
[S addSubview:B]; UIView *C = [UIView new];
C.myRightMargin = ;
C.myWidth = ;
C.myHeight = ;
[S addSubview:C]; UIView *D = [UIView new];
D.myLeftMargin = D.myRightMargin = ;
D.myHeight = ;
[S addSubview:D]; [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
A.backgroundColor = [UIColor greenColor];
B.backgroundColor = [UIColor blueColor];
C.backgroundColor = [UIColor orangeColor];
D.backgroundColor = [UIColor cyanColor];
}
相对布局MyRelativeLayout
等价于iOS的AutoLayout 和 Android的RelativeLayout布局。
相对布局是一种里面的子视图通过相互之间的约束和依赖来进行布局和定位的布局视图。相对布局里面的子视图的布局位置和添加的顺序无关,而是通过设置子视图的相对依赖关系来进行定位和布局的。
示例代码:
-(void)loadView
{
[super loadView]; MyRelativeLayout *S = [MyRelativeLayout new];
S.widthDime.equalTo(@);
S.heightDime.equalTo(@); UIView *A = [UIView new];
A.leftPos.equalTo(@);
A.topPos.equalTo(@);
A.widthDime.equalTo(@);
A.heightDime.equalTo(A.widthDime);
[S addSubview:A]; UIView *B = [UIView new];
B.leftPos.equalTo(A.centerXPos);
B.topPos.equalTo(A.bottomPos).offset();
B.widthDime.equalTo(@);
B.heightDime.equalTo(A.heightDime);
[S addSubview:B]; UIView *C = [UIView new];
C.leftPos.equalTo(B.rightPos).offset();
C.bottomPos.equalTo(B.bottomPos);
C.widthDime.equalTo(@);
C.heightDime.equalTo(B.heightDime).multiply(0.5);
[S addSubview:C]; UIView *D = [UIView new];
D.bottomPos.equalTo(C.topPos).offset();
D.rightPos.equalTo(@);
D.heightDime.equalTo(A.heightDime);
D.widthDime.equalTo(D.heightDime);
[S addSubview:D]; UIView *E = [UIView new];
E.centerYPos.equalTo(@);
E.centerXPos.equalTo(@);
E.heightDime.equalTo(@);
E.widthDime.equalTo(S.widthDime).add(-);
[S addSubview:E];
//.. F, G [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
A.backgroundColor = [UIColor greenColor];
B.backgroundColor = [UIColor blueColor];
C.backgroundColor = [UIColor orangeColor];
D.backgroundColor = [UIColor cyanColor];
E.backgroundColor = [UIColor magentaColor];
}
框架布局MyFrameLayout
等价于Android的FrameLayout布局。
框架布局是一种里面的子视图停靠在父视图特定方位并且可以重叠的布局视图。框架布局里面的子视图的布局位置和添加的顺序无关,只跟父视图建立布局约束依赖关系。框架布局将垂直方向上分为上、中、下三个方位,而水平方向上则分为左、中、右三个方位,任何一个子视图都只能定位在垂直方向和水平方向上的一个方位上。
示例代码:
-(void)loadView
{
[super loadView]; MyFrameLayout *S = [MyFrameLayout new];
S.mySize = CGSizeMake(,); UIView *A = [UIView new];
A.mySize = CGSizeMake(,);
[S addSubview:A]; UIView *B = [UIView new];
B.mySize = CGSizeMake(,);
B.myRightMargin = ;
[S addSubview:B]; UIView *C = [UIView new];
C.mySize = CGSizeMake(,);
C.myCenterYOffset = ;
[S addSubview:C]; UIView *D = [UIView new];
D.mySize = CGSizeMake(,);
D.myCenterOffset = CGPointZero;
[S addSubview:D]; //..E,F,G [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
A.backgroundColor = [UIColor greenColor];
B.backgroundColor = [UIColor blueColor];
C.backgroundColor = [UIColor orangeColor];
D.backgroundColor = [UIColor cyanColor];
}
表格布局MyTableLayout
等价于Android的TableLayout布局和HTML的table元素。
表格布局是一种里面的子视图可以像表格一样多行多列排列的布局视图。子视图添加到表格布局视图前必须先要建立并添加行视图,然后再将子视图添加到行视图里面。如果行视图在表格布局里面是从上到下排列的则表格布局为垂直表格布局,垂直表格布局里面的子视图在行视图里面是从左到右排列的;如果行视图在表格布局里面是从左到右排列的则表格布局为水平表格布局,水平表格布局里面的子视图在行视图里面是从上到下排列的。
示例代码:
-(void)loadView
{
[super loadView]; MyTableLayout *S = [MyTableLayout tableLayoutWithOrientation:MyLayoutViewOrientation_Vert];
S.wrapContentWidth = YES;
S.rowSpacing = ;
S.colSpacing = ; [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; UIView *A = [UIView new];
A.mySize = CGSizeMake(,);
[S addSubview:A]; UIView *B = [UIView new];
B.mySize = CGSizeMake(,);
[S addSubview:B]; UIView *C = [UIView new];
C.mySize = CGSizeMake(,);
[S addSubview:C]; [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; UIView *D = [UIView new];
D.mySize = CGSizeMake(,);
[S addSubview:D]; //...E,F [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
A.backgroundColor = [UIColor greenColor];
B.backgroundColor = [UIColor blueColor];
C.backgroundColor = [UIColor orangeColor];
D.backgroundColor = [UIColor cyanColor];
}
流式布局MyFlowLayout
等价于CSS3的flex-box。
流式布局是一种里面的子视图按照添加的顺序依次排列,当遇到某种约束限制后会另起一行再重新排列的多行展示的布局视图。这里的约束限制主要有数量约束限制和内容尺寸约束限制两种,而换行的方向又分为垂直和水平方向,因此流式布局一共有垂直数量约束流式布局、垂直内容约束流式布局、水平数量约束流式布局、水平内容约束流式布局。流式布局主要应用于那些子视图有规律排列的场景,在某种程度上可以作为UICollectionView的替代品。
示例代码:
-(void)loadView
{
[super loadView]; MyFlowLayout *S = [MyFlowLayout flowLayoutWithOrientation:MyLayoutViewOrientation_Vert arrangedCount:];
S.wrapContentHeight = YES;
S.myWidth = ;
S.padding = UIEdgeInsetsMake(, , , );
S.gravity = MyMarginGravity_Horz_Fill;
S.subviewMargin = ; for (int i = ; i < ; i++)
{
UIView *A = [UIView new];
A.heightDime.equalTo(A.widthDime);
[S addSubview:A]; A.backgroundColor = [UIColor greenColor]; } [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
}
浮动布局MyFloatLayout
等价于css中的float定位。
浮动布局是一种里面的子视图按照约定的方向浮动停靠,当尺寸不足以被容纳时会自动寻找最佳的位置进行浮动停靠的布局视图。浮动布局的理念源于HTML/CSS中的浮动定位技术,因此浮动布局可以专门用来实现那些不规则布局或者图文环绕的布局。根据浮动的方向不同,浮动布局可以分为左右浮动布局和上下浮动布局。
示例代码:
-(void)loadView
{
[super loadView]; MyFloatLayout *S = [MyFloatLayout floatLayoutWithOrientation:MyLayoutViewOrientation_Vert];
S.wrapContentHeight = YES;
S.padding = UIEdgeInsetsMake(, , , );
S.subviewMargin = ;
S.myWidth = ; UIView *A = [UIView new];
A.mySize = CGSizeMake(,);
[S addSubview:A]; UIView *B = [UIView new];
B.mySize = CGSizeMake(,);
[S addSubview:B]; UIView *C = [UIView new];
C.mySize = CGSizeMake(,);
[S addSubview:C]; UIView *D = [UIView new];
D.mySize = CGSizeMake(,);
[S addSubview:D]; UIView *E = [UIView new];
E.mySize = CGSizeMake(,);
E.reverseFloat = YES;
[S addSubview:E]; UIView *F = [UIView new];
F.mySize = CGSizeMake(,);
[S addSubview:F]; [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
A.backgroundColor = [UIColor greenColor];
B.backgroundColor = [UIColor blueColor];
C.backgroundColor = [UIColor orangeColor];
D.backgroundColor = [UIColor cyanColor];
E.backgroundColor = [UIColor blackColor];
F.backgroundColor = [UIColor whiteColor];
}
路径布局MyPathLayout
布局库独有
路径布局是一种里面的子视图根据您提供的一条特定的曲线函数形成的路径来进行布局的布局视图。您需要提供一个实现曲线路径的函数、一个特定的坐标体系、一种特定的子视图在曲线上的距离设置这三个要素来实现界面布局。当曲线路径形成后,子视图将按相等的距离依次环绕着曲线进行布局。路径布局主要应用于那些具有特定规律的不规则排列,而且效果很酷炫的的界面布局。
示例代码:
-(void)loadView
{
[super loadView]; MyPathLayout *S = [MyPathLayout new];
S.mySize = CGSizeMake(,);
S.coordinateSetting.isReverse = YES;
S.coordinateSetting.origin = CGPointMake(0.5, 0.2); S.polarEquation = ^(CGFloat angle)
{
return * ( + cos(angle));
}; for (int i = ; i < ; i++)
{
UIView *A = [UIView new];
A.mySize = CGSizeMake(,);
[S addSubview:A]; A.backgroundColor = [UIColor greenColor];
} [self.view addSubview:S];
S.backgroundColor = [UIColor redColor];
}
Size Classes的支持
等价于iOS的Size Classes
MyLayout布局体系为了实现对不同屏幕尺寸的设备进行适配,提供了对Size Classes的支持。您可以将Size Classes和上述的6种布局搭配使用,以便实现各种设备界面的完美适配。系统提供2个UIView的扩展方法:
-(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass;
-(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass;
来实现对Size Classes的支持。比如下面的例子:
//默认所有设备的设置。
MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyLayoutViewOrientation_Vert];
rootLayout.padding = UIEdgeInsetsMake(, , , );
rootLayout.wrapContentHeight = NO;
rootLayout.gravity = MyMarginGravity_Horz_Fill; //MySizeClass_wAny | MySizeClass_hCompact 表明的是iPhone设备的横屏.
MyLinearLayout *lsc = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; lsc.orientation = MyLayoutViewOrientation_Horz;
lsc.wrapContentWidth = NO;
lsc.gravity = MyMarginGravity_Vert_Fill;
使用方法
直接拷贝
- 将github工程中的Lib文件夹下的所有文件复制到您的工程中。
- 将
#import "MyLayout.h"
头文件放入到您的pch文件中,或者在需要使用界面布局的源代码位置。
CocoaPods安装
如果您还没有安装cocoapods则请先执行如下命令:
$ gem install cocoapods
为了用CocoaPods整合MyLayout到您的Xcode工程, 请建立如下的Podfile:
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'
pod 'MyLayout', '~> 1.3.4'
然后运行如下命令:
$ pod install
演示效果图
链接:
- Swift:TangramKit: https://github.com/youngsoft/TangramKit
- OC:MyLayout: https://github.com/youngsoft/MyLinearLayout