I have spent a long time trying to stack the views that I create programmatically. I looked at examples from Add views in UIStackView programmatically but that didn't work. Listed below is the code, I am calling the setUpListings
from the view controller. There are two entries but only one entry is shown.
import UIKit
import SnapKit
class ListingsView : UIView {
var containerView: UIView!
var listingsContainerView: UIStackView!
init() {
super.init(frame: CGRect.zero)
setUpContainerView()
setUpListingsContainer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
make.height.equalTo(self)
make.width.equalTo(self)
containerView.backgroundColor = UIColor.white
}
}
func setUpListingsContainer() {
listingsContainerView = UIStackView()
listingsContainerView.distribution = .equalSpacing
listingsContainerView.alignment = .fill
listingsContainerView.axis = .vertical
listingsContainerView.spacing = 10
listingsContainerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(listingsContainerView)
listingsContainerView.snp.makeConstraints { (make) in
make.top.equalTo(containerView)
make.left.equalTo(containerView)
make.bottom.equalTo(containerView)
make.right.equalTo(containerView)
}
}
func setUpListings(listings: [Listing]) {
for listing in listings {
let listingEntry = ListingEntry(listingId: listing.id)
listingsContainerView.addArrangedSubview(listingEntry)
}
}
class ListingEntry : UIView {
var listingId: String?
var containerView: UIView!
init(listingId: String) {
super.init(frame: CGRect.zero)
self.listingId = listingId
self.setUpContainerView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
containerView.backgroundColor = UIColor.gray
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
make.width.equalTo(150)
make.height.equalTo(150)
}
}
}
}
But the blocks should be stacked.
Couple things...
First, I'd suggest learning how constraints and auto-layout work before using something like SnapKit. It can make some things easier --- but until one has a good understanding of the fundamentals, it's not clear what's doing what.
Second, during development, it helps to give views and subviews contrasting background colors. Makes it much easier to see what's happening to the frames at run-time.
So, if you're going to stick with SnapKit...
Try to keep code "clean." That is, don't put anything inside a snp.makeConstraints
block that isn't directly related (such as setting background colors).
In your ListingEntry
class, you're adding a subview (containerView
) and giving that view a width and height of 150, but you are not constraining it to its superview... which results in a view height of Zero.
Take a look at the modifications I made to your code. I added comments that should make the changes clear:
class MiscViewController: UIViewController {
var listingsView: ListingsView = {
let v = ListingsView()
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(listingsView)
listingsView.backgroundColor = .red
// constrain listingsView to all 4 sides with 40-pt "padding"
listingsView.snp.makeConstraints { (make) in
make.top.bottom.leading.trailing.equalToSuperview().inset(40.0)
}
let listings: [Listing] = [
Listing(id: "A"),
Listing(id: "B"),
Listing(id: "C"),
]
listingsView.setUpListings(listings: listings)
}
}
struct Listing {
var id: String = ""
}
class ListingsView : UIView {
var containerView: UIView!
var listingsContainerView: UIStackView!
init() {
super.init(frame: CGRect.zero)
// probably want to set clipsToBounds so any content doesn't extend outside the frame
clipsToBounds = true
setUpContainerView()
setUpListingsContainer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
self.addSubview(containerView)
containerView.backgroundColor = UIColor.green
// constrain containerView to all 4 sides
containerView.snp.makeConstraints { (make) in
make.top.bottom.leading.trailing.equalToSuperview()
}
}
func setUpListingsContainer() {
listingsContainerView = UIStackView()
listingsContainerView.distribution = .equalSpacing
listingsContainerView.alignment = .fill
listingsContainerView.axis = .vertical
listingsContainerView.spacing = 10
containerView.addSubview(listingsContainerView)
// constrain listingsContainerView (a stack view) to all 4 sides
listingsContainerView.snp.makeConstraints { (make) in
make.top.leading.bottom.trailing.equalToSuperview()
}
}
func setUpListings(listings: [Listing]) {
for listing in listings {
let listingEntry = ListingEntry(listingId: listing.id)
listingEntry.backgroundColor = .cyan
listingsContainerView.addArrangedSubview(listingEntry)
}
}
class ListingEntry : UIView {
var listingId: String?
var containerView: UIView!
init(listingId: String) {
super.init(frame: CGRect.zero)
self.listingId = listingId
self.setUpContainerView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
containerView.backgroundColor = .gray
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
// you want the "listing container" to be 150 x 150 pts
make.width.equalTo(150)
make.height.equalTo(150)
// and it needs top and bottom constraints to give self a height value
make.top.bottom.equalToSuperview()
// and it needs an x-position constraint
make.leading.equalToSuperview()
}
}
}
}
I've set the "main" ListingsView
background color to red
... you don't see it because its containerView
subview is green
and fills the view.
Each ListingEntry
view has a cyan
background color, and its containerView
has a gray
background color.
The result:
and Debug View Hierarchy:
Last notes...
.distribution = .equalSpacing
but you also set .spacing = 10
, which doesn't make sense. ListingEntry
views than will fit vertically, you'll run into problems. I'd expect you'd put that into a scroll view.