Position UIStackView inside tableview section header - swift

I have a tableview and I want to have a UIStackView in every section header. Now, I want to give a little padding to this stackView: in particular, I want the stackView to take the entire space except for 2px for every side. Is this possible? This is what I tried:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sNumber = UILabel()
sNumber.backgroundColor = UIColor.yellowColor()
sNumber.text = String(section)+". "
let lbl = UILabel()
lbl.backgroundColor = UIColor.cyanColor()
lbl.text = (detailItem![section]["canary"] as! String)
let lbl2 = UILabel()
lbl2.backgroundColor = UIColor.greenColor()
lbl2.text = (detailItem![section]["tbd"] as! String)
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Horizontal
stackView.distribution = UIStackViewDistribution.FillProportionally
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 10
let margins = stackView.layoutMarginsGuide
stackView.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor, constant: 2).active = true
stackView.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor, constant: 2).active = true
stackView.topAnchor.constraintEqualToAnchor(margins.topAnchor, constant: 2).active = true
stackView.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor, constant: 2).active = true
stackView.addArrangedSubview(sNumber)
stackView.addArrangedSubview(lbl)
stackView.addArrangedSubview(lbl2)
return stackView
}
Any help is appreciated.

Related

How to setup constraints UICollectionViewCell label to UICollectionView frame?

What am I trying to achieve:
I have a collectionView which represents events in a timeline. These cells (events) have description label that is constrained to the left edge.
When scrolling to events to the left on the timeline, some events are too wide and are not showing the description label immediately (i must scroll to the very beginning of the cell on timeline to see it)
The idea was to setup constraints between cells label to collectionView frame so that you could see event description immediately, but these are different view hierarchies.
What would be the best way to approach this?
What i have tried:
Setting up the constraint crashes the app:
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x600002cbe700 "UILabel:0x11ef0ea20.leading"> and <NSLayoutXAxisAnchor:0x600002cbf240 "UILayoutGuide:0x6000000289a0'UIScrollView-frameLayoutGuide'.leading"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
Additional Info:
UICollectionViewCompositionalLayout:
UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in
let item = data.items[sectionIndex]
let contentWidth = data.contentWidth
return context.coordinator.layout(item, contentWidth)
}
NSCollectionLayoutSection:
var layout: (Item, CGFloat) -> NSCollectionLayoutSection = { item, contentWidth in
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(contentWidth), heightDimension: .absolute(100))
let itemSizes = item.items.map { NSCollectionLayoutItem(layoutSize: .init(widthDimension: .absolute(CGFloat($0.duration)), heightDimension: .absolute(100)))}
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: itemSizes)
group.interItemSpacing = .fixed(0)
group.contentInsets = .init(top: 0, leading: item.leftGap, bottom: 0, trailing: item.rightGap)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 0
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: .init(widthDimension: .absolute(255), heightDimension: .absolute(100)),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .leading
)}
}
header.pinToVisibleBounds = true
section.boundarySupplementaryItems = [header]
return section
}
CollectionViewCell:
class MyCollectionViewCell: UICollectionViewCell {
static let identifier: String = "MyCollectionViewCell"
var item: DataState.Item = .dummy {
didSet {
descriptionLabel.text = item.title
descriptionTimeLabel.text = item.timeDescription
}
}
var containerView: UIView = {
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.layer.cornerRadius = 15
containerView.clipsToBounds = true
return containerView
}()
var bg: UIView = {
let bg = UIView()
bg.translatesAutoresizingMaskIntoConstraints = false
bg.backgroundColor = .black
return bg
}()
var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 31, weight: .regular)
return label
}()
var descriptionTimeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 23, weight: .regular)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(containerView)
containerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5).isActive = true
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5).isActive = true
containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -5).isActive = true
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 5).isActive = true
containerView.addSubview(bg)
bg.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
bg.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
bg.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
bg.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
containerView.addSubview(descriptionLabel)
descriptionLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10).isActive = true
containerView.addSubview(descriptionTimeLabel)
descriptionTimeLabel.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -10).isActive = true
descriptionTimeLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10).isActive = true
}
}
Just to remind whole point is to make labels of cell visible immediately as the wide cell being revealed and be continously updated so when the cell is fully visible label's leadingAnchor and containerView leadingAnchor equals 10
maybe you can try this. If this in my case there 2 separate file. 1 for cell, and 1 viewController with collectionView in it.
I will use UICollectionViewDataSource, UICollectionViewDelegate to the viewController. You can set the cell with add 1 more extention UICollectionViewFlowLayout or
You can add setUpUI direct to the UICollectionViewCell and call it to override func awakeFromNib(). here's the code :
mainView.translatesAutoresizingMaskIntoConstraints = false mainView.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.size.width - 40).isActive = true
mainView: UIView! from cell and UIScreen.main.bounds.size.width is the controller collectionView Size.
add reference outlet of collectionView controller to mainView
Don't Forget to check you constraint at cell. and change the autoRezized from "none" to "automatic"
So turns out that if you add constraints to cell as properties and then in: scrollViewDidScroll(UIScrollView) find visibleCells and filter those which are visible just halfway or so. you can then change constants on constraints and update the view.
works great. looks weird.
example:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let collectionView = scrollView as? UICollectionView else { return } // move elsewhere just to access reference to collectionView and dont cast it everytime offset changes (which could be many times a second)
let scrollOffset = scrollView.contentOffset.x
if let visible = collectionView?.visibleCells as? [MyCell] {
let visibleDisappearing = visible.filter({ $0.frame.minX <= scrollOffset })
let visibleFull = visible.filter({ $0.frame.minX > scrollOffset })
for disappearingCell in visibleDisappearing {
let distance = scrollView.contentOffset.x - disappearingCell.frame.minX + 10 // 10 would be the original distance from edge to label
disappearingCell.descriptionLeadingConstraint.constant = distance
disappearingCell.descriptionTimeLeadingConstraint.constant = distance
disappearingCell.layoutIfNeeded()
}
for fullCell in visibleFull {
fullCell.descriptionLeadingConstraint.constant = 10
fullCell.descriptionTimeLeadingConstraint.constant = 10
fullCell.layoutIfNeeded()
}
}
}

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
}()

