屏幕适配

屏幕适配

本章节主要还是说明如何让应用程序能够适配在苹果不同的屏幕和如何让应用中的内容在不同的屏幕下能够正常的放置。
具体说明了:

  1. 屏幕适配的发展
  2. autoresizing的使用
  3. autolayout的使用
  4. 代码实现autolayout
  5. sizeClass的使用。

在今天的内容里面将来使用大量的实例来说明不同情况下的适配方式。其中重点是autolayout的使用

  • 屏幕适配的发展
  • autoresizing的使用
  • autolayout的使用
  • sizeClass的使用

了解屏幕适配的发展史

前言:现在已经不像以前那样只有一个尺寸,现在最少的iPhone开发需要最少需要适配三个尺寸。因此以前我们可以使用硬坐标去设定各个控件的位置,但是现在的话已经不可以了,我们需要去做适配,也许你说可以使用两套UI或两套以上的UI,但那样不高效也不符合设计。iOS有两大自动布局利器:autoresizing 和 autolayout(autolayout是IOS6以后新增)。autoresizing是UIView的属性,一直存在,使用也比较简单,但是没有autolayout那样强大。如果你的界面比较简单,要求的细节没有那么高,那么你完全可以使用autoresizing去进行自动布局

iphone3/iphone 3GS:没有屏幕适配,直接使用frame固定子控件的大小
iphone4 /4s/ipad:iphone屏幕大小一样,但是如果进行ipad开发,就需要考虑屏幕适配
iphone 5/5c/5s:屏幕大小不一样,需要考虑屏幕适配(使用autoresizing/autolayout实现)

autoresizing的使用

使用autoresizing的前提
  1. 需要去除autolayout选项,因为这两个属性冲突。view的autoresizesSubviews属性为yes时(默认为yes),autoresizing才会生效

  2. 从XCODE6开始,Storyboard&Xib默认是自动布局,因此我们需要手动调整,才能使用autoresizing


设置面板说明
使用storyboard实现autoresizing
  • 使用属性面板完成简单的案例示例
  • 体验六根线的含义
使用代码实现autoresizing
  • UIViewAutoresizing是一个枚举类型,默认是UIViewAutoresizingNone,也就是不做任何处理
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
  • 属性解释:

UIViewAutoresizingNone:不会随父视图的改变而改变
UIViewAutoresizingFlexibleLeftMargin:自动调整view与父视图左边距,以保证右边距不变
UIViewAutoresizingFlexibleWidth:自动调整view的宽度,保证左边距和右边距不变
UIViewAutoresizingFlexibleRightMargin:自动调整view与父视图右边距,以保证左边距不变
UIViewAutoresizingFlexibleTopMargin:自动调整view与父视图上边距,以保证下边距不变
UIViewAutoresizingFlexibleHeight:自动调整view的高度,以保证上边距和下边距不变
UIViewAutoresizingFlexibleBottomMargin:自动调整view与父视图的下边距,以保证上边距不变

说明一下,如果是经常使用Storyboard/Xib设置autoresizing,那么转变使用代码设置autoresizing的话,容易出现理解错误问题。比如说UIViewAutoresizingFlexibleTopMargin,也许会被误认为是顶部距离不变,其实是底部距离不变

  • autoresizing组合使用:枚举中的值可以使用|隔开,同时拥有多个值的功能,可以针对不同的场景作不同的变化:如:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin。意思是:view的宽度按照父视图的宽度比例进行缩放,距离父视图顶部距离不变

  • 注意事项:

    • 从XCODE6开始,Storyboard&Xib默认是自动布局,因此我们需要手动调整,才能使用autoresizing
  • 代码示例:

- (void)viewDidLoad {
    [super viewDidLoad];
    //1.添加UIView:以320*480为例
    UIView *testView=[[UIView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-44, self.view.frame.size.width, 44)];

    //2.设置UIView的属性(背景色,frame)
    testView.backgroundColor=[UIColor redColor];

    //3.添加UIView的autoresizing属性
    //testView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin;
    [testView setAutoresizingMask: UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleTopMargin];

    //4.添加自定义view到界面
    [self.view addSubview:testView];
}
  • autoresizing使用过程中的注意事项

    • 一定要先去除use autolayout选项
    • 在使用autoreszing的时候是以父容器做为参照进行布局的。
  • autoresizing的局限性

    • 它只能描述父子控件之间的布局关系,而不能描述子控件与子控件之间的布局关系

