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])")
}
}
我正在尝试将 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])")
}
}