The size of the button on the stack - swift

I am trying to make my own button size on the stack.
(width: 350, height: 50)
To create a button, I wrote an extension.
But for some reason it does not work out to create in the required size on the stack.
What did I miss?
final class ChooseLanguageView: UIView {
private let ruButton = UIButton.createSystemButton(withTitle: "ru")
private let enButton = UIButton.createSystemButton(withTitle: "en")
private let deButton = UIButton.createSystemButton(withTitle: "de")
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [ruButton,
enButton,
deButton])
stackView.axis = .vertical
return stackView
}()
private func setupStackView() {
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: centerYAnchor)
])
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .lightGray
setupStackView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
// MARK: - extension
private extension UIButton {
static func createSystemButton(withTitle title: String) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.backgroundColor = .white
button.frame.size = CGSize(width: 350, height: 50) // Here I am trying to adjust the size
return button
}
}
Here's what comes out:

You need to add button width and height as constraints
private extension UIButton {
static func createSystemButton(withTitle title: String) -> UIButton {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.backgroundColor = .white
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: 350),
button.heightAnchor.constraint(equalToConstant: 50)
])
return button
}
}

Related

Unable to simultaneously satisfy constraints of InputAccessoryView

I have an InputAccessoryView with a single button in. However, I am getting the following output.
I have set up my InputAccessoryView in my ViewController as below;
lazy var customInputView: ButtonInputView = {
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 50)
let view = ButtonInputView(frame: frame)
view.doneButton.addTarget(self, action: #selector(signIn), for: .touchUpInside)
return view
}()
override var inputAccessoryView: UIView? {
return customInputView
}
Xcode is breaking the height constraint of 50 set in the frame of my view. Any reason why??
CUSTOM ACCESSORY INPUT VIEW CLASS
class ButtonInputView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var doneButton: UIButton = {
let view = UIButton()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = Color.accent
view.setTitle("Done", for: .normal)
view.titleLabel?.font = Font.mainButton
view.layer.cornerRadius = 19
return view
}()
let separator: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .opaqueSeparator
return view
}()
private func setUpView() {
backgroundColor = Color.background
addSubview(doneButton)
addSubview(separator)
let separatorHeight = (1 / (UIScreen.main.scale))
if let titleLabelText = doneButton.titleLabel?.text {
doneButton.widthAnchor.constraint(equalToConstant: titleLabelText.width(usingFont: Font.mainButton) + 32).isActive = true
}
NSLayoutConstraint.activate([
separator.topAnchor.constraint(equalTo: topAnchor),
separator.leadingAnchor.constraint(equalTo: leadingAnchor),
separator.trailingAnchor.constraint(equalTo: trailingAnchor),
separator.heightAnchor.constraint(equalToConstant: separatorHeight),
doneButton.topAnchor.constraint(equalTo: separator.bottomAnchor, constant: 6),
doneButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
doneButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -6),
])
}
override var intrinsicContentSize: CGSize {
return CGSize.zero
}
override func didMoveToWindow() {
super.didMoveToWindow()
if #available(iOS 11.0, *) {
if let window = window {
bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor,
multiplier: 1.0).isActive = true
}
}
}
}

Why don't my selector functions get called when I clicked on my radio buttons in my Xcode Swift project