autolayout的使用

  • 使用场合:

    • 现在Apple发布的各个产品上市之后,设备的屏幕分辨率也有了很大的变化,iPhone4/4S是3.5寸,iPhone5是4.0寸,ipad2/new ipad/ipad4是9.7寸的,ipad mini是7.0的,分辨率也各不相同,使用传统frame布局的工作量必将越来越大
    • 可以为一个ViewController做出几个不同的尺寸的View来,但是这样会大大的影响到开发的速度
    • autolayout:一种对不同屏幕尺寸有更好兼容的自动布局机制
    • 好处:
      1、你基本上可以不用考虑屏幕不同分辨率的问题,你终于可以不用在viewDidLoad方法里判断不同分辨率下,不同控件应该放在哪里,或者针对不同分辨率写不同的storyboard和xib
      2、你可以抛弃那些根据不同文字来计算tableViewCell、UILabel高度的代码了,因为autolayout会帮你自动计算好
      3、如果你的布局在横屏竖屏下变化不是特别大,你不用再为横着竖着写两套代码或者写两个storyboard/xib了
    • 所以:当你的页面不会变更整体布局和设计,只有在不同屏幕尺寸、不同文字和内容下有适应性的变化,那这种情况使用autolayout就再适宜不过了
  • 设置autolayout的属性面板说明

    • pin面板和align面板:


    • resolve面板:


      Snip20170827_5.png
    • resizing behevior面板:


  • 使用autolayout不再需要设置子控件的frame

    • 通过使用 父子控件之间的约束 或者 子控件与子控件之间的约束进行frame的设置。
    • 添加约束示例:


      Snip20170827_6.png
  • 使用autolayout过程中的注意事项:约束缺失和约束冲突

    • 红色:说明必须的四个约束有缺失,添加缺失的约束就可以
    • 黄色:说明重复添加了约束,可以选择删除其中重复的约束
  • 代码实现autolayout

    • 使用NSLayoutConstraint这个类为子控件添加约束
    • 添加约束的统一语法规律:
      item1.attribute = multiplier ? item2.attribute + constant,
      对应的添加约束的方法:
      [NSLayoutConstraint constraintWithItem:<#(需要添加约束的子控件对象)#> attribute:<#(为子控件的哪一个属性添加约束)#> relatedBy:<#(如何进行约束(如大于,等于...))#> toItem:<#(参照的控件对象)#> attribute:<#(所参照控件的哪一个属性)#> multiplier:<#(倍数)#> constant:<#(增加的常量值)#>];
  • 添加约束的规则:

    • 对于两个同层级view之间的约束关系,添加到它们的父view上
    • 对于两个不同层级view之间的约束关系,添加到他们最近的共同父view上
    • 对于有层次关系的两个view之间的约束关系,添加到层次较高的父view上
  • 代码添加约束示例:

- (void)viewDidLoad {
    
    [super viewDidLoad];
    
    UIView *redView=[[UIView alloc] init];
    
    redView.backgroundColor=[UIColor redColor];
    
    [self.view addSubview:redView];
    
    //添加子控件
    
    UIView *blueView=[[UIView alloc] init];
    
    blueView.backgroundColor=[UIColor blueColor];
    
    [redView addSubview:blueView];
    
    //1.一定要禁用autoresizing
    
    redView.translatesAutoresizingMaskIntoConstraints=NO;
    
    blueView.translatesAutoresizingMaskIntoConstraints=NO;
    
    //2.创建约束
    
    //添加高度约束
    
    NSLayoutConstraint *constriantH=[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0 constant:40];
    
    //设置顶部间距约束
    
    NSLayoutConstraint *constriantT=[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:30];
    
    //设置左边间距约束
    
    NSLayoutConstraint *constriantL=[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
    
    //设置右边间距约束
    
    NSLayoutConstraint *constriantR=[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
    
    //添加蓝色view的约束
    
    NSLayoutConstraint *blueX=[NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:redView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
    
    NSLayoutConstraint *blueH=[NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:redView attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
    
    NSLayoutConstraint *blueR=[NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:redView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
    
    NSLayoutConstraint *blueT=[NSLayoutConstraint constraintWithItem:blueView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:redView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:30];
    
    //添加约束到控件,注意添加的规则
    
    [redView addConstraint:constriantH];
    
    [self.view addConstraint:constriantT];
    
    [self.view addConstraint:constriantL];
    
    [self.view addConstraint:constriantR];
    
    [self.view addConstraint:blueX];
    
    [self.view addConstraint:blueH];
    
    [self.view addConstraint:blueR];
    
    [self.view addConstraint:blueT];
    
}

了解VFL语言的简单说明和使用--不需要掌握
  • 什么是VFL语言:

    • 全称是Visual Format Language,翻译过来是“可视化格式语言”,是苹果公司为了简化Autolayout的编码而推出的抽象语言
  • VFL语言的语法


  • 例如;H:|-间距-[view1对象名]-(>=20)-[view2对象名]

    • H:表示水平方向,V表示垂直方向,不写H/V就表示横向
    • "|"是用来确定view上、下、左、右关系的,如果当前是H,那么就代表水平方向的参照控件的左边或者右边,如果是V就代表参照控件的顶边或者底边
    • 如果是H,这个表达式就可以从左到右描述当前view及与参照view的关系,如果是V,类似。
    • 方括号表示view,圆括号表示尺寸数值。支持大小等于。或者另一个view 如:H:|-[view1(view2)],v1的宽度等于v2
    • 优先级用@表示
    • 上面这句VFL代码的意思就是:view1离父控件左边有一个间距,之后有一个view2控件,view2控件与view1控件有一个>=20的间距。
  • VFL语言的使用

UIView *redView=[[UIView alloc] init];
redView.backgroundColor=[UIColor redColor];
[self.view addSubview:redView];

//添加子控件
UIView *blueView=[[UIView alloc] init];
blueView.backgroundColor=[UIColor blueColor];
[self.view addSubview:blueView];
 
//1.一定要禁用autoresizing
redView.translatesAutoresizingMaskIntoConstraints=NO;
blueView.translatesAutoresizingMaskIntoConstraints=NO;

//2.创建约束

//2.1创建redView的顶部间距约束
NSString *redT=@"V:|-paddingT-[redView]-padding-[blueView]";
NSDictionary *redTmetrics=@{@"paddingT":@40,@"padding":@60};
NSDictionary *redTviews=@{@"redView":redView,@"blueView":blueView};
NSArray *redConT=[NSLayoutConstraint constraintsWithVisualFormat:redT options:0 metrics:redTmetrics views:redTviews];

//2.2创建redView的左右间距约束
NSString *redLR=@"H:|-paddingL-[redView]-paddingR-|";
NSDictionary *redLRmetrics=@{@"paddingL":@10,@"paddingR":@10};
//NSDictionary *redLRviews=@{@"redView":redView};
NSArray *redConLR=[NSLayoutConstraint constraintsWithVisualFormat:redLR options:0 metrics:redLRmetrics views:redTviews];

//2.3创建redView的高度约束
NSString *redH=@"V:[redView(44)]";
NSArray *redConH=[NSLayoutConstraint constraintsWithVisualFormat:redH options:0 metrics:nil views:redTviews];

//添加约束
[self.view addConstraints:redConT];
[self.view addConstraints:redConLR];
[redView addConstraints:redConH];

第三方框架实现autolayout

  • 使用前面两种方式都不是很方便,在公司开发项目的时候一般会使用成熟的第三方框架

  • 我们常用的框架masonry

    • 将框架文件件添加到项目
    • 导入头文件
    • 代码示例:
- (void)viewDidLoad {
    [super viewDidLoad];
    //1.创建红色view
    UIView *redView=[[UIView alloc] init];
    redView.backgroundColor=[UIColor redColor];
    [self.view addSubview:redView];
    //2.创建蓝色view
    UIView *blueView=[[UIView alloc] init];
    blueView.backgroundColor=[UIColor blueColor];
    [self.view addSubview:blueView];
    //3.为红色view添加约束
    [redView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.mas_equalTo(30);//离顶部的距离
        make.left.mas_equalTo(10);//离左边的距离
        make.right.mas_equalTo(-10);//离右边的距离
        make.height.mas_equalTo(44);//高度
    }];
    //4.为蓝色view添加约束
    [blueView mas_makeConstraints:^(MASConstraintMaker *make) {
        //蓝色块离红色块的底部有30的距离
        make.top.mas_equalTo(redView.mas_bottom).offset(30);//离顶部的距离
        //蓝色块的左边与红色块的水平中心对齐
        make.left.mas_equalTo(redView.mas_centerX);//离左边的距离
        make.right.mas_equalTo(-10);//离右边的距离
        make.height.mas_equalTo(redView);//高度
    }];
}
  • 使用autolayout重新布局“微博"小项目。
    • 不需要再使用Frame模型

    • 使用动态单元格进行布局,设置约束


    • 进行约束的连线,根据配图判断是否需要修改约束

if(self.statu.picture)
{
    self.pictureView.hidden=NO;
    self.pictureView.image=[UIImage imageNamed:self.statu.picture];
    self.cellHeight.constant=100;
}
else
{
    self.pictureView.hidden=YES;
    self.cellHeight.constant=0;
}
  • 让tableView高度自动变化
- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.rowHeight=UITableViewAutomaticDimension;
    self.tableView.estimatedRowHeight=44;
}
使用autolayout过程中的注意事项及细节。
  • 一定要禁用autoresizing
  • 一定要将约束添加完整
autolayout和autoresizing的优势:
  • AutoLayout可以指定任意两个view的相对位置,而不需要像Autoresizing Mask那样需要两个view有直接的父子关系。
  • AutoLayout不必须指定相等关系的约束,它可以指定非相等约束(大于或者小于等);而Autoresizing Mask所能做的布局只能是相等条件的。
  • AutoLayout可以指定约束的优先级,计算frame时将优先按照满足优先级高的条件进行计算。
  • Autoresizing Mask是AutoLayout的子集,任何可以用Autoresizing Mask完成的工作都可以用AutoLayout完成。AutoLayout还具备一些Autoresizing Mask不具备的优良特性,以帮助我们更方便地构建界面。

sizeClass的使用

  • sizeClass的由来:
    iOS8之前,公司在开发项目时,先做的iPhone版,然后要求开发iPad版本,其实内容是完全一样的,只是UI变化了,但是我们就需要建立2个工程来分别对应实现.iOS8推出的Size Class,可以让我们在一个工程的storyboard中进行所有尺寸屏幕的适配,不仅是iPhone 4s-5/5s-6-6 Plus,还包括iPad界面.它引入了一种新的概念,抛弃传统意义上我们适配时所谓的具体宽高尺寸,把屏幕的宽和高分别分成两种情况:Compact-紧凑, Regular-正常(Any-任意,其实就是这2种的组合,所以我没分成3种情况)

  • sizeClass能够实现那些效果:解决横屏适配和iPhone iPad共享一个界面

  • any compact regular的含义
    iPhone4S,iPhone5/5s,iPhone6
    竖屏:(w:Compact h:Regular)
    横屏:(w:Compact h:Compact)
    iPhone6 Plus
    竖屏:(w:Compact h:Regular)
    横屏:(w:Regular h:Compact)
    iPad
    竖屏:(w:Regular h:Regular)
    横屏:(w:Regular h:Regular)

  • 具体使用sizeClass

  • 使用sizeClass过程中的注意事项及细节
    *从 iOS8开始才支持 size classes
    *size classes本质就是对所有的屏幕进行了分类, 我们可以为不同类型的屏幕设置不同的约束
    *仅仅是对屏幕进行了分类, 真正排布UI元素还得使用autolayout
    *不再有横竖屏的概念, 只有屏幕尺寸的概念
    *不再有具体尺寸的概念, 只有抽象尺寸的概念
    *把宽度和高度各分为3种情况
    1> any(任意, 表示既可以是compact, 也可以是regular), 一般用 * 表示
    2> compact(紧凑, 小), 一般用 - 表示
    3> regular(正常, 大),
    *注意:
    一般不要在 wAny 和 hAny下设置约束, 否则当在 wAny 和 hAny下设置约束后, 在其他尺寸的屏幕再设置约束会产生冲突。因为约束会被继承下来。

  • 约束的继承关系(*符号就表示+ 或者 -):

* * : 其它8种情况都会继承
* - : 会被- - \ + -继承
+ * : 会被+ - \ + +继承

注意事项:

常见错误

推荐阅读更多精彩内容