MyLayout 框架不仅支持 Objective-C,也可以在 Swift 中使用。通过 MyLayout,可以使用面向对象的方式来创建和管理视图的布局,简化了 Auto Layout 中繁琐的约束设置流程。在 Objective-C 中,MyLayout 提供了相同的布局类型和属性,使用方式稍有不同,主要是语法和调用方式上的差异。
先介绍一下如何使用吧,线性布局和相对布局是用的比较多的布局方式。
1. 线性布局(MyLinearLayout)
线性布局是一种里面的子视图按添加的顺序从上到下或者从左到右依次排列的单列(单行)布局视图,因此里面的子视图是通过添加的顺序建立约束和依赖关系的。 子视图从上到下依次排列的线性布局视图称为垂直线性布局视图,而子视图从左到右依次排列的线性布局视图则称为水平线性布局
创建一个垂直线性布局的示例:
MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert];
rootLayout.frame = self.view.bounds;
rootLayout.topPos.equalTo(@0);
rootLayout.leftPos.equalTo(@0);
rootLayout.rightPos.equalTo(@0);
rootLayout.bottomPos.equalTo(@0);
// 添加子视图
UIView *view1 = [UIView new];
view1.myHeight = 50;
view1.leftPos.equalTo(@10);
view1.rightPos.equalTo(@10);
[view1 setBackgroundColor:[UIColor redColor]];
[rootLayout addSubview:view1];
UIView *view2 = [UIView new];
view2.myHeight = 100;
view2.leftPos.equalTo(@10);
view2.rightPos.equalTo(@10);
[view2 setBackgroundColor:[UIColor blueColor]];
[rootLayout addSubview:view2];
[self.view addSubview:rootLayout];
在这个例子中,我们创建了一个垂直线性布局容器 rootLayout
,并在其中添加了两个 UIView
子视图。每个子视图都有自己的高度和边距设置。view1
和 view2
分别设置了不同的高度,且左右边距为 10。
2. 相对布局(MyRelativeLayout)
相对布局允许子视图通过相对父视图或者其他子视图的位置来布局。
相对布局示例:
MyRelativeLayout *rootLayout = [MyRelativeLayout new];
rootLayout.frame = self.view.bounds;
rootLayout.topPos.equalTo(@0);
rootLayout.leftPos.equalTo(@0);
rootLayout.rightPos.equalTo(@0);
rootLayout.bottomPos.equalTo(@0);
UIView *view1 = [UIView new];
view1.mySize = CGSizeMake(100, 100);
view1.centerXPos.equalTo(rootLayout.centerXPos); // 水平居中
view1.topPos.equalTo(@10); // 距离父视图顶部 10
[view1 setBackgroundColor:[UIColor redColor]];
[rootLayout addSubview:view1];
UIView *view2 = [UIView new];
view2.mySize = CGSizeMake(100, 100);
view2.topPos.equalTo(view1.bottomPos).offset(10); // 位于 view1 底部,间隔 10
view2.centerXPos.equalTo(view1.centerXPos); // 水平与 view1 对齐
[view2 setBackgroundColor:[UIColor blueColor]];
[rootLayout addSubview:view2];
[self.view addSubview:rootLayout];
在这个示例中,view1
在父视图中水平居中,并且距离顶部有 10 的间隔。而 view2
则位于 view1
的下方,并保持水平对齐。通过设置 centerXPos
和 topPos
等属性,MyLayout 可以轻松实现相对布局。
3. 布局框架的类架构
这张图展示了 MyLayout 布局框架的类架构,帮助开发者理解其内部设计和结构。
-
MyBaseLayout:
- 这是 MyLayout 框架的基础类,所有具体的布局类(如
MyLinearLayout
、MyFrameLayout
等)都继承自它。它负责处理布局容器的基础功能,如视图的排列、布局更新等。
- 这是 MyLayout 框架的基础类,所有具体的布局类(如
-
布局子类:
- 从
MyBaseLayout
派生的不同布局类型用于支持多种布局方式:- MyLinearLayout:线性布局,视图依次排列,可以是垂直或水平。
- MyFrameLayout:帧布局,子视图重叠在一起,根据设置的大小、位置显示。
- MyRelativeLayout:相对布局,子视图可以相对于其他视图或容器进行布局。
- MyFlowLayout:流式布局,子视图按行或列排列,类似于文本换行的效果。
- MyFloatLayout:浮动布局,视图可以根据容器空间自动排列。
- MyPathLayout:路径布局,子视图可以沿着路径排列。
- MyGridLayout:网格布局,子视图按网格排列。
- MyTableLayout:表格布局,子视图按表格形式排列。
- 从
-
MyViewSizeClass 和子类:
MyViewSizeClass
是用于定义视图在不同尺寸类别下的表现,类似于 iOS 的 Size Class 概念。MyLayoutViewSizeClass
是它的子类,用于处理 MyLayout 视图的大小、边距、位置等属性。- 不同的布局有各自的
ViewSizeClass
,如MyLinearLayoutViewSizeClass
、MyTableLayoutViewSizeClass
等,来定义在这些布局中的尺寸规则。
-
UIView 的扩展 (Category):
- 通过对
UIView
进行扩展,MyLayout 框架为视图添加了自定义布局属性,如leftPos
、topPos
、widthSize
、heightSize
等。这些属性与MyLayoutPos
和MyLayoutSize
类相关联,帮助开发者通过简单的设置实现复杂的布局需求。
- 通过对
-
MyLayoutPos 和 MyLayoutSize:
-
MyLayoutPos:MyLayoutPos类是用来描述一个视图所在的位置的类。UIView中扩展出了leftPos,topPos,bottomPos,rightPos,centerXPos,centerYPos这六个变量来实现视图的定位操作。您可以用这些变量的equalTo方法来设置视图之间的边距和间距。 equalTo 方法可以设置NSNumber, MyLayoutPos, NSArray<MyLayoutPos*>这几种值,分别用于不同的场景。同时系统提供了6个简单的变量myLeft, myTop, myBottom, myRight, myCenterX, mYCenterY来设置NSNumber类型的值,比如 A.leftPos.equalTo(@10); 等价于 A.myLeft = 10;.
-
MyLayoutSize:MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出了widthSize,heightSize这两个变量来实现视图的宽度和高度尺寸的设置。您可以用其中的equalTo方法来设置视图的宽度和高度。equalTo方法可以设置NSNumber, MyLayoutSize, NSArray<MyLayoutSize*>这几种值,分别用于不同的场景。同时系统提供了2个简单的变量myWidth,myHeight来设置NSNumber类型的值,比如A.widthSize.equalTo(@10); 等价于A.myWidth = 10;.
-
-
MyWeight:
MyWeight
是一个与布局权重相关的概念,用于控制视图在容器中占据的相对空间。
通过这个类架构图,可以看到 MyLayout 框架是如何通过继承和扩展的方式,将多种布局模式整合到一个框架中,从而简化复杂布局的实现。
4. 底层原理
MyLayout 的底层原理主要是通过对每个视图的布局属性(如 myLeftMargin
、myWidth
、myHeight
等)进行计算,并在布局容器中根据这些属性重新调整每个子视图的位置和大小。这个过程与 Auto Layout 系统相似,但 MyLayout 不依赖 iOS 自带的 Auto Layout 约束机制,而是通过手动布局来优化性能和简化实现。
1. 视图树遍历与布局计算
MyLayout 的核心机制是遍历视图树,逐个计算每个视图的位置和大小。这个过程在布局视图的 layoutSubviews
方法中触发。当父布局容器需要重新布局时,会调用 layoutSubviews
,在这个方法中,MyLayout 遍历所有子视图,并根据子视图的布局属性(如边距、宽高、自适应等)进行计算和定位。
每个视图的布局属性都会影响到其最终的 frame
,MyLayout 会根据这些属性和布局容器的尺寸来动态调整子视图的位置和大小。例如:
myLeftMargin
和myRightMargin
决定视图在父视图中的左右间距。myWidth
决定视图的宽度,可以是固定值、百分比或根据内容自适应。weight
属性用于动态分配剩余空间,类似于flexbox
的flex
属性。
2. 布局属性的自定义与扩展
MyLayout 基于 UIView 的扩展,将自定义的布局属性直接挂载在每个子视图上。通过为 UIView 扩展自定义属性(例如 myLeftMargin
、myHeight
),MyLayout 实现了布局属性的可访问性。然后,框架通过在布局视图的 layoutSubviews
方法中访问这些自定义属性,完成布局的计算和调整。
这些自定义属性的设定值可以是固定数值,也可以是相对父视图或兄弟视图的动态值,这使得 MyLayout 在布局时非常灵活。例如:
- 当视图的宽度是相对父视图的宽度时,可以设置
myWidth.equalTo(self.view.myWidth)
,表示视图的宽度等于父视图宽度。
3. 自适应与动态调整
MyLayout 支持子视图的自适应布局,通过计算视图的固有内容大小和父视图的剩余空间,动态调整子视图的尺寸和位置。与 Auto Layout 类似,当某个视图的内容发生变化时(例如文本视图内容变长),MyLayout 可以自动调整该视图的大小,使其适应新的内容。
此外,MyLayout 还支持动态调整布局。当父视图的尺寸改变时(例如旋转屏幕或窗口大小调整),MyLayout 会重新计算所有子视图的布局,确保它们始终适应当前的父视图大小。
4. 避免 Auto Layout 的性能开销
MyLayout 的一个主要优势是避免了 Auto Layout 系统带来的性能开销。Auto Layout 通过约束系统来管理布局,内部需要解决一系列的线性方程,这可能在复杂布局场景下导致性能瓶颈。而 MyLayout 直接操作视图的 frame
属性,跳过了约束的解析过程,从而提高了布局效率,特别是在需要频繁动态调整布局的场景中表现更佳。
5. 布局类型的实现
MyLayout 提供了多种布局类型(线性布局、相对布局、表格布局等),这些布局类型的实现原理是根据布局容器的不同类型,采用不同的算法来计算子视图的排列方式。例如:
- 线性布局:通过遍历子视图,按照垂直或水平方向依次排列,并根据
myLeftMargin
、myTopMargin
等属性调整每个视图的位置。 - 相对布局:根据子视图的相对定位属性(例如
centerXPos.equalTo()
),在布局时计算相对关系,调整视图的位置。 - 表格布局:按照行列方式排列子视图,类似于表格的布局逻辑。
6. 性能优化
MyLayout 的性能优化体现在以下几个方面:
- 避免不必要的重绘:在子视图的布局属性发生变化时,MyLayout 会触发布局刷新,但它会避免无关子视图的重绘和布局调整,减少性能开销。
- 轻量级的布局计算:由于不依赖 Auto Layout 的约束解析,MyLayout 的布局计算只涉及简单的几何运算,避免了复杂的约束求解过程,从而提升布局效率。
- 支持缓存机制:在某些复杂场景下,MyLayout 还可以通过缓存布局结果,进一步减少重复计算的开销。
4. 使用
podfile中加入,然后运行,命令:pod install
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'
pod 'MyLayout'
框架作者还给出了一个y演示demo: