Programmatically create stack view - swift

I am trying to create a date component by using a stack view programmatically. If user enters wrong date an error label will display message accordingly.Its working fine if I create it through storyboard, but when I use my programmatically created component which has added all elements in a stack view. I am not able to see error label.
I verified the autolayout I've added in storyboard and in my component class, both look similar. Here is code for my component.
class CustomDateView: UIView {
// MARK: - Variable
private let dayTextField: UITextField = {
let inputField = UITextField()
inputField.borderStyle = .none
inputField.layer.cornerRadius = 8.0
inputField.layer.borderColor = UIColor.darkGray.cgColor
inputField.backgroundColor = UIColor.blue
inputField.textAlignment = .center
inputField.tag = 0
inputField.translatesAutoresizingMaskIntoConstraints = false
return inputField
}()
private let monthTextField: UITextField = {
let inputField = UITextField()
inputField.borderStyle = .none
inputField.layer.cornerRadius = 8.0
inputField.layer.borderColor = UIColor.darkGray.cgColor
inputField.backgroundColor = UIColor.blue
inputField.textAlignment = .center
inputField.tag = 1
inputField.translatesAutoresizingMaskIntoConstraints = false
return inputField
}()
private let yearTextField: UITextField = {
let inputField = UITextField()
inputField.borderStyle = .none
inputField.layer.cornerRadius = 8.0
inputField.layer.borderColor = UIColor.darkGray.cgColor
inputField.backgroundColor = UIColor.blue
inputField.textAlignment = .center
inputField.tag = 2
inputField.translatesAutoresizingMaskIntoConstraints = false
return inputField
}()
private let errorLabel: UILabel = {
let errorLabel = UILabel()
errorLabel.textColor = .red
errorLabel.textAlignment = .center
errorLabel.numberOfLines = 0
errorLabel.translatesAutoresizingMaskIntoConstraints = false
return errorLabel
}()
private let monthSeparator: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let yearSeparator: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let horizontalStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 16.0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private let verticalStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8.0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
// MARK: - Initialisers
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
// MARK: - Private Functions
private func setup() {
self.horizontalStackView.addArrangedSubview(self.dayTextField)
self.horizontalStackView.addArrangedSubview(self.monthSeparator)
self.horizontalStackView.addArrangedSubview(self.monthTextField)
self.horizontalStackView.addArrangedSubview(self.yearSeparator)
self.horizontalStackView.addArrangedSubview(self.yearTextField)
self.verticalStackView.addArrangedSubview(self.horizontalStackView)
self.verticalStackView.addArrangedSubview(self.errorLabel)
self.addSubview(self.verticalStackView)
self.translatesAutoresizingMaskIntoConstraints = false
let selfType = type(of: self)
NSLayoutConstraint.activate([
self.dayTextField.heightAnchor.constraint(equalToConstant: 48.0),
self.dayTextField.widthAnchor.constraint(equalToConstant: 62.0),
self.monthTextField.widthAnchor.constraint(equalToConstant: 62.0),
self.monthSeparator.widthAnchor.constraint(equalToConstant: 14.0),
self.yearSeparator.widthAnchor.constraint(equalToConstant: 14.0)
])
NSLayoutConstraint.activate([
self.verticalStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),
self.verticalStackView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0),
self.bottomAnchor.constraint(equalTo: self.verticalStackView.bottomAnchor),
self.trailingAnchor.constraint(equalTo: self.verticalStackView.trailingAnchor)
])
}
}
It render view correctly but after setting error label stack view won't grow to display the text.I have given fixed width to my separators and textfield's cause I want them to be of that exact width and height.
Is there any mistakes I'm making while applying autolayout in here.

Related

Displaying property with Horizontal and Vertical Stack View in Swift

