重学AutoLayout (2) — UIStackView

StackView 的要点

  1. UIStackView 仅仅一个容器!!!(它本身没有intrinsic content size)
  2. UIStackView中不同的 distributions 特色作业的原理不一样!!!(fill & fillEqual 表现是不同的!)
  3. 加入到UIStackView中的元素有必要具有Intrinsic Content Size

ScrollView中嵌套StackView

其间的几个要害:

  1. ScrollView的Anchor 配备 ScrollView的 ViewPort
  2. ScrollView内部的StackView的Anchor也需要 Pin到 ScrollView的巨细
  3. StackView中的Row有必要有IntrinsicContentSize
  4. Row可以强制 horizontal 方向的捆绑 override IntrinsicContent.width
{
  func setupViews() {
    navigationItem.title = "Scrollable"
    
    let stackView = makeStackView(withOrientation: .vertical)
    let scrollView = makeScrollView()
    
    // ScrollView 内部是 stackView
    scrollView.addSubview(stackView)
    view.addSubview(scrollView)
    
    for _ in 1...30 {
      let row = RowView()
      stackView.addArrangedSubview(row)
      
      // 操控内部控件捆绑外部view, 强行拉伸宽度
      row.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
    }
    
    // Pinning to the sides of view
    stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    
    // Pinning scrollview
    scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
    scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
  }
}
​
// 自定义 raow
class RowView: UIView {
​
  override init(frame: CGRect) {
    super.init(frame: frame)
​
    setupViews()
  }
​
  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
​
  func setupViews() {
    let titleLabel = makeLabel(withText: "Gapless Playback")
    let onOffSwith = makeSwitch(isOn: true)
​
    addSubview(titleLabel)
    addSubview(onOffSwith)
​
    // titleLabel
    titleLabel.topAnchor.constraint(equalTo: topAnchor).isActive = true
    titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
    
    // offSwitch 
    onOffSwith.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
    onOffSwith.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor).isActive = true
  }
​
  // 内置 Intrinsic ContentSize -> 给StackView 供给参看!!!
  override var intrinsicContentSize: CGSize {
    return CGSize(width: 200, height: 31)
  }
  
}
​

StackView的运用总结

  1. 假设我们Pin一个StackView到某些边距的side时, 留意StackView中内容的变化(与center StackView的效果必定不同)

  2. UIStackView处理的方针有必要具有Intrinsic Size, 因而假设要引入一些自定义方针, 需要重写IntrinsicContentSize办法.

  3. 在轴向上,除了stackView.distribution = .fillEqually以外, 其他的 distribution type, stackview 运用arrangedViewsintrinsicContentSize来核算stack axis的标准!!! (这样我们大约能核算出 StackView 在没有其他的外部捆绑时, 轴向的length).

    1. fillEqually 会使得一切arrangeViews在轴向上具有相同标准的捆绑!!
  4. 在交叉轴上, 除了 stackView.alignment = .fill 以外, 其他的 alignment type, stackview 也是运用 arrangedViewsintrinsicContentSize 的交叉轴的巨细. (这样我们大约能核算出 StackView 在没有其他的外部捆绑时, 交叉轴向的length).

    1. .alignment = .fill时, stack 视图将拉伸其一切处理的视图来匹配其交叉轴中 arrangeViews 的最大的固有标准
  5. 在运用StackView时, 除了处理有效内容往后, 也可以经过Anchor添加一些 StackSpacerView, 协助我们批改不同arrangeSubviews 之间的距离!!!

  6. 运用StackView时, 假设需要在交叉轴拉伸StackView, 有两种常见的办法, 这样required priority 的Anchor能 Override Intrinsic Size的捆绑:

    1. 直接给StackView设置widthAnchor
    2. 给ArrangeView设置一个交叉轴的Anchor, Anchor 需要在 outside View上.
  7. 嵌套StackView的设计方案, 一定是从内到外结合CRPCHP!!!! 具体可以参看 Apple官方的Auto Layout CookBook

下面是一个StackSpacerView的实例:

// 创立一个 spacerView!!! 专门用于添加控件
public func makeSpacerView(height: CGFloat? = nil) -> UIView {
  let spacerView = UIView(frame: .zero)
​
  // 添加它的 heightAchor 并且是 breakable()
  if let height = height {
    spacerView.heightAnchor.constraint(equalToConstant: height).setActiveBreakable()
  }
  spacerView.translatesAutoresizingMaskIntoConstraints = false
  return spacerView
}
​
// 这儿的服务, 表明当 priority 是 breakable 服务
public extension NSLayoutConstraint {
  @objc
  func setActiveBreakable(priority: UILayoutPriority = UILayoutPriority(900)) {
    self.priority = priority
    isActive = true
  }
}

参看

  1. www.cnblogs.com/tieria/p/45…
  2. www.jianshu.com/p/633e238b2…
  3. github.com/HChong3210/…