iOS Layout 简介

iOS中的Layout可以大概分为手动计算坐标值和使用约束自动布局两种;而自动约束布局最终也是通过引擎计算转为坐标值。

手动计算坐标 Frame

手动计算坐标是iOS布局中最基础的方式。这种方式最直观,UIView底层的CALayer也是使用类似的一套坐标系布局。但是缺点也很明显,对于不同尺寸的设备,通常很难通过一套代码全部适配。

按比例自动调整布局 Autoresizing

在第一代iPad面世后,设备屏幕出现了差异,同时iPad通常要兼容横屏和竖屏两种情况,这时候通过设置frame布局就需要写不同的代码。这时候推出了Autoresizing,使得子视图的布局可以随着父视图自动调整。

Autoresizing是一种按比例布局的方案;因此只能指定与父视图的布局关系

autoresizesSubviews属性是控制是否自动调整子视图的开关,默认为True

自动调整会根据 autoresizingMask 指定的属性枚举值来按比例调整布局;默认为 UIViewAutoresizingNone,表示不会自动调整

1
2
3
4
5
6
7
8
9
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
};
  • None View 的 Frame 不会随 superView 变化
  • LeftMargin 自动调整左边距大小,此时右边距不变;right 相反
  • TopMargin 自动调整上边距大小,此时下边距不变;bottom 相反
  • Height / Width 自动调整宽或高,保持4个边距不变

Mask 是位枚举,因此可以混合使用,效果叠加

自动布局 AutoLayout

AutoLayout 描述的是同一个图层树上的视图之间的布局关系,因此有两个重要的决定因素:

  • 参照物:另一个视图,只要是同一图层树(有共同的祖先)上的就行
  • 约束:描述与参照物之间的位置关系

两个视图之间的约束关系模版为 item1.attribute1 = multiplier × item2.attribute2 + constant
这个模版可以使用两种方式描述:VFL格式和面向对象方式

VFL

VFL(Visual Format Layout)可视化格式布局是最初用来描述AutoLayout的方式,VFL通过字符串创建 NSLayoutConstraint 对象

VFL符号

符号 意义
表示父视图边缘
- 表示距离, eg. -100- 表示两个间隔100
V: 表示竖直方向
H: 表示水平方向
>= 表示视图间距、宽度和高度必须大于或等于某个值
<= 表示视图间距、宽度和高度必须小宇或等于某个值
== 表示视图间距、宽度或者高度必须等于某个值
@num 优先级
[view] [] 中放视图
(num) () 设置视图的宽/高,eg. H:│[view(100)] 表示 view 宽为 100; H:│[view(>=100@751)] 表示宽大于等于100,优先级为751

可以使用NSDictionaryOfVariableBindings宏创建视图名对应视图对象的字典

面向对象描述

iOS中将Autolayout抽象为几个不同的类来表示:

NSLayoutConstraint

描述了两个View之间的布局关系;AutoLayou引擎会根据所描述的布局约束关系转换为视图的Frame 完成布局

NSLayoutRelation

枚举了两个属性之间的关系,有>= / > / = / < / <=

NSLayoutAttribute

枚举了可约束的布局属性

Leading | Trailing 前/后属性,表示当前状态下的开始/结束方向;用于适应rtlltr布局,在ltr时等于左右,否则等于右左
LastBaseLine | FirstBaseLine 基线,主要针对UILabel等视图,基线表示内容(文本)底部/底部的位置;对于大多数视图,基线等于bottom | top
Margin 指各个方向上的边距,等效于 UIView 中设置 layoutMargins 属性
CenterXWithinMargins 指 X 轴方向上两侧的边距,同理 CenterYWithinMargins 指 Y 轴方向上两侧的边距
NotAnAttribute 只有一个Attribute时用这个属性来占位,例如将高度指定为数值

NSLayoutFormatOptions

位枚举了视图布局时的方向、对齐方式、间距(iOS 11+)

对齐方式是位枚举,可以组合使用,使用AlignmentMask = 0xFFFF 做掩码计算坐标属性
方向有LeadingToTrailing | LeftToRight | RightToLeft只能选择其中一个,默认为 DirectionLeadingToTrailing,使用DirectionMask = 0x3 << 16做掩码
间距有 EdgeToEdge | BaselineToBaseline 只能选一个,默认为BaselineToBaseline,使用 SpacingMask = 0x1 << 19 做掩码

UILayoutPriority

布局优先级,用数值表示,最大值为 1000;系统预定义了一些优先级常量

  • UILayoutPriorityRequired 1000;最高优先级
  • UILayoutPriorityDefaultHigh 500;保证内容不会被**压缩 (compressing)**的优先级
  • UILayoutPriorityDefaultLow 250;保证内容不会被拉伸(hugs)的优先级
  • UILayoutPriorityFittingSizeLevel 50;这个优先级表示计算符合内容大小的优先级

通过 NSLayoutConstraintpriority 属性设置优先级;无论是 LayoutConstraint、VFL 还是 Anchor,最终都是转为 NSLayoutConstraint 约束对象,因此都可以设置约束的优先级

抗压缩/拉伸优先级

