【问题标题】:Creating a UIStackView with percentage values for distribution使用百分比值创建 UIStackView 以进行分配
【发布时间】:2020-04-25 00:30:02
【问题描述】:

我正在尝试创建一个自定义 UIStackView 函数,该函数采用百分比值来填充轴边界。我正在努力正确实施这一点。我已经尝试了一些东西,但它不能正常工作。有没有办法可以实现这个?还是已经有内置的方法?

extension UIStackView {

    func setHeightPercentageFill(values: [CGFloat]) {
        guard values.count == arrangedSubviews.count else { return }
        axis = .vertical
        distribution = .fillProportionally
        for i in 0..<arrangedSubviews.count {
            arrangedSubviews[i].heightAnchor.constraint(equalToConstant: bounds.height * values[i]).isActive = true
        }
    }
}

Desired Usage(将每个子视图的高度设置为 20%、40%、30%、10%)

let stack = UIStackView(arrangedSubviews: [view1, view2, view3, view4])
addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.topAnchor.constraint(equalTo: topAnchor).isActive = true
stack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
stack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true

stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10])

【问题讨论】:

    标签: ios swift uikit uistackview


    【解决方案1】:

    您的代码不起作用,因为 stackView 的大小取决于它的子视图的内在内容大小。你有一个鸡/蛋的场景。 stackView 的高度取决于子视图的高度,子视图的高度取决于 stackView 的高度。您需要告诉 stackView 您想要的高度。

    将高度传递给setHeightPercentageFill(values:) 对我有用:

    extension UIStackView {
        func setHeightPercentageFill(values: [CGFloat], height: CGFloat) {
            guard values.count == arrangedSubviews.count else { return }
            axis = .vertical
            distribution = .fillProportionally
            for i in 0..<arrangedSubviews.count {
                arrangedSubviews[i].heightAnchor.constraint(equalToConstant: height * values[i]).isActive = true
            }
        }
    }
    

    请注意,我在情节提要中将容器高度定义为 128。您需要确保将 stackView 约束到的任何视图都具有固定高度。

    class ViewController: UIViewController {
    
        lazy var view1: UIView = {
            let view = UIView()
            view.translatesAutoresizingMaskIntoConstraints = false
            view.backgroundColor = .red
            return view
        }()
    
        lazy var view2: UIView = {
            let view = UIView()
            view.translatesAutoresizingMaskIntoConstraints = false
            view.backgroundColor = .orange
            return view
        }()
    
        lazy var view3: UIView = {
            let view = UIView()
            view.translatesAutoresizingMaskIntoConstraints = false
            view.backgroundColor = .blue
            return view
        }()
    
        lazy var view4: UIView = {
            let view = UIView()
            view.translatesAutoresizingMaskIntoConstraints = false
            view.backgroundColor = .yellow
            return view
        }()
    
        @IBOutlet weak var container: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let stack = UIStackView(arrangedSubviews: [view1, view2, view3, view4])
            container.addSubview(stack)
            stack.translatesAutoresizingMaskIntoConstraints = false
            stack.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
            stack.leftAnchor.constraint(equalTo: container.leftAnchor).isActive = true
            stack.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
            stack.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true
    
            stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10], height: container.frame.size.height)
        }
    }
    

    【讨论】:

      【解决方案2】:

      此任务有多种方法。您可能会发现这个更灵活一些...

      您可以将排列的子视图的高度约束设置为堆栈视图高度的百分比,而不是调用 func 来计算显式值。

      几个优点:

      • 当堆栈视图的框架发生变化时,可能在设备旋转时,无需重新计算任何内容
      • 您可以通过限制堆栈视图(使用 .heightAnchor 或 .bottomAnchor)来控制堆栈视图的总高度,方法是设置排列的子视图之一的高度
      • 如果堆栈视图高度不是恒定的,您可以可靠地在viewDidLoad() 中设置约束,而无需等待自动布局来确定堆栈视图的高度

      这是一个完整的示例 - 它将在 viewDidAppear()viewWillTransition(to size:...) 上打印排列的子视图的高度到调试控制台以确认:

      extension UIStackView {
          func setHeightPercentageFill(values: [CGFloat]) {
              guard values.count == arrangedSubviews.count else { return }
              axis = .vertical
              spacing = 0
              distribution = .fill
              for i in 0..<arrangedSubviews.count {
                  arrangedSubviews[i].heightAnchor.constraint(equalTo: heightAnchor, multiplier: values[i]).isActive = true
              }
          }
      }
      
      class StackPercentViewController: UIViewController {
      
          let stack: UIStackView = {
              let v = UIStackView()
              v.translatesAutoresizingMaskIntoConstraints = false
              return v
          }()
      
          override func viewDidLoad() {
              super.viewDidLoad()
      
              let colors: [UIColor] = [.red, .green, .blue, .yellow]
      
              colors.forEach {
                  let v = UIView()
                  v.backgroundColor = $0
                  stack.addArrangedSubview(v)
              }
      
              view.addSubview(stack)
      
              // respect safe area
              let g = view.safeAreaLayoutGuide
      
              NSLayoutConstraint.activate([
      
                  // constrain stack view Top / Leading / Trailing at 40-pts
                  stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
                  stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
                  stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
      
              ])
      
              // change "if true" to "if false" to test giving the stack view a height
              if true {
                  // either give one arranged subview a height constraint
                  if let v = stack.arrangedSubviews.first {
                      v.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
                  }
              } else {
                  // or, constrain the stack view's height (.heightAnchor or .bottomAnchor)
                  stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0).isActive = true
                  //stack.heightAnchor.constraint(equalToConstant: 200.0).isActive = true
              }
      
              stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10])
      
          }
      
          override func viewDidAppear(_ animated: Bool) {
              super.viewDidAppear(animated)
              checkHeights()
          }
      
          override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
              super.viewWillTransition(to: size, with: coordinator)
              coordinator.animate(alongsideTransition: nil, completion: {
                  _ in
                  self.checkHeights()
              })
          }
      
          func checkHeights() {
              // print heights of arrangedSubviews to debug console
              stack.arrangedSubviews.forEach {
                  print($0.frame.size.height)
              }
              print() // blank line in console
          }
      
      }
      

      结果...

      第一个子视图高度限制为 20:

      堆栈视图高度限制为 200:

      堆栈视图在所有 4 面都限制为 40 点:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-14
        • 1970-01-01
        • 2014-08-28
        • 1970-01-01
        • 2013-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多