3

I want to create a vertical infinite scroll and after reading so many tutorials have understood that I need to subclass UICollectionViewFlowLayout. The problem is that I don't fully understand how to do so.

I've tried following: 1. Created a new class newView and assigned it to my view controller in attribute inspector custom class section.

class newView: UICollectionViewController,
UICollectionViewDataSource, UICollectionViewDelegate {
  1. Implemented (override) cellForItemAtIndexPath and sizeForItemAtIndexPath in this class which works fine. I have a vertical scrolling view so far containing 2 items in 1 row. But I have unequal spaces between 2 rows. After laying out first 2 items, the third one's vertical position is below the longer of the previous 2 items as shown below: enter image description here

I've read many SO threads discussing and suggesting to subclass UICollectionViewFlowLayout and override layoutAttributesForElementsInRect method for desired display. But when I try to add flow layout in my view controller like below it gives me errors:

class DiscoverView: UICollectionViewController, UICollectionViewFlowLayout,
UICollectionViewDataSource, UICollectionViewDelegate {

I then thought that it's may be my view layout that needs to be subclassed instead of controller, so I tried to create a separate class like below:

class newViewLayout: UICollectionViewFlowLayout {

override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
    return super.layoutAttributesForElementsInRect(rect)
}

And then I tried to assign this class to my view layout. But it doesn't appear under custom class section (attribute inspector). Neither does it appear in Attribute inspector > collection view > layout > set custom > Class

I know it's some very basic and silly mistake but not sure what I'm doing wrong conceptually.

Rahul
  • 382
  • 2
  • 16
  • This question is about making the `UICollectionViewFlowLayout` RTL instead of it's default LTR, but it can give you a sense on how to do what you want - http://stackoverflow.com/questions/19712201/ios-uicollectionview-default-flow-fill-rows-from-right-to-left – Asaf May 20 '15 at 06:57
  • @Asaf the solution to your referred question states: if you are using storyboards: 1. change the collectionView layout to custom (in the attribute inspector) 2. set it's class to GSRightToLeftCollectionViewFlowLayout. But as stated in my question, I'm not able to set the collectionView layout to the created class. It doesn't give me that option. Please refer to what I wrote towards the end of my question. Thanks for your comment. – Rahul May 20 '15 at 07:29

2 Answers2

0

Though this is old, I wan´t to add the solution that made it work for me :)

You should add the subclass in viewDidLoad like:

collectionView?.collectionViewLayout = YourCustomClass
Jonas Borneland
  • 383
  • 1
  • 6
  • 19
0

You need to override the flowlayout you declared in your main class

let flowLayout = flowLayoutClass()

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.collectionViewFlowLayout = flowLayout
}

class flowLayoutClass: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    let arr = super.layoutAttributesForElements(in: rect)

    for atts:UICollectionViewLayoutAttributes in arr! {
        if nil == atts.representedElementKind {
            let ip = atts.indexPath
            atts.frame = (self.layoutAttributesForItem(at: ip)?.frame)!
        }
    }
    return arr
  }

  override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    let atts = super.layoutAttributesForItem(at: indexPath)

    if indexPath.item == 0 || indexPath.item == 1 {
        var frame = atts?.frame;
        frame?.origin.y = sectionInset.top;
        atts?.frame = frame!;
        return atts
    }

    let ipPrev = IndexPath(item: indexPath.item - 2, section: indexPath.section)

    let fPrev = self.layoutAttributesForItem(at: ipPrev)?.frame

    let rightPrev = (fPrev?.origin.y)! + (fPrev?.size.height)! + 10

    if (atts?.frame.origin.y)! <= rightPrev {
        return atts
    }

    var f = atts?.frame
    f?.origin.y = rightPrev
    atts?.frame = f!
    return atts
  }
}
carlosobedgomez
  • 161
  • 1
  • 2
  • 11
  • 2
    It is Swift naming convention to name protocols, structures and classes starting with an uppercase letter and to do not include the object type to its name. Just drop th UI `CollectionViewFlowLayout` – Leo Dabus Aug 08 '20 at 06:52
  • @LeoDabus it's just an example, don't be rigid – carlosobedgomez Aug 08 '20 at 15:35
  • 1
    I am not. Btw using optional chaining `(fPrev?.origin.y)!` to force unwrap it later it is pointless and ugly. `fPrev!.origin.y`. I would use a guard in `fPrev` and return `nil` in case of failure. You should properly unwrap your optionals. And don't use `;` at the end of the lines when coding in Swift – Leo Dabus Aug 08 '20 at 15:44
  • 1
    "it's just an example, don't be rigid" -- on the contrary: it's an example, set a good one. – hatfinch Jun 17 '21 at 10:15