Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is invalidateLayout is not triggering sizeForItemAtIndexPath in UICollectionView? (code attached)

The issue is the number of columns in the collectionView does NOT stay at 7 (the desired amount) upon rotation. What code change is required to fix this?

It seems the invalidateLayout from the custom UICollectionViewFlowLayout is NOT triggering the sizeForItemAtIndexPath method in the collectionView? Any ideas? I really just want the column resizing to occur via the sizeForItemAtIndexPath upon rotation.

NOTE: I'm not using storyboard here, rather I have a custom view in which I drop in a collectionView and reference my own collectionView classes.

The following code works fine, however upon rotation it does not keep the number of columns to 7 like it should. On running the code initially the console output shows:

GCCalendar - init coder
GCCalendar - commonInit
GCCalendarLayout:invalidateLayout
GCCalendarLayout:invalidateLayout
ViewController:viewWillLayoutSubviews
GCCalendarLayout:invalidateLayout
GCCalendarLayout:prepareLayout
sizeForItemAtIndexPath
.
.
sizeForItemAtIndexPath
minimumInteritemSpacingForSectionAtIndex
minimumLineSpacingForSectionAtIndex
GCCalendarLayout:collectionViewContentSize
GCCalendarLayout:layoutAttributesForElementsInRect
GCCalendarLayout:collectionViewContentSize
ViewController:viewWillLayoutSubviews
GCCalendarCell:drawRect
.
.
GCCalendarCell:drawRect

However then rotating the screen I see the following:

ViewController:viewWillLayoutSubviews
GCCalendarLayout:shouldInvalidateLayoutForBoundsChange
GCCalendarLayout:invalidateLayout
GCCalendarLayout:prepareLayout
GCCalendarLayout:collectionViewContentSize
GCCalendarLayout:layoutAttributesForElementsInRect
GCCalendarLayout:collectionViewContentSize

So the issue is "sizeForItemAtIndexPath" never got called????

Output Code on Rotation

Note: "sizeForItemAtIndexPath" is not triggered even though "invalidateLayout" is

ViewController:viewWillLayoutSubviews GCCalendarLayout:shouldInvalidateLayoutForBoundsChange GCCalendarLayout:invalidateLayout

**My Custom View that houses the collection View **

import UIKit

@IBDesignable class GCCalendarView: UIView {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

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



    // Private

    private func commonInit() {
        if self.subviews.count == 0 {
            let bundle = NSBundle(forClass: self.dynamicType)
            let nib = UINib(nibName: "GCCalendarView", bundle: bundle)
            let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
            view.frame = bounds
            view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
            addSubview(view)
        }
    }

}

Custom Collection View

import UIKit

class GCCalendar : UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {

    // Init ---------------

    func commonInit(coder aDecoder: NSCoder) {
        print("GCCalendar - commonInit")
        self.registerClass(GCCalendarCell.self, forCellWithReuseIdentifier: "GCCalendarCell")
        self.dataSource = self
        self.delegate = self

        let layout : GCCalendarLayout = GCCalendarLayout(coder: aDecoder)!
        self.setCollectionViewLayout(layout, animated: false)

        self.backgroundColor = UIColor.whiteColor()
    }

    required init?(coder aDecoder: NSCoder) {
        print("GCCalendar - init coder")
        super.init(coder: aDecoder)
        commonInit(coder: aDecoder)
    }


    // UICollectionViewDelegateFlowLayout ------------

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        print("sizeForItemAtIndexPath")
        let w : CGFloat = floor(self.frame.size.width/7)
        return CGSize(width: w, height: w)
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) ->
        CGFloat {
        print("minimumInteritemSpacingForSectionAtIndex")
        return 0.0
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
        print("minimumLineSpacingForSectionAtIndex")
        return 0.0
    }


    // UICollectionViewDataSource -------------------

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

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("GCCalendarCell", forIndexPath: indexPath) as? GCCalendarCell


        return cell!
    }
}

** Custom CollectionView Cell **

import UIKit

class GCCalendarCell: UICollectionViewCell {

    @IBOutlet weak var title : UITextField!
    @IBOutlet weak var date: UILabel!

    required init?(coder aDecoder: NSCoder) {
        print("GCCalendarCell - init:coder")
        super.init(coder: aDecoder)
        commonInit()
    }

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

    override func drawRect(rect: CGRect) {
        print("GCCalendarCell:drawRect")
        self.layer.borderWidth = 1
        self.layer.borderColor = UIColor.grayColor().CGColor
    }

    // Private

    private func commonInit() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "GCCalendarCell", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        addSubview(view)
    }

}

Custom Layout

import UIKit

class GCCalendarLayout : UICollectionViewFlowLayout {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        print("GCCalendarLayout:shouldInvalidateLayoutForBoundsChange")
        return true
    }

    override func invalidateLayout() {
        print("GCCalendarLayout:invalidateLayout")
        super.invalidateLayout()

    }

    override func prepareForCollectionViewUpdates(updateItems: [UICollectionViewUpdateItem]) {
        print("GCCalendarLayout:prepareForCollectionViewUpdates")
        super.prepareForCollectionViewUpdates(updateItems)
    }

    override func finalizeCollectionViewUpdates() {
        print("GCCalendarLayout:finalizeCollectionViewUpdates")
        super.finalizeCollectionViewUpdates()
    }


}
like image 234
Greg Avatar asked Oct 14 '15 10:10

Greg


3 Answers

EDIT : Try this in your viewController :

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
{
    (self.collectionview.collectionViewLayout).setItemSize(CGSizeMake(floor(size.width / 7), floor(size.width / 7)))
    self.collectionview.collectionViewLayout.invalidateLayout()
}
like image 195
Kujey Avatar answered Oct 13 '22 01:10

Kujey


if you are using Storyboard Set the CollectionviewFlowLayout Class In storyboard I attached Bellow

this is FlowLayout

Classname Set here

like image 34
Ramkumar chintala Avatar answered Oct 13 '22 00:10

Ramkumar chintala


Nothing helped for me from the existing answers (iPhone X Simulator iOS 11.2).
I've found that changing of estimatedSize helps for me with this problem:

<...>
//***new line
flowLayout.estimatedItemSize = [self getIconSize];//get a new size for items

[flowLayout invalidateLayout];
<...>
like image 29
Andrew Romanov Avatar answered Oct 12 '22 23:10

Andrew Romanov