使用优先级可以决定布局中可以拉伸、压缩的视图,也可以针对不同的布局方向设置及获取抗压缩或拉伸的优先级

  • 抗压缩的优先级
    • setContentHuggingPriority:(UILayoutPriority) forAxis:(UILayoutConstraintAxis)
    • contentHuggingPriorityForAxis:(UILayoutConstraintAxis)
  • 抗拉伸的优先级
    • setContentHuggingPriority:(UILayoutPriority) forAxis:(UILayoutConstraintAxis)
    • contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)

UILayoutConstraintAxis 是坐标轴枚举

锚点描述 LayoutAnchor

为了简化创建Autolayout的方法,在iOS9之后推出了新的自动布局方案–锚点;NSLayoutAnchor是一个创建NSLayoutConstraint的工厂类;它用锚点抽象了 NSLayoutAttribute,使构建自动布局更加方便和简洁

锚点分类

Type Anchor
NSLayoutDimension (尺寸) width / height
NSLayoutXAxisAnchor (X轴) left / right / leading / trailing / centerX
NSLayoutYAxisAnchor (Y轴) top / bottom / firstBaseLine / lastBaseLine / centerY

其他影响Layout的属性

布局操纵 LayoutGuide

UILayoutGuide也是iOS9引入,用于定义一个可以参与自动布局的矩形区域,替代占位视图(dummy view)实现元素之间的等间距效果,或封装界面模块;依赖LayoutAnchor实现

UILayoutGuide不会在在 view 的层级树中展示,但是可以添加view到里面并展示,只是起到布局的作用;使用流程为:

  1. 创建UILayoutGuide对象
  2. 调用UIViewaddLayoutGuide方法添加到View上;意味着guide将以view的坐标作为参照;可以调用UILayoutGuidelayoutFrame属性修改自身的frame
  3. 通过anchor创建约束

UILayoutGuide 当作不能显示的View使用即可

LayoutSupport

UILayoutSupport是一个布局辅助协议;仅仅被UIViewControllertopLayoutGuidebottomLayoutGuide属性实现,用来控制ViewController与导航栏/底部之间布局间隙

顶部和底部都是 UILayoutGuide,分别有自己的顶部/底部锚点以及高度

边距布局 LayoutMargin

边距布局LayoutMargin是iOS8开始提供的一种布局机制,当对一个View设置了起始边距之后,它的所有子视图都会默认的与父视图有一个起始边距

数值型边距

  • UIEdgeInsets layoutMargins 设置所有子视图的默认初始边距
  • NSDirectionalEdgeInsets directionalLayoutMargins 从iOS11开始提供有方向的边距 ,支持rtl

layoutMarginsdirectionalLayoutMargins默认会在所有子视图中都加上这个边距,preservesSuperviewLayoutMargins属性可以阻止这种继承,默认是 NO,也就是可以继承
当边距修改时,会触发回调- layoutMarginsDidChange;子视图可以重写这个方法手动修改自己的边距

占位型边距

  • UILayoutGuide *layoutMarginsGuide
  • UILayoutGuide *readableContentGuide 只读属性,定义了用户不需要操作就可以轻易看见的区域

安全区域 safe area

从iOS11(iPhoneX问世)开始还提供了多个安全区域(不遮挡navigationbar、tabbar、toolbar等视图的区域)相关的api

  • insetsLayoutMarginsFromSafeArea属性,可以自动判断从安全区域开始添加边距,默认YES
  • safeAreaInsets 只读属性,返回安全区域所需的边距

    - safeAreaInsetsDidChange 安全边距触发后的回调

  • UILayoutGuide *safeAreaLayoutGuide 安全区域占位

各种视图边距处理

视图类型iOS 7 ~ iOS 11iOS 11 ~
UIViewController `topLayoutGuide` 和 `bottomLayoutGuide` ,iOS 11 废弃,使用 `view.safeAreaLayoutGuide` 代替`additionalSafeAreaInsets` 管理安全边距
UIView `safeAreaInsets` 和 `safeAreaLayoutGuide`
UIScrollView使用 `UIViewController` 中的 `automaticallyAdjustsScrollViewInsets` 属性可以自动调整 `scrollview` 的边距;常出现 scrollview 部分被遮挡的 BUG,关闭这个属性解决;iOS 11 废弃 使用 `UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior` 属性调整边距
  • Automatic 类似 ScrollableAxes,但是始终会调整竖直方向上的间距
  • ScrollableAxes 只调整滚动的方向;contentSize.width/height > frame.size.width/height or alwaysBounceHorizontal/Vertical = YES
  • Never 不调整
  • Always 根据 safeAreaInsets 的值始终调整 scrollview 的安全区域

adjustedContentInset 属性可以获取实际的 contentInset, contentinset 属性获取的值不再准确

UITableView引入 insetsContentViewsToSafeArea 属性,cell 的 `contentView` 会自动留出默认的安全距离;默认为 `True`
UICollectionView 自定义 `CollectionCell` 时通过 UIView 的相关属性设置安全边距

UICollectionViewFlowLayout 提供了新的枚举属性 sectionInsetReference 来设置整个 collectionView 的安全距离

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2017-2021 HonQi

请我喝杯咖啡吧~