Swift - 使用Auto Layout和Size Classes实现页面自适应弹性布局
(本文代码已升级至Swift4)
结合使用 Auto Layout 和 Size Classes 可对UI可视单元的父子关系,兄弟视图关系进行全方位的调整,而且调整精度更高。不仅能确定一个 View 的位置尺寸的变化依据是什么还能对这些依据加以不同的优先级,即先满足什么条件,再满足什么条件,对于重要的位置尺寸给予优先保证,这样整个APP就具有极强的动态可调性,可以满足不同设备,不同应用场景下的需求。
一、Auto Layout(自动布局)
自动布局是 iOS 6 发布后引入的一个全新的布局特性,其目的是弥补以往 autoresizing 的布局方面的不足之处,使未来面对更多尺寸适配时,界面布局可以跟好的适配。
它通过内定的 Constraint(约束)和各项条件来计算出合理的布局,Constraint 的设定非常灵活,实现一种布局的方法可以通过多个 Constraint 套来完成。而不需要设定具体控件坐标 x 多少,y 多少,Frame、Center 和 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 思路的全新抽象,把所有设备(iPhone4、5、6、iPad、Apple Watch)屏幕抽象成屏幕 Size 的变化,把屏幕的宽和高分成三种情况:紧凑(Compact)、任意(Any)和正常(Regular),这样宽和高 3*3 共 9 种布局。
(2)每种 Size 都可以设置特定的一套布局,如果不特殊指定,默认是在(宽任意,高任意)
(3)通过 Size Classes 和 Auto 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”退出编辑模式即可。
Swift 4, Xcode 9发现报错:Cannot convert value of type '[NSLayoutConstraint]' to expected argument type 'NSLayoutConstraint'
额,我没仔细看,抱歉,站长说的真的很管用,谢了
站长,我不会这个面板上您说的那个约束,后来删除了重新加的,但是前面的是一条横杠,其他的组件约束是一个点,这个怎么修改呢?求指教
站长,我拖了一张图片进来,想覆盖全屏幕,但是约束距上下左右都是0,为什么会小了一圈呢?后来必须调成“-20”才可以。。。
站长大人,我不是说你Height写错了,我的意思是说未知设备应该是:Regular Width | Compact Height;
同时想向站长大人讨要点好的类似于"CFPagesVC"的多控制器滑动切换的三方库;
第二大点的第2小节中,Regular Width的Height写错了吧。。。。
着课的代码好像要大规模的更新下了