1

I am trying to add a stack view containing a UILabel at the top and a UICollectionView underneath. I am trying to constrain the stack view so that it takes up the full view, by anchoring it to all sides. When I run the app the UILabel appears and a slice of the collection view appears. The collection view says its width and height are both zero. Any help would be appreciated, thank you.

var collectionView: UICollectionView!
var titleLabel: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    self.view.translatesAutoresizingMaskIntoConstraints = false
    let margins = self.view.layoutMarginsGuide

    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)

    collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    layout.itemSize = CGSize(width: self.collectionView.frame.width / 4, height: self.collectionView.frame.width / 4)
    layout.minimumInteritemSpacing = self.collectionView.frame.width / 15
    layout.minimumLineSpacing = self.collectionView.frame.width / 5

    collectionView.backgroundColor = UIColor.black
    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")

    titleLabel = UILabel()
    titleLabel.translatesAutoresizingMaskIntoConstraints = false

    titleLabel.textAlignment = .center
    titleLabel.numberOfLines = 1
    titleLabel.font = UIFont.systemFont(ofSize: 22)
    titleLabel.backgroundColor = UIColor.lightGray
    titleLabel.text = "Challenges"
    titleLabel.textColor = UIColor.red

    let stackView = UIStackView(arrangedSubviews: [titleLabel, collectionView])
    stackView.backgroundColor = UIColor.white
    stackView.axis = .vertical
    stackView.distribution = .fillEqually
    stackView.alignment = .fill
    stackView.spacing = 5
    self.view.addSubview(stackView)
    stackView.translatesAutoresizingMaskIntoConstraints = false

    //stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
    //stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
    stackView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
    stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
    stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
    stackView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
    stackView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1.0).isActive = true

}

UPDATE:

I took my code and ran it in a new single view application and it worked as expected. Because of this I think it is worth it to mention that I am trying to incorporate this into a sprite kit game and I present the view controller by doing this:

let root = self.view?.window?.rootViewController
var viewC = SelectionCollectionViewController()
root?.present(viewC, animated: false, completion: nil)

Are there special steps I need to take because this is done with sprite kit?

Kobi Greene
  • 98
  • 1
  • 10
  • A few things, none of which I believe matter. (1) Don't use frame where possible. In other words, set the frame of your collection view to `CGRect.zero`. Again, I don't believe this makes any difference. (2) Set your constraints in `viewDidLoad`, but - particularly if you are using auto layout - do not reference any subview frames there. Odds are you will have `CGRect.zero` anyways. So set your "layout" stuff - which I believe may be correct to use frames - in `viewWillLayoutSubviews` instead. (3) There's no need to set a `heightAnchor` if you've set those other 4 constraints. Get rid of it. –  Aug 18 '17 at 01:16
  • Final note (I ran out of room). Of these three things, I'm betting the second has the best chance of making a difference. –  Aug 18 '17 at 01:17
  • I executed the code, it shows the UILabel - Spacing of 5 pts - CollectionView. What is the expected output? – NNikN Aug 18 '17 at 01:24
  • what is expected output ? – iOS Geek Aug 18 '17 at 04:40
  • @dfd Thanks for helping! I put the code into ViewWillLayoutSubviews(), along with your other suggestions, but it didn't seem to fix the issue. When I switch the frame of my collectionView instead of getting the 2 red rectangles that i've been getting they disappear. – Kobi Greene Aug 18 '17 at 04:40
  • The expected output is the UILabel on the top and the collection view beneath it, and they should fill the entire screen. – Kobi Greene Aug 18 '17 at 04:41

2 Answers2

2

I believe below screen shot is the expected output.

enter image description here

You might not need UIStackView , You can directly add it to self.view.

Once added to self.view, you can set up constraints. You can print the item Size in viewDidLayoutSubviews

class ViewController: UIViewController {

    var collectionView: UICollectionView!
    var titleLabel: UILabel!
    let collectionCellIdentifier:String = "collectionCellId"

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 10, bottom: 10, right: 10)

        collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.register(UICollectionViewCell.classForCoder(), forCellWithReuseIdentifier: collectionCellIdentifier)

        collectionView.backgroundColor = UIColor.black
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")

        titleLabel = UILabel()


        titleLabel.textAlignment = .center
        titleLabel.numberOfLines = 1
        titleLabel.font = UIFont.systemFont(ofSize: 22)
        titleLabel.backgroundColor = UIColor.lightGray
        titleLabel.text = "Challenges"
        titleLabel.textColor = UIColor.red
        titleLabel.translatesAutoresizingMaskIntoConstraints =  false
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(titleLabel)
        self.view.addSubview(collectionView)

        setUpConstraints()
    }


    func setUpConstraints(){
        self.view.addConstraint(NSLayoutConstraint(item: self.titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier:0, constant:50.0 ))
        titleLabel.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        titleLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        titleLabel.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true


        collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
        collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        let flowLayout = (collectionView.collectionViewLayout as! UICollectionViewFlowLayout)
        flowLayout.itemSize = CGSize(width: collectionView.frame.width / 4.0 , height: collectionView.frame.width / 4.0)
        flowLayout.minimumInteritemSpacing = self.collectionView.frame.width / 15.0
        flowLayout.minimumLineSpacing = self.collectionView.frame.width / 5.0
    }


}

extension ViewController:UICollectionViewDataSource,UICollectionViewDelegate {


    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: collectionCellIdentifier, for: indexPath)
        cell.backgroundColor = UIColor.gray
        return cell
    }

}
NNikN
  • 3,720
  • 6
  • 44
  • 86
-1

I think you need to know what happen in these functions:

  • viewDidLoad
  • viewWillAppear
  • viewWillLayoutSubviews
  • viewDidLayoutSubviews
  • viewDidAppear

if you use autoLayout, when in viewDidLoad(), the frame is not confirmed, because the view will make an auto adjustment when in viewWillLayoutSubviews() and viewDidLayoutSubviews(), so I suggest you make these code in viewDidAppear(), and then you may see what you want!

Furthermore, if you use storyboard or nib, you need to do these in awakeFromNib()

pluto
  • 516
  • 6
  • 9