简单的UIStackView布局的思维

510 阅读6分钟

首先这个布局概念是我在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  //插入

为什么把它们称作家庭成员

就是每个属性对应都是独特的,互相都有不同的作用,在相互搭配的过程中也有不同的效果,千万不要弄错。

参考

屏幕快照 2020-05-18 下午4.24.19.png

它的作用

在通常情况下,当我们设计静态界面。对于某些有规律的流线型界面结构时(如TableView、CollectionView),但又不属于列表排列的结构(它没有可复用的Cell View),它可能里面有Label、Button、TextView等,并进行横纵方向混合排列的方式,都可以使用UIStackView。

既然背靠着NSLayoutConstraint,不妨试试简单创建一个UIStackView内部为我们做了什么?

UIStackView().link
  .config(.vertical, alignment: .center, distribution: .equalSpacing, spacing: 10)
  .arranged([titleLab, iconImgv])
  .base

Xnip2022-07-05_16-32-54.jpg

很明显,当我们将控件放入UIStackView中,Apple自动为我们处理优先级和约束关系。而在日后的工作中,这不仅仅为我们省去了布局的时间,还对UI管理和更新起到了非常大的作用。这一点我觉得和声明式有一点像,同样是对属性进行函数封装,以及Apple帮我们做细微的调整,达到用极少的代码描述一段复杂的命令。

优先级

“StackView Size - StackView Config - Space - Subview View”

如下图,在没有设置UIStackView Size的情况下,子控件都是遵循原始尺寸,并且StackView也是自动根据子控件来适应大小

Xnip2022-07-05_17-41-40.jpg

如下图,图1:设置一个大于原始大小的Size,会造成StackView拉伸(拉伸尺度取决于Size);图2:设置一个小于原始大小的Size,会造成子控件压缩

Xnip2022-07-05_17-42-46.jpg

如下图,是关于align中leading、trailing的示例

Xnip2022-07-05_17-44-12.jpg

如下图,当我们将space值增加,StackView会随之增大,这里不会改变子控件大小。

假如UIStackView Size属性被设置,会随之改变UIStackView内优先级较低(也就是约束条件少,更容易拉伸的子控件)

Xnip2022-07-05_17-45-30.jpg

如下图,图1高度均分,产生的间距,会导致子控件压缩height;图2没有间距均分内容高度。

设置Size时会以压缩和拉伸处理,牺牲子控件Size来遵循space的间距,此时space优先级更高;假如解除Size束缚,SubView View和space优先级一样

Xnip2022-07-05_17-45-57.jpg

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

equalSpacingequalCentering的情况下space只能保证最小间距,config优先级大于space

更多心得

当不设置UIStackView大小的时候,使用fill和fillProportionally表达的一样,一旦设置大小且,比例分配就会根据子控件内容设置比例与优先级。fill会根据自身的大小来分布,所以我们需要参考第一优先级,也就是UIStackView Size。
​
我们可以给UIStackView设定位置约束和大小约束,从而管理UIStackView内容的布局与大小。
​
StackView可以解决很多复杂布局问题,通过StackView嵌套StackView使它更具活力,更多时候我们还是以UIStackView Config为主,而减少在它内部定义子控件的约束
​
其实放进来的子控件会根据frame影响约束分配,当原控件设置的约束条件越明确时,视图的优先级越高,改动就会越小。
​
要理解UIStackViiew内部如何自动布局,最重要的是理解它的约束条件中的优先级。

如果需要一些案例的,可以在评论留言,后期整理好后我会放在git仓库中~~