[Android开发学iOS系列] Auto Layout

内容:

  • 介绍什么是Auto Layout.
  • 基本使用方法
    • 在代码中写约束的方法
  • Auto Layout的原理
  • 尺寸和优先级
  • Auto Layout的使用细则
    • 重要的属性
    • StackView
    • Layout Guide
  • Performance
  • Debugging

What is Auto Layout

Auto Layout会根据constraints(约束)动态计算出view hierarchy中所有View的位置和大小.

对于Android开发者来说, Auto Layout很容易上手, 它非常像ConstraintLayoutRelativeLayout: 给View规定它上下左右和谁对齐, 决定UI的位置和大小.

Auto Layout的约束更宽泛一些, 不仅仅是两个View之间的关系, 还有宽高, 比率等设置, 并且可以有一些大于小于等的范围设置.

Auto Layout不是一个View

开始学Auto Layout我还以为它是一个叫AutoLayout的View, 把其他子View包进去然后设置一些放置规则, 就类似于Android的ConstraintLayout或者RelativeLayout.

但是其实不是, AutoLayout不是一个具体的View, 它代表的是一种计算引擎. 因为在代码里你从来不需要写AutoLayout这个关键字, 写的从来都是Constraints.

开发者为View设置足够多的约束, 规定和这个View位置和大小相关的因素, 这个引擎就可以为我们计算出View的位置和大小.

AutoLayout为了解决什么问题

不同屏幕适配; 可以合理应对变化的responsive UI.

改变布局有内外两种因素, 除了屏幕尺寸, 屏幕旋转, 窗口大小改变等外部因素.

内部因素还包含了内容的动态变化, 国际化的支持, 字体的调整等.

和Auto Layout平行的解决方案是什么

摆放UI有三种主要的方法:

  • 在程序里给每个View设置frame.
  • 设置frame, 结合使用autoresizing masks来应对外部变化. (autoresizing mask定义了一个view的frame在它的superview frame变化时应该如何变化.)
  • 使用Auto Layout.

可以看出第二种只是在基于frame的方式上做出了一点改进, 所能应对的也仅仅是外部变化, 有一定的局限性. 所以可以把前两种归类为一种.

这也正是Auto Layout出现之前的解决方案, 即基于frame的布局方式.

Auto Layout的思考点不再着眼于view frame, 而是view的relationship.

如何使用Auto Layout

写iOS的UI有多种方式, Auto Layout属于UIKit, 在写的时候, 可以用storyboard, 也可以直接在代码中写约束.

在storyboard里面有一些好处, 比如所见即所得, 而且ide会给出一些warnings, 比如控件在storyboard上的位置与约束不一致, 会提示, 并且可以选择方式修复.

在storyboard里面写约束确实是不容易出错的一种方式, xcode的操作也很直观, 这里不做演示了.

之前我们也讨论过, 用storyboard写UI存在阅读性差, 代码版本管理和团队合作都有问题等.

所以具体使用需要看实际情况.

关于约束, location和size的约束不能混着用, 这个也是从逻辑上就可以理解的.

比如让某个view的top和parent的top对齐(或者再offset个常量)是可以的, 但是让top等于某个size就不能理解了.

在代码中创建约束

如果不用Interface Builder, 而是选择在代码中创建约束, 那么仍然有多种选择:

  • 使用layout anchor.
  • 使用NSLayoutConstraint类.
  • 使用Visual Format Language.

我们在改变约束的时候通常不会add/remove constraints, 而是active/deactivate.

使用Layout anchor

这个方法可能是最直观的一种方法.

// Get the superview's layout
let margins = view.layoutMarginsGuide // Pin the leading edge of myView to the margin's leading edge
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true // Pin the trailing edge of myView to the margin's trailing edge
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true // Give myView a 1:2 aspect ratio
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0).isActive = true

这里我们把每一条约束设置了isActive = true.

也可以直接放在一个数组里一起activate, 会有性能优势:

