2

I have a collection view that will have a UILabel and between 2-5 UIButtons.

I want the cell to size to how many buttons that are visible according to each cell. I know that each button is about a 100 in height.

class myViewController: UIViewController {

    var myCollectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 100, right: 0) // add spacing to the bottom
        layout.itemSize = CGSize(width: self.view.frame.width, height: 300)
        layout.scrollDirection = .vertical
        layout.minimumLineSpacing = 20
        layout.minimumInteritemSpacing = 20

        myCollectionView=UICollectionView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height), collectionViewLayout: layout)
        myCollectionView.delegate=self
        myCollectionView.dataSource=self
        myCollectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        myCollectionView.alwaysBounceVertical = false
        myCollectionView.showsVerticalScrollIndicator = false
        myCollectionView.translatesAutoresizingMaskIntoConstraints=false
        myCollectionView.backgroundColor=UIColor.white
        myCollectionView.isPagingEnabled = false

        loadViews()
    }

    func loadViews() {
        self.view.addSubview(myCollectionView)
        myCollectionView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive=true
        myCollectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive=true
        myCollectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive=true
        myCollectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive=true

    }

}

Note that the above code block has layout.itemSize = CGSize(width: self.view.frame.width, height: 300) which works great for 3 buttons (3*100 = 300).

Then when setting up my cell class I create my buttons and then determine their visibility based off of a variable (at the bottom of this).


class MyCollectionViewCell: UICollectionViewCell {
    var btn1: UIButton!
    var btn2: UIButton!
    var btn3: UIButton!
    var btn4: UIButton!
    var btn5: UIButton!

   override init(frame: CGRect) {
        super.init(frame: frame)

        setupViews()
    }