Swift- unable to align stack elements with different font sizes

I have 2 UILabels as follows:
private lazy var titleLabel: UILabel = {
let label = UILabel()
label.textColor = Constants.mainFontColor.withAlphaComponent(0.7)
label.translatesAutoresizingMaskIntoConstraints = false
label.isUserInteractionEnabled = false
label.sizeToFit()
return label
}()
private lazy var valueLabel: UILabel = {
let label = UILabel()
label.textColor = Constants.mainFontColor
label.translatesAutoresizingMaskIntoConstraints = false
label.isUserInteractionEnabled = false
label.sizeToFit()
return label
}()
These labels are then placed into a UIStackView:
private lazy var stackView: UIStackView = {
let stack = UIStackView(arrangedSubviews: [titleLabel, valueLabel])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .horizontal
stack.spacing = 5
stack.alignment = .firstBaseline
addSubview(stack)
return stack
}()
I then create several instances of these e.g.:
private lazy var flightNumber: FuelSheetHeaderField = {
return FuelSheetHeaderField(title: FuelSheetStringsField.flightNumber.title.localized,
value: viewModel.flightNumber, titleFontSize: 17, valueFontSize: 24)
}()
These are then placed into a separate stack view:
private lazy var flightData: UIStackView = {
let stack = UIStackView(arrangedSubviews: [flightNumber, aircraftReg, dateTime, origin])
stack.axis = .horizontal
stack.distribution = .equalSpacing
stack.spacing = Constants.standardHorizontalStackSpacing
return stack
}()
The title labels have font size of 12 and the values have font size 17. Except for one which is customised to have font size 17 and 2 accordingly. I am trying to get all the elements to align along the same base line. I added the following constraint:
titleLabel.firstBaselineAnchor.constraint(equalTo: valueLabel.firstBaselineAnchor),
However the effect is as follows:
Most of the stack elements are aligning but as you can see the first element with the larger font is sitting higher than the second. This is driving me a little crazy. How can I fix it so that all elements in the stack align along the same baseline.
I think you're going to have trouble trying to use baselines across stack views.
One option - not sure if it will work for you - is to put all the labels in a single horizontal stack view, and use .setCustomSpacing() to adjust the spaces between labels.
As an example:
and with Debug -> View Debugging -> Show View Frames:
Here's the code I used to generate that:
class AlignedStackViewController: UIViewController {
let detailsStack: UIStackView = {
let v = UIStackView()
v.axis = .vertical
v.alignment = .leading
v.distribution = .fill
v.spacing = 12
return v
}()
let dataStack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.alignment = .firstBaseline
v.distribution = .fill
v.spacing = 5
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.928, green: 0.973, blue: 1.0, alpha: 1.0)
let fdLabel = UILabel()
fdLabel.text = "FLIGHT DETAILS"
detailsStack.addArrangedSubview(fdLabel)
detailsStack.addArrangedSubview(dataStack)
detailsStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(detailsStack)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// position stack view at 40, 100
detailsStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
detailsStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
])
addLabelPair(with: "#", value: "VS0105", titleFontSize: 17, valueFontSize: 24)
addLabelPair(with: "Reg:", value: "GAAAA", titleFontSize: 12, valueFontSize: 17)
addLabelPair(with: "Departure:", value: "09 Sep 2020", titleFontSize: 12, valueFontSize: 17)
addLabelPair(with: "at", value: "08:34", titleFontSize: 12, valueFontSize: 17)
updateStackSpacing()
}
func updateStackSpacing() -> Void {
// make sure stack has been filled
guard dataStack.arrangedSubviews.count == 8 else {
return
}
// verbose for clarity
let flightNumberLabel = dataStack.arrangedSubviews[1]
let aircraftRegLabel = dataStack.arrangedSubviews[3]
dataStack.setCustomSpacing(12, after: flightNumberLabel)
dataStack.setCustomSpacing(12, after: aircraftRegLabel)
}
func addLabelPair(with title: String, value: String, titleFontSize: CGFloat, valueFontSize: CGFloat) -> Void {
let tLabel = UILabel()
let vLabel = UILabel()
tLabel.font = UIFont.systemFont(ofSize: titleFontSize)
vLabel.font = UIFont.systemFont(ofSize: valueFontSize)
tLabel.textColor = UIColor.black.withAlphaComponent(0.7)
vLabel.textColor = UIColor.black
tLabel.text = title
vLabel.text = value
dataStack.addArrangedSubview(tLabel)
dataStack.addArrangedSubview(vLabel)
}
}
I think, It seems that uiLabel has default padding
And the default padding value depends on the font size.
In spite of sizeToFit, the result is the same

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

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.