I am new to swift . I am following the programmatic approach to create the view . I created two stack view . One is horizontal and other one is vertical. Into horizontal stack view I want to display the label property and Vertical stack view I want to display the Image. I want to display the image on left side and label properties on right side.
Here is the code I used ..
import UIKit
class PeopleCell: UITableViewCell {
static let identifier = "PeopleCell"
private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .leading
return stackView
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.distribution = .fill
stackView.alignment = .center
return stackView
}()
private lazy var lastnameTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var firstnameTitleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textAlignment = .center
return label
}()
private lazy var peopleImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
// imageView.backgroundColor = .blue
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureCell(firstName: String, lastName: String) {
firstnameTitleLabel.text = "Firstname :\(firstName)"
lastnameTitleLabel.text = "Lastname : \(lastName)"
}
func configureImageCell(row: Int, viewModel: ViewModel) {
peopleImageView.image = nil
viewModel
.downloadImage(row: row) { [weak self] data in
let image = UIImage(data: data)
self?.peopleImageView.image = image
}
}
private func setUpUI() {
stackView.addArrangedSubview(lastnameTitleLabel)
stackView.addArrangedSubview(firstnameTitleLabel)
mainStackView.addArrangedSubview(peopleImageView)
mainStackView.addArrangedSubview(stackView)
contentView.addSubview(mainStackView)
// constraints
let safeArea = contentView.safeAreaLayoutGuide
mainStackView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
mainStackView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
mainStackView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 10).isActive = true
mainStackView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -10).isActive = true
peopleImageView.heightAnchor.constraint(equalToConstant: 140).isActive = true
peopleImageView.widthAnchor.constraint(equalToConstant: 140).isActive = true
stackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor).isActive = true
}
}
Here is the screenshot ..
Here is the expected result.
The main problem is that you have your .axis properties reversed.
You want your mainStackView.axis to be .horizontal and your stackView.axis to be .vertical.
Also, these two lines are not needed (and cause problems):
// don't do this
//stackView.leadingAnchor.constraint(equalTo: mainStackView.leadingAnchor).isActive = true
//stackView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor).isActive = true
As a side note, instead of:
let safeArea = contentView.safeAreaLayoutGuide
you may want to use:
let safeArea = contentView.layoutMarginsGuide
which gives you the default cell margins.
Edit
// this is the "main" stack view with
// the image on the left
// the labels on the right
// so it needs to be HORIZONTAL
private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
//stackView.axis = .vertical
stackView.axis = .horizontal
stackView.distribution = .fill
return stackView
}()
// this is the "labels" stack view with
// two (or more) labels from top down
// so it needs to be VERTICAL
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
//stackView.axis = .horizontal
stackView.axis = .vertical
stackView.distribution = .fill
return stackView
}()

StackView with two labels doesn't wrap content

