热干面
是一个轻量级的 iOS 布局辅助框架。用类似于 AutoLayout
的位置和关系描述以及从 Masonry 平行过渡的语法,为 UI 元素设置简洁直观的 frame
布局信息,取代了 UIView
自带的较简陋的 frame
设置方法。
热干面
本质上仍然是 frame
布局,只不过将设置 frame
信息的步骤由之前固定的 CGRectMake(x,y,width,height)
变成了更灵活和更直观的方式。
强烈推荐使用 Cocoapods 为您的项目引入 热干面
:
platform:ios,'7.0'
pod 'Hotpot'
注意: 请指定 iOS 的使用版本号,最低为 iOS7.0
热干面
仅在 iOS7+ 的 ARC 项目中良好运行,请确保使用最新版本的 XCode。
一切准备就绪后,在需要使用 热干面
的文件中引入头文件:
#import "Hotpot.h"
热干面
扩展了几个 UIView
的基本布局属性,这些属性是可读且可写的。通过这些属性,可以在代码中更加灵活地读取和设置元素的 frame
信息。
例如,在原始的 UIKit
中,为了读取一个元素的宽度,需要使用以下代码:
// 获取 testView 的宽度
CGFloat width = CGRectGetWidth(testView.frame);
UIKit
没有提供单独设置某项布局属性的方法,需要设置元素的宽度也必须重新设置其 frame
信息:
// 设置 testView 的宽度为 120.f
testView.frame = CGRectMake(CGRectGetMinX(testView.frame),
CGRectGetMinY(testView.frame),
120.f,
CGRectGetHeight(testView.frame));
有了 热干面
,只需要这样的代码:
// 获取 testView 的宽度
CGFloat width = testView.width;
// 设置 testView 的宽度为 120.f
testView.width = 120.f;
除了 width
外,热干面
还提供了更多这样的便捷属性:
热干面属性 | UIKit 方法 | 类型 |
---|---|---|
left |
CGRectGetMinX |
CGFloat |
right |
CGRectGetMaxX |
CGFloat |
top |
CGRectGetMinY |
CGFloat |
bottom |
CGRectGetMaxY |
CGFloat |
width |
CGRectGetWidth |
CGFloat |
height |
CGRectGetHeight |
CGFloat |
centerX |
CGRectGetMinX |
CGFloat |
centerY |
CGRectGetMinX |
CGFloat |
origin |
-- | CGPoint |
size |
-- | CGSize |
有了这些属性,布局时可以更加灵活地只针对某一项进行设置:
viewA.left = viewB.right + 20.f;
viewB.top = viewC.bottom + 20.f;
viewC.centerX = self.view.centerX;
viewD.width = 100.f;
viewE.size = CGSizeMake(100.f, 80.f);
AutoLayout
通过对 UI 元素布局信息的描述创建了一系列约束。这些约束信息记录了 UI 元素相对于其他元素的位置关系和大小信息。
在传统的 frame
布局思路中,一个 UI 元素的布局由一组坐标和一组宽高信息确定。通常界面上 UI 元素的布局是相互关联的,并且这些相对关系是非常直观的。然而,在编写代码时,需要将这些原本很清晰的相对关系用冗长难懂的 CGRectMake
代码表示出来。这些代码可读性差,且不利于维护。
例如,对于一个在水平方向居中、竖直方向底部 30px 的元素的布局代码:
viewA.frame = CGRectMake((CGRectGetWidth(self.view.bounds) - CGRectGetWidth(viewA.frame))/2,
CGRectGetMaxX(self.view.bounds) - CGRectGetHeight(viewA.frame) - 30.f,
CGRectGetWidth(viewA.frame),
CGRectGetHeight(viewA.frame)
);
有了 热干面
,只需描述清楚位置关系即可:
[viewA frameLayout:^(HotpotFrameLayout *layout) {
layout.centerX.equalTo(self.view.centerX);
layout.bottom.equalTo(self.view.bottom).offset(-30.f);
}];
这样的代码更加清晰和可读,省去了繁琐的计算逻辑,将代码的重点集中在布局关系的描述上。
如果您使用过 Masonry
,那么这样的代码语法您一定不陌生,热干面
采用了从 Masonry
平行过渡的语法,阅读起来非常清晰。
equalTo
CGFloat
表示关联到的属性。
offset
CGFloat
表示偏移量。向左和向上偏移是负数,向下和向右偏移为正数。
热干面
本质上仍然是 frame
布局。在布局描述中使用另一个元素的布局属性时,请确保这个属性已经确定好了布局,这点与 AutoLayout
是不同的。例如:
// 请确保 viewB 和 viewC 的布局已经完成
[viewA frameLayout:^(HotpotFrameLayout *layout) {
layout.left.equalTo(viewB.left);
layout.top.equalTo(viewC.bottom).offset(30.f);
}];
但是,在同一个元素的布局描述中,每个属性都是相互独立的,不需要考虑顺序。
由于 热干面
终究是 frame
布局,所以关于布局的代码仍建议写在 viewDidLayoutSubviews
或 viewDidLayout
中:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self.redView frameLayout:^(HotpotFrameLayout *layout) {
layout.left.equalTo(self.view.left).offset(20.f);
layout.right.equalTo(self.view.right).offset(-20.f);
layout.height.equalTo(40.f);
layout.top.equalTo(self.view.top).offset(40.f);
}];
[self.blueView frameLayout:^(HotpotFrameLayout *layout) {
layout.width.equalTo(100.f);
layout.centerX.equalTo(self.view.centerX);
layout.bottom.equalTo(self.view.bottom).offset(-50.f);
layout.height.equalTo(40.f);
}];
[self.blackView frameLayout:^(HotpotFrameLayout *layout) {
layout.height.equalTo(100.f);
layout.centerY.equalTo(self.view.centerY);
layout.left.equalTo(self.view.left).offset(20.f);
layout.right.equalTo(self.blueView.left);
}];
[self.orangeView frameLayout:^(HotpotFrameLayout *layout) {
layout.width.equalTo(40.f);
layout.height.equalTo(40.f);
layout.left.equalTo(self.blackView.left);
layout.top.equalTo(self.blackView.bottom).offset(20.f);
}];
}
性能
UI 元素较多,布局关系复杂的场景,以及需要频繁复用的场景如 UITableViewCell
,frame
布局性能优于 Autolayout
。
动画
在动画实现上,相对于 Autolayout
,frame
布局更加直观和可控。
调试
Autolayout
布局由于元素之间互相影响,加上优先级冲突,在实际应用中的调试不太方便。而且对于在 xib 中使用了 Autolayout
属性的元素,在代码中更新约束也比较麻烦。
过渡
许多团队的项目仍在使用传统的 frame
布局,Hotpot
是一个很轻量的过渡方案。