【问题标题】:Combining constraints with variable and fixed heights on UIView in Swift在 Swift 中的 UIView 上将约束与可变高度和固定高度相结合
【发布时间】:2021-10-02 10:06:43
【问题描述】:

更新:

感谢大家的独特方法。感谢您的见解。


背景:

我在下面的 Xcode Swift 5 中编写了一些代码,它创建了四个大小相等的矩形,这些矩形根据设备大小和方向调整大小。

但是,所需的结果是具有不同高度的矩形,其中顶部矩形的高度取决于设备大小和方向,底部矩形的高度恒定为 200 像素。


问题:

我需要对我的代码进行哪些更改才能实现顶部的可变高度和底部的固定高度?


代码:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let frameTopLeft = UIView()
        frameTopLeft.backgroundColor = .systemRed
        view.addSubview(frameTopLeft)
        frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
            frameTopLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
            frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
            frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
        ])

        let frameTopRight = UIView()
        frameTopRight.backgroundColor = .systemBlue
        view.addSubview(frameTopRight)
        frameTopRight.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
            frameTopRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
            frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
            frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
        ])

        let frameBottomLeft = UIView()
        frameBottomLeft.backgroundColor = .systemGreen
        view.addSubview(frameBottomLeft)
        frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            frameBottomLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
            frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
            frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
        ])

        let frameBottomRight = UIView()
        frameBottomRight.backgroundColor = .systemYellow
        view.addSubview(frameBottomRight)
        frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            frameBottomRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5),
            frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
            frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
        ])

    }

}

图片:

模拟器输出。

想要的输出。

