一. 概述
进行iOS项目开发时,常用的布局方式有两种:Frame
布局和AutoLayout
,Frame
布局没啥可说的,直接设置控件的横纵坐标,并指定宽高。AutoLayout
是通过设置控件相对位置的约束进行布局。AutoLayout
的本意是好的,但是由于他的语法实在不怎么友好,导致我们在实际项目开发中用的并不多,只能靠Masonry
这样的第三方库来使用它。Masonry
布局虽然容易理解,但是它的代码量太大,每一个控件都要对其进行block设置。
在前端开发中,Flex布局使用尤为普遍,那么iOS端有没有类似前端Flex布局的方案呢?答案是肯定的,除了在 Weex
以及 React Native
两个著名的跨平台项目里有用到 Flex布局外,AsyncDisplayKit
也同样引入了 Flex布局,它们都是基于 Facebook
的 Yoga
的二次开发。而今天我们要介绍的 FlexLib 同样如此。
二. Flex布局与相关属性介绍
先来了解一下Flex布局:
Flex布局又叫弹性布局,是一种为一维布局而设计的布局方法。一维的意思是你希望内容是按行或者列来布局。你可以使用display:flex
来将元素变为弹性布局。我们直接看例子:
实现7个item横向排列
flex布局2
item0
item1
item2
item3
item4
item5
item6
item7
上面是前端代码,看不懂没关系,这里重点是借这个例子来熟悉一下Flex布局的一些常用属性。Flex属性分类两类:一类是作用在容器上的,另一类是作用在子Item上的。
- 作用在容器上的属性
display:flex
: 设置container
容器为弹性布局flex-direction
:决定主轴的方向,项目横向或是纵向排列
取值:row | row-reverse | column | column-reverse;
row
(默认值):主轴为水平方向,起点在左端。
row-reverse
:主轴为水平方向,起点在右端。
column
:主轴为垂直方向,起点在上沿。
column-reverse
:主轴为垂直方向,起点在下沿。justify-content
: 定义Item在主轴上如何对齐。
取值:flex-start | flex-end | center | space-between | space-around;
flex-start
(默认值):左对齐
flex-end
:右对齐
center
: 居中
space-between
:两端对齐,项目之间的间隔都相等。
space-around
:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。align-items
:定义Item在交叉轴上如何对齐。
取值:align-items: flex-start | flex-end | center;
flex-start
:交叉轴的起点对齐。
flex-end
:交叉轴的终点对齐。
center
:交叉轴的中间点对齐。flex-wrap
:一条轴线上放不下,决定其是否换行
取值: nowrap(不换行) | wrap(换行)align-content
:也是控制Flex Item 在交叉轴上的对齐方式,只不过是以一整行作为最小单位。注意,如果Flex Item只有一行,该属性不起作用的。调整flex-wrap
为 Wrap,效果才显示出来。
取值:flex-start | flex-end | center | space-between | space-around;
- 作用在Item上的属性
align-self
:可以让单个 Flex Item 与其它 Flex Item 有不一样的对齐方式,覆盖align-items
属性。默认值为auto,表示继承Flex容器的align-items
属性。flex
属性:flex属性是flex-grow
,flex-shrink
和flex-basis
的简写,默认值为0 1 auto
。
flex-grow
: 属性定义项目的放大比例,默认为0,即如果存在剩余空间也不放大。 取值越大,占用剩余空间越大。
flex-shrink
: 属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
flex-basis
:属性指定了flex元素在主轴方向上的初始大小
这里设置item6
与 item7
的flex为1和2,表示当前轴剩余的空间item6占1/3,item7占2/3.
更多的关于flex的属性可以查看这里:Flex布局
三. FlexLib的使用
1.使用方式
CocoaPods
引入FlexLib:
pod 'FlexLib'
使用:
- 创建视图控制器继承
FlexBaseVC
,或者创建视图继承FlexCustomBaseView
- 创建与视图控制器或视图同名的xml文件
- 在xml文件中添加控件进行布局
例子:
TestLoginVC.h
@interface TestLoginVC : FlexBaseVC
@end
TestLoginVC.m
#import "TestLoginVC.h"
@interface TestLoginVC ()
{
UIScrollView* scroll;//自动绑定name属性的值
UIView* close;
}
@end
@implementation TestLoginVC
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"Touch Demo";
}
- (void)tapTouchAction
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@""
message:@"You pressed"
delegate:nil
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK",nil];
[alert show];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
TestLoginVC.xml
说明:
默认视图从上到下进行排列,在视图标签内添加其他视图,相当于为视图添加子视图
FlexLib
支持两种类型的属性:布局属性(Layout)和视图属性(attr),布局属性与yoga
所支持的属性一致,视图属性除了文档中所列的属性以外,还可以使用FLEXSET宏对现有属性进行扩展。你可以在这里查看它所支持的 layout attributes 和 view attributesFlexScrollView
(UIScrollView的子类)可以自动管理滚动范围,FlexTouchView
类似于UIButton,内置onPress
属性,可以设置其触发方法。可以设置控件的
name
属性,name
属性的值会自动绑定代码中具有相同名称的成员变量,你可以直接在代码中使用它。你可以预定义一些属性值,将其放入全局xml文件中,在
AppDelegate
进行初始化
全局xml文件 system.style
的格式类似这样:
导入系统样式文件
NSString *path = [[NSBundle mainBundle]pathForResource:@"system" ofType:@"style"];
[[FlexStyleMgr instance] loadClassStyle:path];
使用:
FlexLib
支持运行时更新界面,具体的配置方式那你可以看这里FlexLib
支持Cell高度自动计算,并且适配iPhone X等机型,实际开发中自定义UITableViewCell
需要继承FlexBaseTableCell
,自定义UICollectionViewCell
需要继承FlexCollectionCell
。
2.FlexXmlBaseView、FlexFrameView、FlexCustomBaseView 的使用
在使用xml
进行布局时,不可避免的要用到自定义视图,如果自定义视图也想要用xml进行布局,此时需要继承FlexXmlBaseView
、FlexFrameView
、FlexCustomBaseView
这三种视图中的一种。
(1)自定义视图使用xml布局
继承
FlexXmlBaseView
的视图:能在xml文件中使用,可以通过initWithRootView
方式创建,但不能通过initWithFrame
创建,也不能能直接设置frame,优点是更加轻量级,不会增加额外的视图层级.继承
FlexFrameView
的视图:可以直接设置frame,但是不能用在xml文件中。例如
FlexFrameView *frameView = [[FlexFrameView alloc] initWithFlex:@"CustomFrameView" Frame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 0) Owner:self];
frameView.flexibleHeight = YES;//设置高度自适应
[_scrollView addSubview:frameView];
CustomFrameView.xml
是这个视图对应的xml布局文件
- 继承
FlexCustomBaseView
的视图:既可以用在xml中,也可以像传统的UIView派生类那样使用initWithFrame
创建,缺点是会额外的增加多余的视图层级.
注意:继承
FlexCustomBaseView
或FlexFrameView
的视图必须要设置以下两个属性
//宽和高是否可变?缺省值均为NO
@property(nonatomic,assign) BOOL flexibleWidth;
@property(nonatomic,assign) BOOL flexibleHeight;
(2)自定义视图不使用xml布局,但是也想用在xml文件中
此时我们的自定义视图需要满足以下两个条件就可以用于xml文件中:
- 所有的初始化必须在
init
方法中完成。任何其他的init
函数,比如initWith…
不会被调用。 - 你可以用
FLEXSET
宏扩展视图属性,然后你可以在xml文件中设置属性。
直接在父视图中使用
3.其他设置
- 缩放因子
在进行页面布局时,如果想让不同尺寸的屏幕共用一套设计,随着屏幕尺寸的变化,页面控件的大小随之缩放,这时候会用到缩放因子。设置缩放因子:
// 设置缩放因子,使用方法 在xml 布局中 *16 ==> 16*factor + 1
float factor = [UIScreen mainScreen].bounds.size.width/375;
FlexSetScale(factor, 1);
在布局xml中使用,数字前面加上*
这表示:Label的fontSize为16*factor + 1
- 在属性中使用表达式
在布局属性和视图属性中添加表达式,属性名前要加$
,ScreenWidth
是系统预定义的宏,你也可以用自定义的宏。目前支持的运算符+, -, *, /, (, ),
- FlexTextView
它是另外一个系统提供的类,能够自动根据输入的文字调整其高度,且保证其高度不会超出最小(minHeight)和最大高度(maxHeight)。
四. 总结
FlexLib
可能对有前端开发经验的同学比较友好,对原生开发同学而言,学习一下他的语法也没坏处。在日常开发中,我们可以有选择的使用这个布局,熟悉了这个布局后,会极大的提升我们页面布局效率,从而让我们将精力集中到更加核心的功能上。
参考:
https://github.com/zhenglibao/FlexLib