iOS开发之了解屏幕适配

荷叶生时春恨生,荷叶枯时秋恨成。
深知身在情长在,怅望江头江水声。

李商隐 《暮秋独游曲江》

屏幕适配

屏幕类型

hack_appList_result_failed

点和像素

hack_appList_result_failed

屏幕分辨率

hack_appList_result_failed

iPhone4之前

  • 没有适配,不用适配
  • 经常会出现坐标值写死的代码
1
2
3
4
5
6
7
8
9
//#define ScreenW 320
//#define ScreenH 480
#define ScreenW [UIScreen mainScreen].bounds.size.width
#define ScreenH [UIScreen mainScreen].bounds.size.height

// 创建一个显示在屏幕右下角的按钮
CGFloat buttonW = 100;
CGFloat buttonH = 50;
button.frame = CGRectMake(ScreenW - buttonW, ScreenH - buttonH, buttonW, buttonH);

iPad出现以后

  • 需要横竖屏适配
  • 出现了一种方便的屏幕适配技术:Autoresizing
    • 局限性:仅仅能解决子控件和父控件之间的相对关系问题

iOS6开始(Xcode4开始)

  • 出现了一种新的屏幕适配技术:Autolayout
    • 解决任何控件之间的相对关系问题

iOS8开始(Xcode6开始)

  • 出现了一种新的屏幕适配技术:Sizeclass

Autoresizing

  • UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
    • 距离父控件左边的间距是伸缩的
  • UIViewAutoresizingFlexibleRightMargin = 1 << 2,
    • 距离父控件右边的间距是伸缩的
  • UIViewAutoresizingFlexibleTopMargin = 1 << 3,
    • 距离父控件上边的间距是伸缩的
  • UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    • 距离父控件下边的间距是伸缩的
  • UIViewAutoresizingFlexibleWidth = 1 << 1,
    • 宽度跟随父控件宽度进行伸缩
  • UIViewAutoresizingFlexibleHeight = 1 << 4,
    • 高度跟随父控件高度进行伸缩

Autolayout

2个核心概念

  • 约束
    • 尺寸约束
      • width约束
      • height约束
    • 位置约束
      • 间距约束(上下左右间距)
  • 参照
    • 所添加的约束跟哪个控件有关(相对于哪个控件来说)

常见单词

  • Leading -> Left -> 左边
  • Trailing -> Right -> 右边

实现约束动画

1
2
3
4
5
[UIView animateWithDuration:2.0 animations:^{
self.topCos.constant += 250;
// layoutIfNeeded:强制更新控件(会更新当前控件和所有子控件)
[self.view layoutIfNeeded];
}];

警告和错误

hack_appList_result_failed

UILabel实现包裹内容

  • 设置宽度约束为 <= 固定值
  • 设置位置约束
  • 不用去设置高度约束

开发中能用到的小技巧

  • 有时候也会约束某几个控件的位置和宽高在屏幕中的位置等间距不变 此时需要在这些控件中间添加占位控件并设置hidden属性为yes即可
hack_appList_result_failed
  • 青色的控件设置:
    • 第一个青色控件自身的宽高以及和顶部或者底部的间隙
    • 第二个青色控件和第一个顶部或者底部水平 宽高尺寸也相同
  • 灰色的为占位控件 设置 :
    • 左右间隙均为0
    • 高度随便固定一个值 (宽度不要设置)
    • 距离顶部或者底部的间隙
    • 其他两个控件和第一个控件的顶或者底部对齐 宽度相同(高度随意)
    • 设置隐藏属性为yes

Masonry基本使用

头文件的宏定义

1
2
3
4
5
6
7
8
9
10
//define this constant if you want to use Masonry without the 'mas_' prefix
// 只要在导入Masonry主头文件之前定义这个宏, 那么以后在使用Masonry框架中的属性和方法的时候, 就可以省略mas_前缀
// 如果这个宏是在导入了Masonry.h之后定义, 那么无效
#define MAS_SHORTHAND

//define this constant if you want to enable auto-boxing for default syntax
// 只要在导入Masonry主头文件之前定义这个宏,那么就可以让equalTo函数接收基本数据类型, 内部会对基本数据类型进行包装
#define MAS_SHORTHAND_GLOBALS

#import "Masonry.h"

添加约束

方式1 (推荐的写法 可视化比较好)
1
2
3
4
5
[blueView makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view.top).offset(20); // y
make.left.equalTo(self.view.left).offset(20); // x
make.right.equalTo(self.view.right).offset(-20); // w
make.height.equalTo(50); // h
方式2(有mas_前缀)
1
2
3
4
5
6
7
8
[redView makeConstraints:^(MASConstraintMaker *make) {
// make.top.equalTo(self.view.mas_top).with.offset(20);
make.top.equalTo(self.view.top).with.offset(20);
make.left.equalTo(self.view.mas_left).offset(20);
make.width.and.height.equalTo(@100);
make.width.and.height.mas_equalTo(100);
make.width.and.height.equalTo(100);
}];
不推荐的写法(可视化比较差)
1
2
3
4
5
6
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(20);
make.left.offset(20);
make.bottom.offset(-20);
make.right.offset(-20);
}];
1
2
3
[redView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.insets(UIEdgeInsetsMake(20, 20, 20, 20));
}];

约束的其他常见注意点

  • 约束的添加
1
2
3
4
// makeConstraints: 每次都会添加新的约束
[self.redView makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.blueView.bottom).offset(100);
}];
  • 约束的update
1
2
3
4
// updateConstraints:专门用于更新约束的,如果没有约束会创建一个新的 如果有直接修改以前的
[self.redView updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.blueView.bottom).offset(100);
}];
  • 约束的清空
1
2
3
4
// remakeConstraints: 清空约束, 删除约束
[self.redView remakeConstraints:^(MASConstraintMaker *make) {

}];

VFL

  • VisualFormatLanguage: 可视化规格语言。

    注意点

  • 如果是通过代码添加Autolayout,那么必须在添加约束之前禁用Autoresizing
  • 禁用Autoresizing时,必须是给谁添加就禁用谁的,也就是说如果禁用了父控件无效
  • 添加约束之前,必须保证被约束的控件已经添加到父控件中了
1
2
self.view.translatesAutoresizingMaskIntoConstraints = NO;
redView.translatesAutoresizingMaskIntoConstraints = NO;

通过VFL添加约束

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 /*
VisualFormat: VFL语句
options: 对齐方式
metrics: 占位字符对应的值
views: 占位字符对应的控件
*/
// 设置水平方向的约束
NSDictionary *dict = NSDictionaryOfVariableBindings(redView);
NSArray *hCos = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[redView(w)]" options:kNilOptions metrics:@{@"w":@100} views:dict];
[self.view addConstraints:hCos];

// 设置垂直方向的约束
NSArray *vCos = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-20-[redView(h)]" options:kNilOptions metrics:@{@"h":@100} views:@{@"redView":redView}];
[self.view addConstraints:vCos];

注意点

  • VFL语句中不支持乘除法
  • 此时还是需要用最原始的方法(代码)来做
1
2
3
4
5
6
7
8
9
10
11
12
/*
Item == 第一个控件
attribute == 第一个控件的什么属性
relatedBy == 等于/小于等于/大于等于
toItem == 第二个控件
attribute == 第二个控件的什么属性
multiplier == 乘以多少
constant == 加上多少
*/
NSLayoutConstraint *redWidth = [NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:blueView attribute:NSLayoutAttributeWidth multiplier:0.5 constant:0.0];

[self.view addConstraint:redWidth];
要不要鼓励一下😘