I have a custom UIView class called SortView with two radio buttons and neither of their respective selector functions are being called when I click on them. I have added an instance of the SortView class to a parent class with view.addView(). Here is my custom class:
class SortView: UIView {
// MARK: - Properties
lazy var driverSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handleDriverSortRadioButton), for: .touchUpInside)
return button
}()
lazy var pickupTimeSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handlePickupTimeSortRadioButton), for: .touchUpInside)
return button
}()
private let driverSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by driver:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let pickupTimeSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by pickup time:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let driverSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "Louise"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private let pickupTimeSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "2:00pm"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private enum radioButtonStates {
case driver
case pickupTime
}
private var radioButtonState = radioButtonStates.driver
// MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Selectors
#objc func handleDriverSortRadioButton() {
print("DEBUG: driver sort radio button clicked")
if radioButtonState != .driver {
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
pickupTimeSortTextField.isEnabled = false
pickupTimeSortTextField.text = ""
radioButtonState = .driver
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
driverSortTextField.isEnabled = true
driverSortTextField.text = ""
}
}
#objc func handlePickupTimeSortRadioButton() {
print("DEBUG: pickup time sort radio button clicked")
if radioButtonState != .pickupTime {
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
driverSortTextField.isEnabled = false
driverSortTextField.text = ""
radioButtonState = .pickupTime
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
pickupTimeSortTextField.isEnabled = true
pickupTimeSortTextField.text = ""
}
}
// MARK: - Helper Functions
private func configureUI() {
let driverSortStackView = UIStackView(arrangedSubviews: [driverSortRadioButton,
driverSortTitleLabel,
driverSortTextField])
driverSortStackView.axis = .horizontal
driverSortStackView.distribution = .fill
driverSortStackView.spacing = 5
let pickupTimeSortStackView = UIStackView(arrangedSubviews: [pickupTimeSortRadioButton,
pickupTimeSortTitleLabel,
pickupTimeSortTextField])
pickupTimeSortStackView.axis = .horizontal
pickupTimeSortStackView.distribution = .fill
pickupTimeSortStackView.spacing = 5
let stackView = UIStackView(arrangedSubviews:[driverSortStackView,
pickupTimeSortStackView])
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
self.addSubview(stackView)
stackView.centerX(inView: self)
stackView.centerY(inView: self)
}
}
The functions which are not being called are handleDriverSortRadioButton and handlePickupTimeSortRadioButton. Here is instantiation:
private lazy var sortView = SortView()
and here is where I use it in my parent UIViewController class:
view.addSubview(sortView)
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32)
I'm going to guess that the problem is that a containing view (perhaps one of the stack views) has zero size. The result would be that its subviews are visible but not tappable.
Here is a debugging utility method you can use to track down this sort of thing:
extension UIView {
#objc func reportSuperviews(filtering:Bool = true) {
var currentSuper : UIView? = self.superview
print("reporting on \(self)\n")
while let ancestor = currentSuper {
let ok = ancestor.bounds.contains(ancestor.convert(self.frame, from: self.superview))
let report = "it is \(ok ? "inside" : "OUTSIDE") \(ancestor)\n"
if !filtering || !ok { print(report) }
currentSuper = ancestor.superview
}
}
}
Wait until your interface is all set up and the buttons are untappable, and then call that on one of the untappable buttons to get a report in the console.
Fixed it by adding with and height to the bottom stack view
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32,
width: view.frame.width,
height: 100)

How to remove mysterious top margin in UIStackView when `isLayoutMarginsRelativeArrangement=true`?