    func setupViews() {

        addSubview(lblQue)
        lblQue.topAnchor.constraint(equalTo: self.topAnchor, constant: 30).isActive=true
        lblQue.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 12).isActive=true
        lblQue.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -12).isActive=true
        lblQue.heightAnchor.constraint(equalToConstant: 50).isActive=true

        let btnWidth: CGFloat = 650
        let btnHeight: CGFloat = 65
        btn1 = getButton(tag: 0)
        addSubview(btn1)
        NSLayoutConstraint.activate([btn1.topAnchor.constraint(equalTo: lblQue.bottomAnchor, constant: 10), btn1.leftAnchor.constraint(equalTo: self.centerXAnchor, constant: -300), btn1.widthAnchor.constraint(equalToConstant: btnWidth), btn1.heightAnchor.constraint(equalToConstant: btnHeight)])
        btn1.addTarget(self, action: #selector(btnOptionAction), for: .touchUpInside)

        btn2 = getButton(tag: 1)
        addSubview(btn2)
        NSLayoutConstraint.activate([btn2.topAnchor.constraint(equalTo: btn1.bottomAnchor, constant: 10), btn2.leftAnchor.constraint(equalTo: self.centerXAnchor, constant: -300), btn2.widthAnchor.constraint(equalToConstant: btnWidth), btn2.heightAnchor.constraint(equalToConstant: btnHeight)])
        btn2.addTarget(self, action: #selector(btnOptionAction), for: .touchUpInside)

        btn3 = getButton(tag: 2)
        addSubview(btn3)
        NSLayoutConstraint.activate([btn3.topAnchor.constraint(equalTo: btn2.bottomAnchor, constant: 10), btn3.leftAnchor.constraint(equalTo: self.centerXAnchor, constant: -300), btn3.widthAnchor.constraint(equalToConstant: btnWidth), btn3.heightAnchor.constraint(equalToConstant: btnHeight)])
        btn3.addTarget(self, action: #selector(btnOptionAction), for: .touchUpInside)

        btn4 = getButton(tag: 3)
        addSubview(btn4)
        NSLayoutConstraint.activate([btn4.topAnchor.constraint(equalTo: btn3.bottomAnchor, constant: 10), btn4.leftAnchor.constraint(equalTo: self.centerXAnchor, constant: -300), btn4.widthAnchor.constraint(equalToConstant: btnWidth), btn4.heightAnchor.constraint(equalToConstant: btnHeight)])
        btn4.addTarget(self, action: #selector(btnOptionAction), for: .touchUpInside)

        btn5 = getButton(tag: 4)
        addSubview(btn5)
        NSLayoutConstraint.activate([btn5.topAnchor.constraint(equalTo: btn4.bottomAnchor, constant: 10), btn5.leftAnchor.constraint(equalTo: self.centerXAnchor, constant: -300), btn5.widthAnchor.constraint(equalToConstant: btnWidth), btn5.heightAnchor.constraint(equalToConstant: btnHeight)])
        btn5.addTarget(self, action: #selector(btnOptionAction), for: .touchUpInside)
    }

    func getButton(tag: Int) -> UIButton {
        let btn=UIButton()
        btn.tag=tag
        btn.setTitle("Option", for: .normal)
        btn.setTitleColor(UIColor.black, for: .normal)
        btn.backgroundColor=UIColor.white
        btn.layer.borderWidth=1
        btn.layer.borderColor=UIColor.darkGray.cgColor
        btn.layer.cornerRadius=5
        btn.clipsToBounds=true
        btn.translatesAutoresizingMaskIntoConstraints=false
        return btn
    }

    let lblQue: UILabel = {
        let lbl=UILabel()
        lbl.text="This is a question and you have to answer it?"
        lbl.textColor=UIColor.black
        lbl.textAlignment = .center
        lbl.font = UIFont.systemFont(ofSize: 20)
        lbl.numberOfLines=4
        lbl.translatesAutoresizingMaskIntoConstraints=false
        return lbl
    }()

    var myVariable: MyClassIMade? {
        didSet {
             // go through and determine button Text and Visibility of each button
             // i.e. 
             // if 1>0 { 
             //     btn3.visible = false
             // } else {
             //     btn3.visible = true
             // }

        }
    }

So how can I determine how many buttons are visible to determine my size of cell for each section?

thalacker
  • 2,389
  • 3
  • 23
  • 44

3 Answers3

0

How about something like this ? just get all the buttons inside an array and then loop through them to check which button are hidden and which are not. you could use .isHidden to hide and display them and then check which is hidden and return the number..

Example function :

func getButtonsCount(buttons: [UIButton]) -> Int{
    //A counter to get how many button are not hidden
    var count = Int()
    //A Loop to check which buttons are hidden and increment the counter
    for button in buttons {
        if button.isHidden == true {
            count += 1
        }
    }

    return count
}
Yasser
  • 265
  • 1
  • 8
0

Here is how I ended up solving it:

I used sizeForItemAt which overrides all else and allowed me to get my cell for each row/usecase.

Basically, I can access all of the controls tied to my cell and determine their visibility and alter the height according to that.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! QuizCollectionViewCell

    var countOfButtonsNotHidden = 0

    if !cell.btn1.isHidden {
        countOfButtonsNotHidden += 1
    }
    if !cell.btn2.isHidden {
        countOfButtonsNotHidden += 1
    }
    if !cell.btn3.isHidden {
        countOfButtonsNotHidden += 1
    }
    if !cell.btn4.isHidden {
        countOfButtonsNotHidden += 1
    }
    if !cell.btn5.isHidden {
        countOfButtonsNotHidden += 1
    }

    return CGSize(width: self.view.frame.width, height: CGFloat(countOfButtonsNotHidden * 100))
}
thalacker
  • 2,389
  • 3
  • 23
  • 44
0

Use a UIStackView to layout your buttons. When you set the buttons to .isHidden the stack view will automatically deal with the layout of the buttons.

Example:

let stackView = UIStackView(arrangedSubviews: [button1, button2, button3, button4, button5])
stackView.axis = .vertical
stackView.alignment = .fill
stackView.distribution = .equalSpacing
stackView.spacing = 10

Next, you can calculate the height of the collection view cell by multiplying the number of visible buttons with your set button height.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath) as! YourCellClass

    let buttonHeight: CGFloat = 45
    let numberOfVisibleButtons = 0

    if !cell.button1.isHidden { numberOfVisibleButtons += 1 }
    if !cell.button2.isHidden { numberOfVisibleButtons += 1 }
    if !cell.button3.isHidden { numberOfVisibleButtons += 1 }
    if !cell.button4.isHidden { numberOfVisibleButtons += 1 }
    if !cell.button5.isHidden { numberOfVisibleButtons += 1 }

    let cellHeight: CGFloat = (buttonHeight + cell.stackView.spacing) * numberOfVisibleButtons

    return CGSize(width: yourCellWidth, height: cellHeight)

}
Nathan
  • 949
  • 8
  • 19