首先这个布局概念是我在WWDC中看到的,这是iOS开发设计师在iOS9提出的一个概念。之所以要写下来,是我认为在日常使用中很常用到。感谢它给我带来的改变。
简单来说,是通过块元素操作布局。这样做的好处,是把原本一整个页面的约束线,通过控件的关联性整合到一个块结构中,方便我们频繁的操作管理错综复杂的布局结构。
对于stackview,本身也有属性可以帮助我们友好的管理操作块内元素。更建议在使用Storyboard时使用,当然也可以用代码封装成更便利的调用方式。
它的家庭成员
“我把它分为config和arranged!!”
config
axis来自NSLayoutConstraint扩展
public enum Axis : Int, @unchecked Sendable {
case horizontal = 0
case vertical = 1
}
alignment对齐方式
public enum Alignment : Int, @unchecked Sendable {
case fill = 0
case leading = 1
public static var top: UIStackView.Alignment { get }
case firstBaseline = 2 // Valid for horizontal axis only
case center = 3
case trailing = 4
public static var bottom: UIStackView.Alignment { get }
case lastBaseline = 5 // Valid for horizontal axis only
}
distribution分布/分配方式
public enum Distribution : Int, @unchecked Sendable {
case fill = 0
case fillEqually = 1
case fillProportionally = 2
case equalSpacing = 3
case equalCentering = 4
}
spacing
原文:Spacing between adjacent edges of arrangedSubviews.Used as a strict spacing for the Fill distributions, and as a minimum spacing for the EqualCentering and EqualSpacing distributions. Use negative values to allow overlap.
解释:排列子视图的间距。用作Fill的严格间距;用作EqualCentering和EqualSpacing的最小间距使用。负值会重叠子控件。
arranged
addArrangedSubview //添加
removeArrangedSubview //删除
insertArrangedSubview //插入
为什么把它们称作家庭成员?
就是每个属性对应都是独特的,互相都有不同的作用,在相互搭配的过程中也有不同的效果,千万不要弄错。
参考
它的作用
在通常情况下,当我们设计静态界面。对于某些有规律的流线型界面结构时(如TableView、CollectionView),但又不属于列表排列的结构(它没有可复用的Cell View),它可能里面有Label、Button、TextView等,并进行横纵方向混合排列的方式,都可以使用UIStackView。
既然背靠着NSLayoutConstraint,不妨试试简单创建一个UIStackView内部为我们做了什么?
UIStackView().link
.config(.vertical, alignment: .center, distribution: .equalSpacing, spacing: 10)
.arranged([titleLab, iconImgv])
.base
很明显,当我们将控件放入UIStackView中,Apple自动为我们处理优先级和约束关系。而在日后的工作中,这不仅仅为我们省去了布局的时间,还对UI管理和更新起到了非常大的作用。这一点我觉得和声明式有一点像,同样是对属性进行函数封装,以及Apple帮我们做细微的调整,达到用极少的代码描述一段复杂的命令。
优先级
“StackView Size - StackView Config - Space - Subview View”
如下图,在没有设置UIStackView Size的情况下,子控件都是遵循原始尺寸,并且StackView也是自动根据子控件来适应大小
如下图,图1:设置一个大于原始大小的Size,会造成StackView拉伸(拉伸尺度取决于Size);图2:设置一个小于原始大小的Size,会造成子控件压缩
如下图,是关于align中leading、trailing的示例
如下图,当我们将space值增加,StackView会随之增大,这里不会改变子控件大小。
假如UIStackView Size属性被设置,会随之改变UIStackView内优先级较低(也就是约束条件少,更容易拉伸的子控件)
如下图,图1高度均分,产生的间距,会导致子控件压缩height;图2没有间距均分内容高度。
设置Size时会以压缩和拉伸处理,牺牲子控件Size来遵循space的间距,此时space优先级更高;假如解除Size束缚,SubView View和space优先级一样
StackView Size优先级一定大于config优先级,意思就是你们首先得听我的,如果我没有Size需求再按照你们的设置自己划分,其次是space,它的优先级根据分布方式来决定(上面有讲到),优先级最小的就是子控件。这一切的目的就是帮助在已知条件的UIstackView中,确定子控件的约束环境,有点编写动态约束的意味。当子控件的约束条件更明确,StackView能做的就越少,以优先级判定的影响就越小,但这不妨碍我们进行StackView布局。
Alignment与Distribution
“它们是共存的”
UIStackView可以和约束同时使用,这也就意味着以alignment和distribution的调整对于子控件约束来说是共存的。
axis很好理解,horizontal为行布局方式;vertical为列布局方式。
在此前提下,还需要考虑alignment对齐属性,对齐方式分为:
leading(顶部)、trailing(尾部)、center(居中)、fill(填充后)
还有两个仅限于horizontal布局
lastBaseline居顶部后,子控件底部平齐
horizontal居底部后,子控件顶部平齐
在对齐方式选择后,需要考虑分布方式,常用分为:
- fill - 填充分配
- fillEqually - 子控件平均分配
- fillProportionally - 按子控件比列分配
- equalSpacing - 按等间距分配
- equalCentering - 按子控件中心线等间距分配
使用alignment事项:
fill会使原本正常合理的内容产生一定程度的拉伸,horizontal拉伸height,vertical拉伸width
fillEqually会在space存在的情况下均分,一定是等均匀的,此时space优先级为最高
fillProportionally子控件会在内部自动瞄定优先级,从999开始依次减1
equalSpacing和equalCentering的情况下space只能保证最小间距,config优先级大于space
更多心得
当不设置UIStackView大小的时候,使用fill和fillProportionally表达的一样,一旦设置大小且,比例分配就会根据子控件内容设置比例与优先级。fill会根据自身的大小来分布,所以我们需要参考第一优先级,也就是UIStackView Size。
我们可以给UIStackView设定位置约束和大小约束,从而管理UIStackView内容的布局与大小。
StackView可以解决很多复杂布局问题,通过StackView嵌套StackView使它更具活力,更多时候我们还是以UIStackView Config为主,而减少在它内部定义子控件的约束
其实放进来的子控件会根据frame影响约束分配,当原控件设置的约束条件越明确时,视图的优先级越高,改动就会越小。
要理解UIStackViiew内部如何自动布局,最重要的是理解它的约束条件中的优先级。
如果需要一些案例的,可以在评论留言,后期整理好后我会放在git仓库中~~