I'm creating a simple "toolbar" component with a horizontal axis UIStackView. It looks fine, except when I switch on isLayoutMarginsRelativeArrangement, a strange margin is added to the top above the items, making the height of the stack view incorrect.
I've tried giving the stack view directionalLayoutMargins property many different values, including no value at all. Yet still this unwanted spacing remains. Why does this margin exist and how can I remove it?
override func viewDidLoad() {
super.viewDidLoad()
self.stackView = UIStackView(frame: CGRect.zero)
self.stackView.axis = .horizontal
self.stackView.alignment = .center
self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
self.stackView.isLayoutMarginsRelativeArrangement = true
self.stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(self.stackView)
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: view.topAnchor),
self.stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
self.stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
self.stackView.heightAnchor.constraint(equalTo: view.heightAnchor)
])
let title = UILabel(frame: .zero)
title.text = "My Toolbar"
self.stackView.addArrangedSubview(title)
title.sizeToFit()
let button = MDCButton()
button.setTitle("Recipes", for: .normal)
button.applyContainedTheme(withScheme: containerScheme)
button.minimumSize = CGSize(width: 64, height: 48)
self.stackView.addArrangedSubview(button)
}
Here's a couple things to try...
First, StackBarViewControllerA which creates a newView (plain UIView) to hold the stack view with the label and button:
class StackBarViewControllerA: UIViewController {
var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
let newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = .cyan
view.addSubview(newView)
self.stackView = UIStackView(frame: CGRect.zero)
self.stackView.axis = .horizontal
self.stackView.alignment = .center
self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
self.stackView.isLayoutMarginsRelativeArrangement = true
self.stackView.translatesAutoresizingMaskIntoConstraints = false
newView.addSubview(self.stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: newView.topAnchor),
self.stackView.bottomAnchor.constraint(equalTo: newView.bottomAnchor),
self.stackView.widthAnchor.constraint(equalTo: newView.widthAnchor),
self.stackView.heightAnchor.constraint(equalTo: newView.heightAnchor),
newView.topAnchor.constraint(equalTo: g.topAnchor),
newView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
newView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
])
let title = UILabel(frame: .zero)
title.text = "My Toolbar"
self.stackView.addArrangedSubview(title)
title.sizeToFit()
let button = UIButton() // MDCButton()
button.setTitle("Recipes", for: .normal)
button.backgroundColor = .blue
button.heightAnchor.constraint(equalToConstant: 48).isActive = true
//button.applyContainedTheme(withScheme: containerScheme)
//button.minimumSize = CGSize(width: 64, height: 48)
self.stackView.addArrangedSubview(button)
}
}
Second, StackBarViewControllerB using a custom StackBarView:
class StackBarView: UIView {
var stackView: UIStackView!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
self.stackView = UIStackView(frame: CGRect.zero)
self.stackView.axis = .horizontal
self.stackView.alignment = .center
self.stackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
self.stackView.isLayoutMarginsRelativeArrangement = true
self.stackView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.stackView)
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: self.topAnchor),
self.stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
self.stackView.widthAnchor.constraint(equalTo: self.widthAnchor),
self.stackView.heightAnchor.constraint(equalTo: self.heightAnchor)
])
let title = UILabel(frame: .zero)
title.text = "My Toolbar"
self.stackView.addArrangedSubview(title)
title.sizeToFit()
let button = UIButton() // MDCButton()
button.setTitle("Recipes", for: .normal)
button.backgroundColor = .blue
//button.applyContainedTheme(withScheme: containerScheme)
//button.minimumSize = CGSize(width: 64, height: 48)
button.widthAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true
button.heightAnchor.constraint(greaterThanOrEqualToConstant: 48).isActive = true
button.setContentHuggingPriority(.defaultHigh, for: .horizontal)
self.stackView.addArrangedSubview(button)
}
}
class StackBarViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = StackBarView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .cyan
view.addSubview(v)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: g.topAnchor),
v.leadingAnchor.constraint(equalTo: g.leadingAnchor),
v.trailingAnchor.constraint(equalTo: g.trailingAnchor),
])
}
}
Both give this result (I gave it a cyan background so we can see the frame):
Looks like the problem is your anchor settings. Try removing your bottomAnchor and set your heightAnchor to the size you want for your buttons plus some padding, instead of just matching the heightAnchor of view:
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: view.topAnchor),
// self.stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
self.stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
// self.stackView.heightAnchor.constraint(equalTo: view.heightAnchor),
self.stackView.heightAnchor.constraint(equalToConstant: 80)
])
Note: You may need to set an offset constant for your top anchor like: self.stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50)

How to fix UITableView not displaying in UIStackView

