当前位置: > > > Swift - 使用Auto Layout和Size Classes实现页面自适应弹性布局

Swift - 使用Auto Layout和Size Classes实现页面自适应弹性布局

(本文代码已升级至Swift4)

    在过去只有 iphone4 的时候,可以在代码里将一个可视单元的位置写死,这样是没问题的,但随着 iPhone56 的发布,屏幕尺寸有了越来越多种可能。这就要求 App UI 控件具有在不同屏幕尺寸的设备上具有一定动态的可调性,实现较好的UI展示效果。
    
    结合使用 Auto LayoutSize Classes 可对UI可视单元的父子关系,兄弟视图关系进行全方位的调整,而且调整精度更高。不仅能确定一个 View 的位置尺寸的变化依据是什么还能对这些依据加以不同的优先级,即先满足什么条件,再满足什么条件,对于重要的位置尺寸给予优先保证,这样整个APP就具有极强的动态可调性,可以满足不同设备,不同应用场景下的需求。

一、Auto Layout(自动布局)

自动布局是 iOS 6 发布后引入的一个全新的布局特性,其目的是弥补以往 autoresizing 的布局方面的不足之处,使未来面对更多尺寸适配时,界面布局可以跟好的适配。
它通过内定的 Constraint(约束)和各项条件来计算出合理的布局,Constraint 的设定非常灵活,实现一种布局的方法可以通过多个 Constraint 套来完成。而不需要设定具体控件坐标 x 多少,y 多少,FrameCenter autoresing 等。

1,在StoryBoard中使用自动布局

我们以如下的界面来演示(可以看到由于自动布局,不管在横屏还是竖屏都很好的实行屏幕自适应)
 

(1)先拖入一个 Text Filed 并设置约束(距上方距离为 20,距左右距离为 5

(2)拖入一个 Button 并设置约束(距下方距离为 5,水平居中)

(3)拖入一个 Text View 并设置约束(距上方即 TextField 距离为 20,距下方即 Button 距离为 20,距左右两侧为 5

(4)在左侧的列表栏目可以看到对应生成的约束,同时如果约束设置不正确会显示红色圆圈
 

(5)而选择具体的 View 组件,在右边的 Size Inspector 控制面板中可以进行约束的修改和删除。

2,使用纯代码来实现自动布局

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //创建组件
        let textField = UITextField(frame: .zero) //这里不再需要刻意制定x,y坐标
        textField.borderStyle = UITextBorderStyle.roundedRect
        self.view.addSubview(textField)
        
        let button = UIButton(type: .system)
        button.setTitle("按钮", for: .normal)
        button.backgroundColor = UIColor(red: 55/255, green: 186/255, blue: 89/255,
                                         alpha: 0.5)
        self.view.addSubview(button)
        
        let textView = UITextView(frame: .zero)
        textView.text="hangge.com"
        textView.backgroundColor = UIColor(red: 55/255, green: 186/255, blue: 89/255,
                                           alpha: 0.5)
        self.view.addSubview(textView)
        
        //使用Auto Layout的方式来布局
        textField.translatesAutoresizingMaskIntoConstraints = false
        button.translatesAutoresizingMaskIntoConstraints = false
        textView.translatesAutoresizingMaskIntoConstraints = false
        
        //创建一个控件数组
        let views:[String:AnyObject] = ["textField": textField,
                                        "button": button, "textView": textView]
        
        //创建一个水平居中约束(按钮)
        let constraint:NSLayoutConstraint = NSLayoutConstraint(
            item: button, attribute: .centerX, relatedBy: .equal,
            toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 0.0)
        self.view.addConstraint(constraint)
        //创建水平方向约束
        self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-5-[textField]-5-|", options:.alignAllCenterX,
            metrics: nil, views: views))
        self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "H:|-5-[textView]-5-|", options: .alignAllCenterX,
            metrics: nil, views: views))
        //创建垂直方向约束
        self.view.addConstraints(NSLayoutConstraint.constraints(
            withVisualFormat: "V:|-20-[textField]-20-[textView]-20-[button]-20-|",
            options:NSLayoutFormatOptions.alignAllCenterX, metrics: nil, views: views))
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

二、Size Classes(适配各类型的屏幕)

1,说明

