热干面 0.0.5

热干面 0.0.5

测试已测试
语言语言 Obj-CObjective C
许可证 MIT
发布时间最新发布2016年4月

高票参与者Cao维护。



热干面 0.0.5

  • 作者:
  • 高票参与者

热干面 是一个轻量级的 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 布局,所以关于布局的代码仍建议写在 viewDidLayoutSubviewsviewDidLayout 中:

-(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);
    }];
}

为什么不直接使用 Autolayout / Masonry ?

  • 性能

    UI 元素较多,布局关系复杂的场景,以及需要频繁复用的场景如 UITableViewCellframe 布局性能优于 Autolayout

  • 动画

    在动画实现上,相对于 Autolayoutframe 布局更加直观和可控。

  • 调试

    Autolayout 布局由于元素之间互相影响,加上优先级冲突,在实际应用中的调试不太方便。而且对于在 xib 中使用了 Autolayout 属性的元素,在代码中更新约束也比较麻烦。

  • 过渡

    许多团队的项目仍在使用传统的 frame 布局,Hotpot 是一个很轻量的过渡方案。

感谢

Masonry 致敬。