UIStackView 不显示

UIStackView not showing

我正在尝试将 UIStackView 添加到我的 window,但它没有显示。如下所示,我已经成功添加了一个 UINavigationBar。我还尝试添加各种其他控件,但实际上只显示了另一个 UIView。我附上了我的 ViewController class:


class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {


    var pickerData: [String] = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        pickerData = ["Select a segment..."]

        let guide = view.safeAreaLayoutGuide
        let safeHeight = guide.layoutFrame.size.height
        let safeWidth = guide.layoutFrame.size.width

        let navBar = UINavigationBar(frame: CGRect(x: 0, y: safeHeight, width: 20, height: 44))

        self.view.addSubview(navBar)

        navBar.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
        navBar.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true

        navBar.topAnchor.constraint(equalTo: guide.topAnchor, constant: 0).isActive = true

        navBar.translatesAutoresizingMaskIntoConstraints = false

        let navItem = UINavigationItem(title: "Add Binge Segment")
        let cancelItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.cancel, target: nil, action: #selector(cancel))
        navItem.leftBarButtonItem = cancelItem

        navBar.setItems([navItem], animated: false)


        var items:[UIView] = [UIView]()

        let picker = UIPickerView(frame: CGRect(x: safeWidth, y: navBar.frame.height + 50, width: 320, height: 230))
        picker.showsSelectionIndicator = true
        picker.delegate = self
        picker.dataSource = self

        items.append(picker)

        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.distribution = .fillProportionally
        stackView.alignment = .fill
        stackView.spacing = 10
        stackView.backgroundColor = .green
        stackView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(stackView)

        stackView.topAnchor.constraint(equalTo: navBar.bottomAnchor, constant: 50).isActive = true
        stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 50).isActive = true

        stackView.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: 0).isActive = true
        stackView.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 0).isActive = true

        stackView.addArrangedSubview(picker)


    }

    @objc func cancel() {

        print("Clicked")

    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 0
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }

}


关于为什么没有显示的任何想法?我看到的大多数答案都建议添加约束,但我已经有了它们

编辑:新视图控制器:


class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {


    var pickerData: [String] = [String]()

    var guide: UILayoutGuide = UILayoutGuide()
    var safeHeight: CGFloat = 0.0
    var safeWidth: CGFloat = 0.0

    var navBar: UINavigationBar = UINavigationBar()
    var stackView: UIStackView = UIStackView()

    var picker: UIPickerView = UIPickerView()

    var currentSelection: String = ""

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.

        pickerData = ["Select a segment..."]

        guide = view.safeAreaLayoutGuide
        safeHeight = guide.layoutFrame.size.height
        safeWidth = guide.layoutFrame.size.width

        navBar = UINavigationBar(frame: CGRect(x: 0, y: safeHeight, width: 20, height: 44))

        self.view.addSubview(navBar)

        picker = UIPickerView(frame: CGRect(x: safeWidth, y: navBar.frame.height + 50, width: 320, height: 230))

        self.view.addSubview(picker)

        stackView.axis = .vertical
        stackView.distribution = .fillProportionally
        stackView.alignment = .fill
        stackView.spacing = 10
        self.view.addSubview(stackView)




    }

    override func viewWillLayoutSubviews() {

        navBar.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
        navBar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
        navBar.topAnchor.constraint(equalToSystemSpacingBelow: guide.topAnchor, multiplier: 0)

        navBar.translatesAutoresizingMaskIntoConstraints = false

        let navItem = UINavigationItem(title: "Add Binge Segment")
        let cancelItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.cancel, target: nil, action: #selector(cancel))
        navItem.leftBarButtonItem = cancelItem

        navBar.setItems([navItem], animated: false)


        var items:[UIView] = [UIView]()

        picker.showsSelectionIndicator = true
        picker.delegate = self
        picker.dataSource = self


        items.append(picker)

        stackView.translatesAutoresizingMaskIntoConstraints = false

        stackView.topAnchor.constraint(equalTo: navBar.bottomAnchor, constant: 50).isActive = true
        stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 50).isActive = true

        stackView.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: 0).isActive = true
        stackView.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 0).isActive = true

        stackView.addArrangedSubview(picker)
    }

    @objc func cancel() {

        print("Clicked")

    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 0
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }

    // The data to return fopr the row and component (column) that's being passed in
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerData[row]
    }

    // Capture the picker view selection
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        // This method is triggered whenever the user makes a change to the picker selection.
        // The parameter named row and component represents what was selected.

        currentSelection = pickerData[row]
    }