【问题讨论】:

    标签: ios swift optimization constraints nslayoutconstraint


    【解决方案1】:

    使用 StackView 获得更好的代码和理解。

    class ViewController: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let topStack = UIStackView()
            topStack.axis = .horizontal
            topStack.distribution = .fillEqually
            
            let frameTopLeft = UIView()
            frameTopLeft.backgroundColor = .systemRed
            topStack.addArrangedSubview(frameTopLeft)
            
            
            let frameTopRight = UIView()
            frameTopRight.backgroundColor = .systemBlue
            topStack.addArrangedSubview(frameTopRight)
            
            
            let bottomStack = UIStackView()
            bottomStack.axis = .horizontal
            bottomStack.distribution = .fillEqually
            
            let frameBottomLeft = UIView()
            frameBottomLeft.backgroundColor = .systemGreen
            bottomStack.addArrangedSubview(frameBottomLeft)
            
            
            let frameBottomRight = UIView()
            frameBottomRight.backgroundColor = .systemYellow
            bottomStack.addArrangedSubview(frameBottomRight)
            
            frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
            frameBottomRight.heightAnchor.constraint(equalToConstant: 200).isActive = true
            
            let mainStack = UIStackView()
            mainStack.axis = .vertical
            mainStack.addArrangedSubview(topStack)
            mainStack.addArrangedSubview(bottomStack)
            
            self.view.addSubview(mainStack)
            
            mainStack.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                mainStack.topAnchor.constraint(equalTo: view.topAnchor),
                mainStack.heightAnchor.constraint(equalTo: view.heightAnchor),
                mainStack.leftAnchor.constraint(equalTo: view.leftAnchor),
                mainStack.widthAnchor.constraint(equalTo: view.widthAnchor)
            ])
        }
        
    }
    

    或者你可以给每个底视图相对约束。

    class ViewController: UIViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            let frameTopLeft = UIView()
            frameTopLeft.backgroundColor = .systemRed
            view.addSubview(frameTopLeft)
            frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
                //            frameTopLeft.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5), << Here
                frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
                frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
            ])
            
            let frameTopRight = UIView()
            frameTopRight.backgroundColor = .systemBlue
            view.addSubview(frameTopRight)
            frameTopRight.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
                //            frameTopRight.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5), << Here
                frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
                frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
            ])
            
            let frameBottomLeft = UIView()
            frameBottomLeft.backgroundColor = .systemGreen
            view.addSubview(frameBottomLeft)
            frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                frameBottomLeft.topAnchor.constraint(equalTo: frameTopLeft.bottomAnchor), // << Here
                frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                frameBottomLeft.heightAnchor.constraint(equalToConstant: 200), // << Here
                frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
                frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
            ])
            
            let frameBottomRight = UIView()
            frameBottomRight.backgroundColor = .systemYellow
            view.addSubview(frameBottomRight)
            frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                frameBottomRight.topAnchor.constraint(equalTo: frameTopRight.bottomAnchor), // << Here
                frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                frameBottomRight.heightAnchor.constraint(equalToConstant: 200), // << Here
                frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
                frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5)
            ])
        }
    }
    

    【讨论】:

      【解决方案2】:

      您希望为底部视图提供 200 的恒定高度约束,然后将顶部视图的底部约束到底部视图的顶部。

      您可能会发现将相关代码组合在一起并随时添加 cmets 很有帮助(让您相信代码应该做什么更清楚一些)。

      所以,试试这样:

      override func viewDidLoad() {
          super.viewDidLoad()
      
          // create 4 views
          let frameTopLeft = UIView()
          let frameTopRight = UIView()
          let frameBottomLeft = UIView()
          let frameBottomRight = UIView()
      
          // set background colors
          frameTopLeft.backgroundColor = .systemRed
          frameTopRight.backgroundColor = .systemBlue
          frameBottomLeft.backgroundColor = .systemGreen
          frameBottomRight.backgroundColor = .systemYellow
      
          // set Autoresizing and add to view
          [frameTopLeft, frameTopRight, frameBottomLeft, frameBottomRight].forEach { v in
              v.translatesAutoresizingMaskIntoConstraints = false
              view.addSubview(v)
          }
      
          NSLayoutConstraint.activate([
              
              // top-left view constrained Top / Left / 50% Width
              frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
              frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
              frameTopLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
      
              // top-right view constrained Top / Right / 50% Width
              frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
              frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
              frameTopRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
      
              // bottom-left view constrained Bottom / Left / 50% Width
              frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
              frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
              frameBottomLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
      
              // bottom-right view constrained Bottom / Right / 50% Width
              frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
              frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
              frameBottomRight.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
      
              // bottom views, constant Height of 200-pts
              frameBottomLeft.heightAnchor.constraint(equalToConstant: 200.0),
              frameBottomRight.heightAnchor.constraint(equalToConstant: 200.0),
              
              // constrain bottoms of top views to tops of bottom views
              frameTopLeft.bottomAnchor.constraint(equalTo: frameBottomLeft.topAnchor),
              frameTopRight.bottomAnchor.constraint(equalTo: frameBottomRight.topAnchor),
              
          ])
      
      }
      

      【讨论】:

        【解决方案3】:

        我会这样做:

        let frameTopLeft = UIView()
        frameTopLeft.backgroundColor = .systemRed
        view.addSubview(frameTopLeft)
        frameTopLeft.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameTopLeft.topAnchor.constraint(equalTo: view.topAnchor),
            frameTopLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
        ])
        
        let frameTopRight = UIView()
        frameTopRight.backgroundColor = .systemBlue
        view.addSubview(frameTopRight)
        frameTopRight.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameTopRight.topAnchor.constraint(equalTo: view.topAnchor),
            frameTopRight.rightAnchor.constraint(equalTo: view.rightAnchor),
            frameTopRight.leftAnchor.constraint(equalTo: frameTopLeft.rightAnchor),
            frameTopRight.widthAnchor.constraint(equalTo: frameTopLeft.widthAnchor)
        ])
        
        let frameBottomLeft = UIView()
        frameBottomLeft.backgroundColor = .systemGreen
        view.addSubview(frameBottomLeft)
        frameBottomLeft.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameBottomLeft.topAnchor.constraint(equalTo: frameTopLeft.bottomAnchor),
            frameBottomLeft.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            frameBottomLeft.leftAnchor.constraint(equalTo: view.leftAnchor),
            frameBottomLeft.heightAnchor.constraint(equalToConstant: 200),
        ])
        
        let frameBottomRight = UIView()
        frameBottomRight.backgroundColor = .systemYellow
        view.addSubview(frameBottomRight)
        frameBottomRight.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            frameBottomRight.topAnchor.constraint(equalTo: frameTopRight.bottomAnchor),
            frameBottomRight.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            frameBottomRight.rightAnchor.constraint(equalTo: view.rightAnchor),
            frameBottomRight.leftAnchor.constraint(equalTo: frameBottomLeft.rightAnchor),
            frameBottomRight.heightAnchor.constraint(equalTo: frameBottomLeft.heightAnchor),
            frameBottomRight.widthAnchor.constraint(equalTo: frameBottomLeft.widthAnchor),
        ])
        

        与其他人的主要区别是尽可能地摆脱常量。

        1. 您的底视图应该是 200?添加单个 200 约束,并使第二个视图高度等于第一个。
        2. 您可以使左侧宽度等于右侧宽度,而不是使宽度等于父级的 50%

        它使您的代码更易于维护,减少了编辑位置。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-21
          • 1970-01-01
          • 1970-01-01
          • 2017-09-27
          相关资源
          最近更新 更多