NSLayoutConstraint.activate([
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
myView.trailingAnchor.constraint(equalTo: margins.trailingAnchor),
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 2.0)
])

使用NSLayoutConstraint

使用NSLayoutConstraint写起来比较啰嗦, 必须给每个参数都指定值:

NSLayoutConstraint(item: myView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leadingMargin, multiplier: 1.0, constant: 0.0).isActive = true

NSLayoutConstraint(item: myView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailingMargin, multiplier: 1.0, constant: 0.0).isActive = true

NSLayoutConstraint(item: myView, attribute: .height, relatedBy: .equal, toItem: myView, attribute:.width, multiplier: 2.0, constant:0.0).isActive = true

这个不但写起来麻烦, 可读性也很差.

Visual Format Language (VFL)

let views = ["myView" : myView]
let formatString = "|-[myView]-|" let constraints = NSLayoutConstraint.constraints(withVisualFormat: formatString, options: .alignAllTop, metrics: nil, views: views) NSLayoutConstraint.activate(constraints)

用一些键盘符号来表达这个布局的. (like a way of drawing the layout you want with a series of keyboard symbols)

管道符号代表parent view的边边.

Auto Layout的工作原理

图来自于: https://developer.apple.com/videos/play/wwdc2018/220

Render loop包含如上三个阶段:

  • update constraints从叶子节点向上.
  • layout从parent节点向下执行.
  • display即最后的绘制阶段.

这三个阶段对应的方法:

Update Constraints

它的工作是:

  • 把每个公式(约束)加入计算引擎Engine里.
  • 计算引擎负责解出变量: 最后的frame.
  • 通知View: Superview: setNeedsLayout().

engine这里扮演一个layout cache和tracker. 收到变化时它会重新计算.

Layout

从engine得到信息后, Subview setBounds(), subview setCenter().

尺寸和优先级

了解了Auto Layout的原理之后, 看尺寸和优先级的部分就很好理解.

Intrinsic content size

有一些View有固有内容尺寸, 对于AutoLayout来说, 会默认使用intrinsic content size, 这样开发者就不用非得提供尺寸信息.

默认使用: intrinsic content size. 固有内容尺寸.

  • UIImageView: image size.
  • UILabel: text size.

优先级

优先级的值可以从1到1000, 默认是1000.

  • Required: 1000
  • Default High: 750
  • Default Low: 250

有优先级是因为多个constraints之间可能会有冲突, 那么约束的要求可能不能完全100%满足, 计算引擎会在在不能满足的情况下, 尽量地减少偏差.

约束的优先级就用来表示哪条约束我们更加关心, 更想满足, 优先考虑.

优先级相关的变量

  • content hugging priority: 尺寸比固有内容更大的可能性. 默认250. 值越小表示View更愿意扩张来满足约束了; 值越大表示View希望尽可能地接近固有尺寸.
  • content compression resistance priority: 尺寸比固有内容尺寸更小的阻力程度. 默认750. 值越大表示这个View压缩内容的可能性越小.

Auto Layout的使用细则

Properties & Functions

有个重要的属性要提一下:

  • translatesAutoresizingMaskIntoConstraints

这个属性是为了兼容Auto Layout出现之前的基于frame布局的legacy layout系统, 帮助View在Auto Layout的世界里, 以legacy layout system的方式运作.

当这个属性为true, 并且设置了frame时, 引擎会自动生成constraints来满足这个frame.

这个View的属性默认为true. 当我们要用constraints时需要设置为false.

  • 当在storyboard中开始为View设置constraints时, 会自动设置为false.
  • 当我们在代码中给view设置约束之前, 需要自己显式地把这个属性设置为false.

如果还是用frame布局, 这个属性不用设置成false. 比如在循环里生成很多view的时候, 可能想有一些尺寸和位置用frame设置.

  • sizeToFit(): 刚好包裹内容的大小.

Stack View

