Unscrollable Multiple Collection Views

Refresh

April 2019

Views

62 time

1

I am trying to develop an iOS application that has 03 UICollectionViews handled by the same ViewController. (The reason why I am opting for 03 UICollectionViews rather than one with sections and different prototype cells is because I may need to add additional content not relevant to collection views between the sections in the future)

 ________________
|  _   _
| | | | |
| |_| |_| . . .  (UICollectionView1)
|_______________

 _______________
|  _   _
| | | | |
| |_| |_| . . .  (UICollectionView2)
|_______________

My problem is as follows:

No of cells in each of the CollectionViews is variable and if the number exceeds the width constraint it wraps down (so far so good). However, the height constraint of the UICollectionView causes a scroll bar to appear rather than simply laying out the cells if the number of cells causes to wrap beyond the height constraint

I have tried a couple of things to get this to work, most of which revolve around the advice given in the following questions

how to set dynamic height of a Collection View, the 'view' not the 'cells'?

How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?

In the end I tried this

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: collectionView.contentSize.height)
    }

But then the content in the UICollectionViewCell stretched wierdly and still problem of scrolling exists.

I do not mind that the main view of the view controller (the one on which all other UICollectionViews are placed) becomes scrollable (actually that is part of the requirement), I just don't want the UICollectionViews to act like some HTML iframe and allow scrolling but just layout the cells in order for as much as constrained by width of the UICollectionView

In pseudo code, something like this

array = (cell1, cell2, cell3)
for i in array 
    if currentCollectionViewRow is filled
       wrapToNextLine()
    add cell{i} to view controller

Any help is appreciated. Even help that suggests better ways to achieve this functionality along with best practices rather than hacking code

EDIT

I carried out the instructions as @Saad Chaudhry mentioned but to no avail. My layout is as follows: CollectionView Layout

As you can see, the stack view encloses both collection views as suggested. Now the IDE gives the following complaints: Ambiguous Layout

I tried adding constraints intuitively, then tried the IDEs options to add constraints automatically to no avail. Most times, there is no data cells on the screen.

For more information, without stack view but just two collection views, I get the following simulation: Without Stack Views

And with stack views I get the simulation: With stack views and note that the second collection view is missing

If I then constraint the stack view at 0,0,0,0, this brings back the issues where each of the collection view heights are ambiguous. Providing heights causes scrolling "within that collection view" if the number of cells are large (from datasource).

I simply want all the black squares to be rendered first and then the yellow squares. The parent view may scroll and that's fine bu not the individual collection views

My code for the controller:

import UIKit

class DemoCollectionViewController: UICollectionViewController {

    @IBOutlet weak var nonPriorityCollectionView: UICollectionView!
    @IBOutlet weak var priorityCollectionView: UICollectionView!

    private let reuseIdentifier = "priorityCell"
    fileprivate let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)
    fileprivate var priorityItems = [PriorityItem]()
    fileprivate let itemsPerRow: CGFloat = 3

    private let nonPriorityReuseIdentifier = "nonPriorityCell"
    fileprivate var nonPriorityItems = [String]()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
        self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: nonPriorityReuseIdentifier)

        loadPriorityItems()
        loadNonPriorityItems()
    }

    func loadPriorityItems(){
        let item1 = PriorityItem(image: #imageLiteral(resourceName: "User"))
        let item2 = PriorityItem(image: #imageLiteral(resourceName: "User"))
        let item3 = PriorityItem(image: #imageLiteral(resourceName: "User"))
        let item4 = PriorityItem(image: #imageLiteral(resourceName: "User"))

        priorityItems = [item1, item2, item3, item4, item1, item2, item3, item4]
    }

    func loadNonPriorityItems(){
        let item1 = "Item 1"
        let item2 = "Item 2"
        let item3 = "Item 3"
        let item4 = "Item 4"

        nonPriorityItems = [item1, item2, item3, item4]
    }
}

// MARK: UICollectionViewDataSource
extension DemoCollectionViewController {

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

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if collectionView == self.priorityCollectionView {
            return priorityItems.count
        }
        else{
            return nonPriorityItems.count
        }
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        if collectionView == self.priorityCollectionView {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
            cell.backgroundColor = UIColor.black
            return cell
        } else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: nonPriorityReuseIdentifier, for: indexPath)
            cell.backgroundColor = UIColor.yellow
            return cell
        }
    }
}

extension DemoCollectionViewController : UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
        let availableWidth = view.frame.width - paddingSpace
        let widthPerItem = availableWidth / itemsPerRow

        return CGSize(width: widthPerItem, height: widthPerItem)
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return sectionInsets.left
    }
}

Please help me in figuring out this issue. I now feel like collection view is not the ideal way to achieve this due to the complexity I'm facing...

jms

0 answers