I have one StackView with two labels inside. I want to place the two labels one next to the other.
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .center
stackView.axis = .horizontal
stackView.backgroundColor = .black
stackView.spacing = 10
return stackView
}()
// MARK: Labels
private lazy var firstLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor.brandColor(.white)
label.textAlignment = .right
label.font = UIFont.bloomSpeakFont(.titleUltraHeavy, ofSize: 8.0)
return label
}()
private lazy var secondLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.textColor = UIColor.brandColor(.white)
label.textAlignment = .right
label.font = UIFont.bloomSpeakFont(.bodyExtraBold, ofSize: 10.0)
return label
}()
// MARK: - Setup
private func setupStackView() {
stackView.addArrangedSubview(firstLabel)
//firstLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
//firstLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
//firstLabel.setContentHuggingPriority(UILayoutPriority.defaultLow, for: .horizontal)
stackView.addArrangedSubview(secondLabel)
//secondLabel.widthAnchor.constraint(equalToConstant: 100).isActive = true
//secondLabel.heightAnchor.constraint(equalToConstant: 100).isActive = true
//secondLabel.setContentHuggingPriority(UILayoutPriority.defaultHigh, for: .horizontal)
addSubview(stackView)
stackView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
My problem is:
What I want:
The label next to the number and also some padding some padding with the margins.
Thanks in advance.
EDIT
After set spacing to 0, numberOfLines to 1, stackView.alignment = .fill I got the result bellow. I just wonder how to add padding to the labels?
Thanks to the comments of #SandeepBhandari and #DonMag i could solved my issue:
The final solution was:
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.alignment = .fill // << changed from .center to .fill
stackView.axis = .horizontal
stackView.backgroundColor = .black
//stackView.spacing = 10 << removed line
return stackView
}()
// MARK: Labels
private lazy var firstLabel: PaddingLabel = {
let label = PaddingLabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 1 // << changed from 0 to 1
label.textColor = UIColor.brandColor(.white)
label.textAlignment = .right
label.font = UIFont.bloomSpeakFont(.titleUltraHeavy, ofSize: 8.0)
return label
}()
private lazy var secondLabel: PaddingLabel = {
let label = PaddingLabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 1
label.textColor = UIColor.brandColor(.white)
label.textAlignment = .right
label.font = UIFont.bloomSpeakFont(.bodyExtraBold, ofSize: 10.0)
return label
}()
// MARK: - Setup
private func setupStackView() {
stackView.addArrangedSubview(firstLabel)
stackView.addArrangedSubview(secondLabel)
firstLabel.paddingLeft = 2
firstLabel.paddingRight = 1
secondLabel.paddingLeft = 1
secondLabel.paddingRight = 2
addSubview(stackView)
stackView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
I also use this guide https://johncodeos.com/how-to-add-padding-in-uilabel-in-ios-using-swift/, to add padding to the labels.
The final result is:

Cell not displaying elements when being reused on a stack that adds a LinkPresentation view

I had this custom view who worked like a charm before i introduce a LinkView for a Metadata
After i introduce a LinkView, since it was inside a stackView i had to remove linkView from superview when preparing for reusable (not sure why tried to redraw layout, but seems this not work with LinkView) the problems shows up when scrolling down elements, seems the data get lost at certain point, curious thing is that it only happens with the reusable element that contains the linkView item, is there any reason for this ? How can i fix it ?
Here is the code i use for the cell
final class TimeLineTableViewCell: UITableViewCell {
var cornerRadius: CGFloat = 6
var shadowOffsetWidth = 0
var shadowOffsetHeight = 3
var shadowColor: UIColor = .gray
var shadowOpacity: Float = 0.3
lazy var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.addSubview(stackViewContainer)
let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
view.layer.cornerRadius = cornerRadius
view.clipsToBounds = true
view.layer.masksToBounds = false
view.layer.shadowColor = shadowColor.cgColor
view.layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight);
view.layer.shadowOpacity = shadowOpacity
view.layer.shadowPath = shadowPath.cgPath
return view
}()
lazy var stackViewContainer: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.alignment = .center
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fill
stack.spacing = 10.0
stack.addArrangedSubview(profileImage)
stack.addArrangedSubview(stackViewDataHolder)
return stack
}()
lazy var profileImage: UIImageView = {
let image = UIImage()
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
return imageView
}()
lazy var userName: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var tweetInfo: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var tweetText: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
lazy var linkView: LPLinkView = {
let viewer = LPLinkView(frame: CGRect(origin: .zero, size: .init(width: 200, height: 20)))
viewer.translatesAutoresizingMaskIntoConstraints = false
return viewer
}()
lazy var stackViewDataHolder: UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.addArrangedSubview(userName)
stack.addArrangedSubview(tweetInfo)
stack.addArrangedSubview(tweetText)
return stack
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
override func prepareForReuse() {
linkView.removeFromSuperview()
}
func configure(viewModel: ProfileTweetViewModel) {
tweetInfo.configure(model: viewModel.tweetInfo)
userName.configure(model: viewModel.name)
tweetText.configure(model: viewModel.tweet)
if let metadata = viewModel.linkData {
linkView = LPLinkView(metadata: metadata)
stackViewDataHolder.addArrangedSubview(linkView)
//Tried almost all layoyt options but seems a previous view can't be updated since frame is wrong
}
if let url = viewModel.profilePic {
profileImage.downloadImage(from: url)
}
}
}
private extension TimeLineTableViewCell {
struct Metrics {
static let lateralPadding: CGFloat = 8
}
func constraints() {
NSLayoutConstraint.activate([
stackViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: Metrics.lateralPadding),
stackViewContainer.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -Metrics.lateralPadding),
stackViewContainer.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: Metrics.lateralPadding),
stackViewContainer.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -Metrics.lateralPadding),
profileImage.heightAnchor.constraint(equalTo: profileImage.widthAnchor, multiplier: 1.0),
profileImage.widthAnchor.constraint(equalToConstant: 50.0),
])
}
func commonInit() {
addSubview(containerView)
backgroundColor = .clear
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: topAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4),
containerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
])
constraints()
}
}
Thank you for your time.
The issue was related to .fillProportionally in stackView
since the linkView sometimes renders with 0 height, i just had to use .fill property in stackView in order to show it fully