Stack View是在Auto Layout的基础上的, 帮助我们做一些水平或者垂直的布局, 不用写内部元素间的constraints. (类似于Android中的LinearLayout.)

往Stack View里加需要叠放的元素用的是addArrangedSubview()这个方法.

与此同时, addSubview()方法可以用来加一些别的View.

几个属性:

  • axis: 主轴方向.
  • alignment: 对齐方式.
  • distribution: 沿着主轴的分布.

Stack View是比较轻量的, 所以官方会建议尽量多使用Stack View, 只在有必要的时候写约束.

确实方便很多.

Layout Guide

很多时候为了布局的需要我们可能要包裹View或者是添加一下辅助View, 每个View都有自己的layer, 所以为了改进性能, 我们可以使用Layout Guide.

View自带一个layoutMarginsGuide.

还挺方便的. (看了这个视频: https://www.youtube.com/watch?v=4qPcMGiSADA)

Performance & Building Efficient Layouts

iOS12对AutoLayout的性能做了很多改进, 这个WWDC的talk有讲.

关于有效率的布局, 简而言之就是少做无用功.

Constraint Churn

constraint churning是个典型的性能问题.

churn: 搅动.

constraint churn是指更新了constraints, 但实际上view并不需要移动.

这样是给engine发送了额外的信息, 达到一定数量之后, 就会影响性能.

需要注意的是:

  • 不要remove all constraints然后又add all. 可以把它们分组, 哪些是固定不变的, 那么addView的时候就加上, 然后activate; 对于需要动态变化的部分可以分两组(比如一个根据内容动态决定是否需要显示图片的例子, 可以有两个数组: imageConstraints和noImageConstraints), 单独activate/deactivate这两组约束.
  • 使用isHidden可以提高效率. 比起add/remove Subview来说.

也是WWDC2018/220里提到的, 如何避免Constraint Churn:

  • Avoid removing all constraints
  • Add static constraints once
  • Only change the constraints that need changing
  • Hide views instead of removing them

Size

可以选择性地override一些尺寸, 减少text measure计算的过程:

  • Return size if known without text measurement
  • Use UIView.noIntrinsicMetric and constraints.

System Layout Size Fitting Size

intrinsic content size是view传给engine的.

而这个system layout size fitting size, 是从engine取出来的.

但是它有想不到的性能消耗. (every time you call the method, an engine is created and discarded.)

Debugging

Auto Layout中由约束引起的错误可能会有:

  • 约束自相矛盾(冲突), 不能满足, 无解. (比如一个宽度即等于100又等于200, ???)
  • 约束不足导致有很多可能的解. (Engine会给出一个解, 但可能不是你想要的.)

关于怎么debug可以看: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/TypesofErrors.html#//apple_ref/doc/uid/TP40010853-CH17-SW1

大体上是根据Log还有一些可能有帮助的view的属性和方法(供debug用).

这个视频(https://developer.apple.com/videos/play/wwdc2015/219/)的后半段有讲debug.

这里还有一个小工具网站: https://www.wtfautolayout.com/

Summary

Auto Layout是线性代数的应用实例.

有时候搬砖搬久了是不是应该慢下来欣赏一下数学的美.

References

[Android开发学iOS系列] Auto Layout的更多相关文章

  1. [Android开发学iOS系列] 工具篇: Xcode使用和快捷键

    [Android开发学iOS系列] 工具篇: Xcode使用和快捷键 工欲善其事必先利其器. 编辑 Cmd + N: 新建文件 Option + Cmd + N: 新建文件夹 Cmd + / : 注释 ...

  2. [Android开发学iOS系列] iOS写UI的几种方式

    [Android开发学iOS系列] iOS写UI的几种方式 作为一个现代化的平台, iOS的发展也经历了好几个时代. 本文讲讲iOS写UI的几种主要方式和各自的特点. iOS写UI的方式 在iOS中写 ...

  3. [Android开发学iOS系列] 语言篇: Swift vs Kotlin

    Swift vs Kotlin 这篇文章是想着帮助Android开发快速学习Swift编程语言用的. (因为这个文章的作者立场就是这样.) 我不想写一个非常长, 非常详尽的文章, 只是想写一个快速的版 ...

  4. [Android开发学iOS系列] ViewController

    iOS ViewController 写UIKit的代码, ViewController是离不开的. 本文试图讲讲它的基本知识, 不是很深入且有点杂乱, 供初级选手和跨技术栈同学参考. What is ...

  5. [Android开发学iOS系列] 快速上手UIKit

    快速上手iOS UIKit UIKit是苹果官方的framework, 其中包含了各种UI组件, window和view, 事件处理, 交互, 动画, 资源管理等基础设施支持. 按照前面的介绍, 用U ...

  6. iOS 7 - Auto Layout on iOS Versions prior to 6.0

    链接地址:http://stackoverflow.com/questions/18735847/ios-7-auto-layout-on-ios-versions-prior-to-6-0 Stac ...

  7. Android开发—智能家居系列】(二):用手机对WIFI模块进行配置

    在实际开发中,我开发的这款APP是用来连接温控器,并对温控器进行控制的.有图为证,哈哈. 上一篇文章[Android开发—智能家居系列](一):智能家居原理的文末总结中写到: 手机APP控制智能温控器 ...

  8. iOS Programming Auto Layout: Programmatic Constraints 自动布局:通过编程限制

    iOS Programming  Auto Layout: Programmatic Constraints  1.  However, if your views are created in co ...

  9. iOS 8 Auto Layout界面自动布局系列2-使用Xcode的Interface Builder添加布局约束

    http://blog.csdn.net/pucker/article/details/41843511 上一篇文章<iOS 8界面自动布局系列-1>简要介绍了iOS界面布局方式的前世今生 ...

随机推荐

  1. SpringBoot 注解简介(持续更新)

    虽然工作中交替会使用spring mvc 和spring boot 框架,但实际对spring中的很多注解并不是很了解,本篇将持续更新学习到的spring 注解. Spring 主入口类上的注解 Sp ...

  2. Canvas 线性图形(三):曲线

    前言 画曲线要用到二次贝塞尔曲线或三次贝塞尔曲线.贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如 PhotoShop. 二次贝塞尔曲线 二次贝塞尔曲线在 ...

  3. 高颜值,类似Fliqlo的翻页时钟-BdTab新标签页插件组件

    起因: 很多用户在使用BdTab插件时,反馈说希望添加一个时钟的功能, 而BdTab又是组件模块化的插件,于是在空余时间就用html+js+css写了一款高颜值的分页时钟 源码如下: 需要其他网页组件 ...

  4. ubuntu语言支持打不开,点了没反应

    ubuntu语言支持打不开,点了没反应 sudo dpkg-reconfigure locales

  5. haodoop概念总结

    大数据部门组织结构 Hadoop的优势(4高) 高可靠性:Hadoop底层维护多个数据副本 高扩展性:在集群间分配任务数据,可方便的扩展 高效性:在MapReduce的思想下,Hadoop时并行工作的 ...

  6. Django django-admin.py 命令详解

    一.Django 基本命令 下载 Django pip3 install django     # 默认下载最新版 pip3 install django==4.1  # 手动选择版本 创建Djang ...

  7. k8s实际操作中的小知识点

    1.批量执行yaml文件 # 把所有要执行的yaml文件放在同一个目录下,并且切换到这个目录下 kubectl apply -f . 2.利用pod的亲和和反亲和功能把pod调度到不同的node上 亲 ...

  8. 11. 第十篇 网络组件flanneld安装及使用

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483834&idx=1&sn=b04ec193 ...

  9. es,logstash各版本对应要求的JDK版本,操作系统对应示意图

    官网地址:https://www.elastic.co/cn/support/matrix

  10. Elasticsearch:同步 MongoDB 数据到 Elasticsearch

    转载自:https://elasticstack.blog.csdn.net/article/details/114639152 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写 ...