您需要将布局配置移动到 viewWillLayoutSubviews,因为视图的框架尚未正确设置

这里有一个很好的post解释View Controller生命周期

https://medium.com/@felicity.johnson.mail/viewcontroller-lifestyle-e304e1078bc5

首先,使用约束时无需指定帧。让自动布局引擎为您完成工作。

其次,约束都可以在viewDidLoad()中设置——你不需要在viewWillLayoutSubviews()中设置任何东西。

第三,不太清楚您希望选择器视图显示在哪里,或者为什么要将它放在堆栈视图中?我假设您希望选择器从导航栏底部下方 50 磅处开始,并且您将向堆栈视图添加其他元素?

所以,一些提示:

  • 在开发过程中,为视图提供对比色背景,以便在 运行-time
  • 时轻松查看帧
  • 使用 Debug View Hierarchy 帮助找出错误/缺失的约束条件
  • 按逻辑关联对代码进行分组。如
    • 实例化对象
    • 设置对象属性
    • 添加子视图
    • 设置约束

这是我得到的:

对您的代码进行这些修改:

class BenDentViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    var pickerData: [String] = [String]()

    // instantiate navBar, stackView and picker
    var navBar: UINavigationBar = UINavigationBar()
    var stackView: UIStackView = UIStackView()
    var picker: UIPickerView = UIPickerView()

    var currentSelection: String = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // sample picker data
        pickerData = [
            "Select a segment...",
            "Segment 1",
            "Segment 2",
            "Segment 3",
            "Segment 4",
        ]

        // set navBar properties
        let navItem = UINavigationItem(title: "Add Binge Segment")
        let cancelItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.cancel, target: self, action: #selector(cancel))
        navItem.leftBarButtonItem = cancelItem
        navBar.setItems([navItem], animated: false)

        // set picker properties
        picker.showsSelectionIndicator = true
        picker.delegate = self
        picker.dataSource = self

        // give it a background color so we can see its frame
        picker.backgroundColor = .yellow

        // set stackView properties
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.alignment = .fill
        stackView.spacing = 10

        // add the picker to the stack view
        stackView.addArrangedSubview(picker)

        // add elements to self.view
        self.view.addSubview(navBar)
        self.view.addSubview(stackView)

        // we're using auto-layout constraints
        [navBar, stackView, picker].forEach {
            [=10=].translatesAutoresizingMaskIntoConstraints = false
        }

        let guide = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // constrain navBar to top, leading, trailing and height of 44
            navBar.topAnchor.constraint(equalToSystemSpacingBelow: guide.topAnchor, multiplier: 0),
            navBar.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 0),
            navBar.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: 0),
            navBar.heightAnchor.constraint(equalToConstant: 44.0),

            // constrain stackView leading and trailing
            stackView.trailingAnchor.constraint(equalTo: guide.trailingAnchor, constant: 0),
            stackView.leadingAnchor.constraint(equalTo: guide.leadingAnchor, constant: 0),

            // constrain stackView top: 50-pts from bottom of navBar
            stackView.topAnchor.constraint(equalTo: navBar.bottomAnchor, constant: 50),

            // not needed, depending on desired layout
            //stackView.bottomAnchor.constraint(equalTo: guide.bottomAnchor, constant: -50),

            ])

    }

    @objc func cancel() {
        print("Cancel Clicked")
    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return pickerData.count
    }

    // The data to return fopr the row and component (column) that's being passed in
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return pickerData[row]
    }

    // Capture the picker view selection
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        // This method is triggered whenever the user makes a change to the picker selection.
        // The parameter named row and component represents what was selected.

        currentSelection = pickerData[row]

        print("Selected row: \(row) value: \(pickerData[row])")
    }

}