UIScrollView not scrolling w/ programmatic autolayout constraints

I am trying to understand how to use a UIScrollView programmatically.
When I give my content a size that does not fit on screen though, it does not scroll.
final class ProfileView: UIView {
private var isIpad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
private lazy var headerImageView: UIImageView = {
let iv = UIImageView(frame: .zero)
iv.translatesAutoresizingMaskIntoConstraints = false
iv.heightAnchor.constraint(equalToConstant: 600).isActive = true
iv.backgroundColor = .purple
return iv
}()
private lazy var profileImageView: UIImageView = {
let iv = UIImageView(frame: .zero)
iv.translatesAutoresizingMaskIntoConstraints = false
[iv.heightAnchor, iv.widthAnchor].forEach { $0.constraint(equalToConstant: 170).isActive = true }
iv.layer.cornerRadius = 170 / 2
iv.layer.borderColor = .white
iv.layer.borderWidth = 3
iv.layer.masksToBounds = true
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
iv.backgroundColor = .red
return iv
}()
private(set) lazy var nameLabel: UILabel = {
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Foo\nBar"
label.numberOfLines = 2
return label
}()
private lazy var contentScrollView: UIScrollView = {
let sv = UIScrollView(frame: .zero)
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureLayout()
}
required init?(coder: NSCoder) {
return nil
}
private func configureLayout() {
addSubview(contentScrollView)
[headerImageView, profileImageView, nameLabel].forEach { contentScrollView.addSubview($0) }
let compactConstraints = [
contentScrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
contentScrollView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
contentScrollView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
contentScrollView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
headerImageView.topAnchor.constraint(equalTo: topAnchor),
headerImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
headerImageView.trailingAnchor.constraint(equalTo: trailingAnchor),
profileImageView.centerYAnchor.constraint(equalTo: headerImageView.bottomAnchor),
profileImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
nameLabel.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 16),
nameLabel.centerXAnchor.constraint(equalTo: profileImageView.centerXAnchor),
]
NSLayoutConstraint.activate(compactConstraints)
}
}
This gives me the following -
The header image pushes the name label and avatar off screen and scrolling does not work.
I've read a bunch about giving the scroll view a huge offset so it scrolls, but that surely cannot be correct.
You need to create the constraints of all the subviews to the scrollView , add width constraint to the header img = profileview width and finally make bottom of label = bottom of the scrollview to make it infer it's height
let compactConstraints = [
contentScrollView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
contentScrollView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor),
contentScrollView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
contentScrollView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
headerImageView.topAnchor.constraint(equalTo:contentScrollView.topAnchor),
headerImageView.leadingAnchor.constraint(equalTo:contentScrollView.leadingAnchor),
headerImageView.trailingAnchor.constraint(equalTo:contentScrollView.trailingAnchor),
headerImageView.widthAnchor.constraint(equalTo: self.widthAnchor) // add this
profileImageView.centerYAnchor.constraint(equalTo: headerImageView.bottomAnchor),
profileImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
nameLabel.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 16),
nameLabel.centerXAnchor.constraint(equalTo: profileImageView.centerXAnchor),
nameLabel.bottomAnchor.constraint(equalTo: contentScrollView.bottomAnchor) // and this
]

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.