(1)iOS8 中新增了 Size Classes 特性,是对老式 UI 思路的全新抽象,把所有设备(iPhone456iPadApple Watch)屏幕抽象成屏幕 Size 的变化,把屏幕的宽和高分成三种情况:紧凑(Compact)、任意(Any)和正常(Regular),这样宽和高 3*3 9 种布局。
(2)每种 Size 都可以设置特定的一套布局,如果不特殊指定,默认是在(宽任意,高任意)
(3)通过 Size ClassesAuto Layout 的结合,可以很完美适配各种不同的分辨率。先用 Size Classes 将屏幕分类后,再用 Auto Layout 执行布局。

2,Size Classes 主要的几种设备类型

  • Compact Width | Regular Height:竖屏 iPhone
  • Compact Width | Compact Height:横屏 iPhone 4/4s 5/5s 6/6s
  • Regular Width | Compact Height:横屏 iPhone 6 plus/6s plus
  • Regular Width | Regular Height:任意方向的 iPad
  • Any Width | Any Height:任意宽高

3,使用样例

比如我要把一个按钮放在页面的最下方并居中。但要求在竖屏下按钮宽度自适应(距两边框 5 像素),而在横屏下按钮使用固定宽度(150 像素)

效果图如下:
                 

(1)首先我们分别设置按钮离左、右、下边框距离的约束。默认情况下做的所有操作都是适配所有的设备类型和方向(即 wAny hAny)。

(2)通过下方的 SizeClasses 面板可以切换显示在不同设备、不同方向下的布局效果。可以看到不管横屏还是竖屏,按钮都是自适应撑满整个屏幕。

(3)为了给按钮单独设置在横屏下的约束。我们先选中 size 面板里的横屏,然后点击“Vary for Traits”按钮。

(4)在弹出的选项框中勾选上 Height 后便进入编辑模式。该模式下会发现整个 size 面板变蓝,同时左侧会显示当前适配的所有设备(这里时当 iPhone 横屏时)。也就是说当前所有的操作都只针对 iphone 横屏的情况(不管是设置约束,还是添加控件)。

(5)首先我们要把之前针对全局的左右距离约束给删除。先选择按钮,在约束面板中分别点击左边距约束、右边距约束,然后按下 delete 键将它们删除。

(6)删除后看到左侧树这边相关的约束变成半透明的了,说明这两个约束已经不对当前模式起作用。

(7)我们先不退出编辑模式。接着给按钮添加个固定宽度约束,和水平居中约束。
        

(8)完成后点击“Done Varying”退出编辑模式即可。
评论7
  • 7楼
    2018-01-04 11:39
    哈哈

    Swift 4, Xcode 9发现报错:Cannot convert value of type '[NSLayoutConstraint]' to expected argument type 'NSLayoutConstraint'

    站长回复

    这篇文章写的比较早,我现在把代码更新了,你再试试看呢。

  • 6楼
    2016-04-25 10:16
    linjoe

    额,我没仔细看,抱歉,站长说的真的很管用,谢了

    站长回复

    不客气:)

  • 5楼
    2016-04-25 09:57
    linjoe

    站长,我不会这个面板上您说的那个约束,后来删除了重新加的,但是前面的是一条横杠,其他的组件约束是一个点,这个怎么修改呢?求指教

    站长回复

    现在搞定了吗?

  • 4楼
    2016-04-20 10:46
    linjoe

    站长,我拖了一张图片进来,想覆盖全屏幕,但是约束距上下左右都是0,为什么会小了一圈呢?后来必须调成“-20”才可以。。。

    站长回复

    你设置约束的时候,把面板上的“constrain to margins”勾给去掉。

  • 3楼
    2016-04-13 15:07
    Felix_泡泡

    站长大人,我不是说你Height写错了,我的意思是说未知设备应该是:Regular Width | Compact Height;
    同时想向站长大人讨要点好的类似于"CFPagesVC"的多控制器滑动切换的三方库;

    站长回复

    哦,这个原来写的是有些问题,现已修正。我目前还不知道其他类似CFPagesVC的第三方库,以后如果有碰到好用的,我会写篇相关文章的。

  • 2楼
    2016-04-08 17:44
    Felix_泡泡

    第二大点的第2小节中,Regular Width的Height写错了吧。。。。

    站长回复

    Height没有拼错啊?

  • 1楼
    2015-10-20 16:25
    木头人

    着课的代码好像要大规模的更新下了

    站长回复

    代码已更新,支持Swfit2.0