I followed a tutorial to build a weather app programmatically (without a storyboard) that displays the current city and temperature. I am modifying it to display a 5 day forecast instead of just the current temperature by adding a UITableView, but it is not showing up.
Here is my WeatherView code:
class WeatherView: UIView, UITableViewDelegate, UITableViewDataSource {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
setupViews()
setupConstraints()
}
private func setupViews() {
self.addSubview(mainStack)
conditionsImageStack.addArrangedSubview(conditionsImageView)
mainStack.addArrangedSubview(conditionsImageStack)
// forecastTable.register(UITableView.self, forCellReuseIdentifier: "forecast")
forecastTable.register(UITableViewCell.self, forCellReuseIdentifier: "forecast")
forecastTable.delegate = self
forecastTable.dataSource = self
mainStack.addArrangedSubview(centerContentStack)
// centerContentStack.addArrangedSubview(temperatureLabel)
centerContentStack.addArrangedSubview(forecastTable)
centerContentStack.addArrangedSubview(cityAndConditionsStack)
cityAndConditionsStack.addArrangedSubview(cityLabel)
cityAndConditionsStack.addArrangedSubview(conditionsLabel)
mainStack.addArrangedSubview(buttonsStack)
buttonsStack.addArrangedSubview(celsiusButton)
buttonsStack.addArrangedSubview(fahrenheitButton)
buttonsStack.addArrangedSubview(UIView(frame: .zero))
}
private func setupConstraints() {
mainStack.pinEdges(to: self)
}
let mainStack: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.spacing = 10
stackView.isLayoutMarginsRelativeArrangement = true
stackView.layoutMargins = UIEdgeInsets(top: 10, left: 30, bottom: 30, right: 30)
return stackView
}()
let conditionsImageStack: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.alignment = .trailing
stackView.spacing = 10
return stackView
}()
let cityAndConditionsStack: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.distribution = .fillProportionally
stackView.spacing = 10
return stackView
}()
let centerContentStack: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.distribution = .fillProportionally
stackView.alignment = .center
stackView.spacing = 60
return stackView
}()
// TABLE CODE HERE
var animalArray : [String] = ["elephant", "pig", "goat"]
var forecastTable: UITableView = {
let tableView = UITableView()
let estimatedHeight = tableView.numberOfRows(inSection: 3) //You may need to modify as necessary
// let width = parentView.frame.size.width
let width = estimatedHeight
tableView.frame = CGRect(x: 0, y: 0, width: width, height: estimatedHeight)
return tableView
}()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = forecastTable.dequeueReusableCell(withIdentifier: "forecast")
cell?.textLabel!.text = "Success"
return cell!
}
let temperatureLabel: UILabel = {
let label = UILabel(frame: .zero)
label.font = UIFont.systemFont(ofSize: 144)
label.textColor = .white
label.textAlignment = .center
label.text = "18°"
return label
}()
let cityLabel: UILabel = {
let label = UILabel(frame: .zero)
label.font = UIFont.systemFont(ofSize: 36)
label.textColor = .white
label.textAlignment = .center
label.text = "Atlanta"
return label
}()
let conditionsLabel: UILabel = {
let label = UILabel(frame: .zero)
label.font = UIFont.systemFont(ofSize: 20)
label.textColor = .white
label.textAlignment = .center
label.text = "Sunny"
return label
}()
let conditionsImageView: UIImageView = {
let image = UIImage(named: "sun")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
imageView.widthAnchor.constraint(equalToConstant: image!.size.width).isActive = true
imageView.heightAnchor.constraint(equalToConstant: image!.size.height).isActive = true
return imageView
}()
let celsiusButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("°C", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 73)
button.setTitleColor(.white, for: .normal)
return button
}()
let fahrenheitButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("°F", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 73)
button.setTitleColor(.white, for: .normal)
return button
}()
let buttonsStack: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.axis = .horizontal
stackView.distribution = .equalCentering
stackView.spacing = 10
return stackView
}()
}
Here is my ViewController code:
class WeatherViewController: UIViewController {
var mainView: WeatherView! { return self.view as! WeatherView }
let presenter: WeatherPresenter!
init(with presenter: WeatherPresenter){
self.presenter = presenter
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init coder not implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
updateBackground()
}
func updateBackground() {
self.mainView.updateGradient(presenter.backgroundColor)
}
override func loadView() {
self.view = WeatherView(frame: UIScreen.main.bounds)
}
}
Here is my UIView + Constraints code:
extension UIView {
func pinEdges(to view: UIView){
self.translatesAutoresizingMaskIntoConstraints = false
self.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
self.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
self.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
I have tried forcing it to display by setting the dimensions with tableView.frame = CGRect(x: 0, y: 0, width: width, height: estimatedHeight) but that did not work.
I am registering the table in the view class instead of of the view controller class, but I am not sure if this is the problem or how to modify it correctly.
You're doing a couple things wrong...
First, in your var forecastTable: UITableView = {...} declaration, you have a line:
let estimatedHeight = tableView.numberOfRows(inSection: 3)
But, at that point, the table view has no dataSource -- even if it did, your dataSource has only section. So the value returned is undefined. If Iprint(esttmatedHeight)I get9223372036854775807. So you are trying to set the frame of your table view to9223372036854775807 x 9223372036854775807`
Next, when you add a table view to a stack view, the stack view will try to arrange it based on the stack view's properties and the table view's intrinsic size. However, the table view has no intrinsic size at that point - you must use auto-layout properties.
So, remove the frame setting for your table view, and after you've added it to the stack view, use:
// I'm guessing you want a height based on number of rows * row height?
// so, something like:
let estimatedHeight = CGFloat(3 * 44)
forecastTable.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
forecastTable.widthAnchor.constraint(equalTo: centerContentStack.widthAnchor),
forecastTable.heightAnchor.constraint(equalToConstant: estimatedHeight),
])
That will make the table view the same width as the centerContentStack that holds it, and give it a height.
At this point, you should see your table.
You are adding mainStack to the superView but not specifying it's dimensions. You need to set constrains to mainStack similar this,
mainStack.translatesAutoresizingMaskIntoConstraints = false
mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
mainStack.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainStack.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
Above code fills the entire super view with mainStack. You can modify this code according to your requirement.

UIViewController as rootViewController of UINavigationController causes root views buttons to move

When I do this in AppDelegate:
window?.rootViewController = {
let mainController = MenuViewController()
return mainController
}()
I get this:
But when I do this in AppDelegate:
window?.rootViewController = {
let mainController = UINavigationController(rootViewController: MenuViewController())
return mainController
}()
I get this:
Why and how do I fix? Please specify which information if more information is needed.
Here is the MenuView code that lays out the buttons manually and also sets up the properties of the buttons:
class MenuView: UIView {
//title
let titleLabel: UILabel = {
let label = UILabel()
label.text = "Survive The Attackers!!"
label.backgroundColor = UIColor.white
return label
}()
//set up buttons
let newGameButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("New Game", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
let resumeButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Resume Game", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
let highScoresButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("High Scores", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
//add subviews and initialize the view
override init(frame: CGRect){
super.init(frame: frame)
self.backgroundColor = UIColor(patternImage: UIImage(named: "background1.png")!)
addSubview(titleLabel)
addSubview(newGameButton)
addSubview(resumeButton)
addSubview(highScoresButton)
}
required init?(coder aDecoder: NSCoder) {
fatalError("It's Apple. What did you expect?")
}
//manually layout the main menu
override func layoutSubviews() {
var cursor: CGPoint = .zero
let buttonHeight = CGFloat(40.0);
let buttonWidth = CGFloat(160.0);
let labelWidth = buttonWidth + 20;
let spacing = bounds.height/4
let titleY = 2/3 * spacing
cursor.y = titleY
cursor.x = bounds.width/2 - labelWidth/2
titleLabel.frame = CGRect(x: cursor.x, y: cursor.y, width: labelWidth, height: buttonHeight)
cursor.y = spacing
cursor.x = bounds.width/2 - buttonWidth/2
newGameButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
cursor.y += spacing
resumeButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
cursor.y += spacing
highScoresButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
}
The buttons are laid out manually in layoutSubviews
Here is my MenuView controller code:
class MenuViewController: UIViewController {
var delegateID: String = UUIDVendor.vendUUID()
private var menuView: MenuView {
return view as! MenuView
}
init(){
super.init(nibName: nil, bundle: nil)
//edgesForExtendedLayout = .init(rawValue: 0)
}
required init?(coder aDecoder: NSCoder){
fatalError()
}
//loads the view in and sizes it correctly
override func loadView() {
view = MenuView()
//extendedLayoutIncludesOpaqueBars = false
}
override func viewDidLoad() {
menuView.newGameButton.addTarget(self, action: #selector(MenuViewController.newGameButtonTapped(button:)), for: .touchUpInside)
menuView.resumeButton.addTarget(self, action: #selector(MenuViewController.resumeGameButtonTapped(button:)), for: .touchUpInside)
menuView.highScoresButton.addTarget(self, action: #selector(MenuViewController.highScoreButtonTapped(button:)), for: .touchUpInside)
menuView.setNeedsLayout()
}
//fuction that handles the event when the newGameButton is tapped
#objc func newGameButtonTapped(button: UIButton){
//reset the data in the model somehow
navigationController?.pushViewController(GameViewController(), animated: true)
}
//function that handles the event when the resume game button is tapped
#objc func resumeGameButtonTapped(button: UIButton){
}
//function that handels the event when the high scores button is tapped
#objc func highScoreButtonTapped(button: UIButton){
}
call super for layoutSubviews
private var menuView: MenuView = {
let vw = MenuView()
return vw
}()
override func viewDidLoad() {
super.viewDidLoad()
view = MenuView() //Add here
//Your code
}
And remove loadView() from MenuViewController