With a subclass of UICollectionViewFlowLayout / UICollectionViewLayout CellForItemAtIndexPath is not called at the right time

This question is not a duplicate, although I see many similar to stavkOverflow. Here is the situation related to my code:

  • CellForItemAtIndexPath - Called correctly if I have my collection view outside of UIStackView. The problem only occurs when it is inside the stackView

  • I correctly subclassed my StreamLayout from a UICollectionViewFlowLayout.

  • CollectionView correctly returns numberOfSections and NumberOfItems in each section. The number of elements in each section is 42 (6 rows x 7 columns)

This is what the problem looks like: you can see that it will reach the point where the CellForItemAtIndexPath method is not called. There will be no cells. Then all of a sudden, when you go to a certain scroll point, it will be called right away, as a result of which the cells will appear.

enter image description here

Does anyone know what an error is? This error does not occur if the UICollectionView does not fit inside the UIStackView.

[edit]

Ok I narrowed down the issue to a subclass of UICollectionViewFlowLayout. There is a short line of code for changing the layout of the View collection to a horizontal position. Meaning, cells in a view are usually displayed as follows:

[1][4][7] [2][5][8] [3][6][9] //But the code should render it like this [1][2][3] [4][5][6] [7][8][9] 

I have the following code for this (taken from KDCalendar). This code works fine, but as soon as it is inside the stackView, it looks like the one shown above. Is the approach to reordering cells different when inside a stackView? the following is the code causing the problem (only inside stackView)

 override public func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? { if let attrs = super.layoutAttributesForItemAtIndexPath(indexPath) { let attrscp = attrs.copy() as! UICollectionViewLayoutAttributes self.applyLayoutAttributes(attrscp) return attrscp } return nil } override public func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { return super.layoutAttributesForElementsInRect(rect)?.map { attrs in let attrscp = attrs.copy() as! UICollectionViewLayoutAttributes self.applyLayoutAttributes(attrscp) return attrscp } } func applyLayoutAttributes(attributes : UICollectionViewLayoutAttributes) { if attributes.representedElementKind != nil {return} if let collectionView = self.collectionView { let stride = (self.scrollDirection == .Horizontal) ? collectionView.frame.size.width : collectionView.frame.size.height let offset = CGFloat(attributes.indexPath.section) * stride var xCellOffset : CGFloat = CGFloat(attributes.indexPath.item % 7) * self.itemSize.width var yCellOffset : CGFloat = CGFloat(attributes.indexPath.item / 7) * self.itemSize.height if(self.scrollDirection == .Horizontal) { xCellOffset += offset; } else { yCellOffset += offset } attributes.frame = CGRectMake(xCellOffset, yCellOffset, self.itemSize.width, self.itemSize.height) } } 

So, I think, basically my question is: which code is the fastest most efficient way to render cells horizontally, as shown above, by subclassing UICollectionViewFlowLayout? My View collection will always have 7 columns. Lines can only be 1, 2, 3, or 6.

[EDIT]

I have a problem before the following function:

 func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { //Inside this function, I have put the following code to test it. print(super.layoutAttributesForElementsInRect(rect)) } 

Here is the printout I get

  • [0]: index path: ({length = 2, path = 0 - 0}); Frame = (0, 59.1429, 72.8333);
    • 1 : index path: ({length = 2, path = 0 - 1}); frame = (0 73, 59.1429, 72.8333);
    • [2]: index path: ({length = 2, path = 0 - 2}); frame = (0 145.667; 59.1429, 72.8333);
    • [3]: index path: ({length = 2, path = 0 - 3}); frame = (0 218.667; 59.1429, 72.8333);
    • [4]: index path: ({length = 2, path = 0 - 4}); frame = (0 291.333; 59.1429, 72.8333);
    • [5]: index path: ({length = 2, path = 0 - 5}); frame = (0 364.333; 59.1429, 72.8333);
    • [6]: index path: ({length = 2, path = 0 - 6}); frame = (59 0; 59.1429, 72.8333);
    • [7]: index path: ({length = 2, path = 0 - 7}); frame = (59 73, 59.1429, 72.8333);
    • [8]: index path: ({length = 2, path = 0 - 8}); frame = (59 145.667; 59.1429, 72.8333);
    • [9]: index path: ({length = 2, path = 0 - 9}); frame = (59,218.667; 59.1429, 72.8333);
    • [10]: index path: ({length = 2, path = 0 - 10}); frame = (59,291.333; 59.1429, 72.8333);
    • [11]: index path: ({length = 2, path = 0 - 11}); frame = (59 364.333; 59.1429, 72.8333);
    • [12]: index path: ({length = 2, path = 0 - 12}); frame = (118.333, 59.1429, 72.8333);
    • [13]: index path: ({length = 2, path = 0 - 13}); frame = (118.333, 73.7529, 72.8333);
    • [14]: index path: ({length = 2, path = 0 - 14}); frame = (118.333, 145.667, 59.1429, 72.8333);
    • [15]: index path: ({length = 2, path = 0 - 15}); frame = (118.333 218.667; 59.1429 72.8333);
    • [16]: index path: ({length = 2, path = 0 - 16}); frame = (118.333 291.333; 59.1429 72.8333);
    • [17]: index path: ({length = 2, path = 0 - 17}); frame = (118.333, 364.333, 59.1429, 72.8333);
    • [18]: index path: ({length = 2, path = 0 - 18}); frame = (177.333, 59.1429, 72.8333);
    • [19]: index path: ({length = 2, path = 0 - 19}); frame = (177.333 73, 59.1429, 72.8333);
    • [20]: index path: ({length = 2, path = 0 - 20}); frame = (177.333, 145.667, 59.1429, 72.8333);
    • [21]: index path: ({length = 2, path = 0 - 21}); frame = (177.333, 218.667; 59.1429, 72.8333);
    • [22]: index path: ({length = 2, path = 0 - 22}); frame = (177.333, 291.333, 59.1429, 72.8333);
    • [23]: index path: ({length = 2, path = 0 - 23}); frame = (177.333 364.333; 59.1429, 72.8333);
    • [24]: index path: ({length = 2, path = 0 - 24}); frame = (236.667 0; 59.1429, 72.8333);
    • [25]: index path: ({length = 2, path = 0 - 25}); frame = (236.667 73, 59.1429, 72.8333);
    • [26]: index path: ({length = 2, path = 0 - 26}); frame = (236.667, 145.667, 59.1429, 72.8333);
    • [27]: index path: ({length = 2, path = 0 - 27}); frame = (236.667 218.667; 59.1429 72.8333);
    • [28]: index path: ({length = 2, path = 0 - 28}); frame = (236.667 291.333; 59.1429, 72.8333);
    • [29]: index path: ({length = 2, path = 0 - 29}); frame = (236.667 364.333; 59.1429, 72.8333);
    • [30]: index path: ({length = 2, path = 0 - 30}); frame = (295.667 0; 59.1429, 72.8333);
    • [31]: index path: ({length = 2, path = 0 - 31}); frame = (295.667 73, 59.1429, 72.8333);
    • [32]: index path: ({length = 2, path = 0 - 32}); frame = (295.667, 145.667, 59.1429, 72.8333);
    • [33]: index path: ({length = 2, path = 0 - 33}); frame = (295.667 218.667; 59.1429, 72.8333);
    • [34]: index path: ({length = 2, path = 0 - 34}); frame = (295.667, 291.333, 59.1429, 72.8333);
    • [35]: index path: ({length = 2, path = 0 - 35}); frame = (295.667 364.333; 59.1429 72.8333);
    • [36]: index path: ({length = 2, path = 0 - 36}); frame = (355, 59.1429, 72.8333);
    • [37]: index path: ({length = 2, path = 0 - 37}); frame = (355 73, 59.1429, 72.8333);
    • [38]: index path: ({length = 2, path = 0 - 38}); frame = (355, 145.667, 59.1429, 72.8333);
    • [39]: index path: ({length = 2, path = 0 - 39}); frame = (355,218.667; 59.1429, 72.8333);
    • [40]: index path: ({length = 2, path = 0 - 40}); frame = (355,291.333; 59.1429, 72.8333);
    • [41]: index path: ({length = 2, path = 0 - 41}); frame = (355 364.333; 59.1429, 72.8333);
    • [42]: index path: ({length = 2, path = 1 - 0}); frame = (414 0; 59.1429, 72.8333);
    • [43]: index path: ({length = 2, path = 1 - 1}); frame = (414 73, 59.1429, 72.8333);
    • [44]: index path: ({length = 2, path = 1 - 2}); frame = (414, 145.667, 59.1429, 72.8333);
    • [45]: index path: ({length = 2, path = 1 - 3}); frame = (414,218.667; 59.1429, 72.8333);
    • [46]: index path: ({length = 2, path = 1 - 4}); frame = (414,291.333; 59.1429, 72.8333);
    • [47]: index path: ({length = 2, path = 1 - 5}); frame = (414 364.333; 59.1429, 72.8333);
    • [48]: index path: ({length = 2, path = 1 - 6}); frame = (473, 59.1429, 72.8333);
    • [49]: index path: ({length = 2, path = 1 - 7}); frame = (473 73, 59.1429, 72.8333);
    • [50]: index path: ({length = 2, path = 1 - 8}); frame = (473, 145.667, 59.1429, 72.8333);
    • [51]: index path: ({length = 2, path = 1 - 9}); frame = (473,218.667; 59.1429, 72.8333);
    • [52]: index path: ({length = 2, path = 1 - 10}); frame = (473,291.333; 59.1429, 72.8333);
    • [53]: index path: ({length = 2, path = 1 - 11}); frame = (473,364.333; 59.1429, 72.8333);
    • [54]: index path: ({length = 2, path = 1 - 12}); frame = (532.333, 59.1429, 72.8333);
    • [55]: index path: ({length = 2, path = 1 - 13}); frame = (532.333, 73.1429, 72.8333);
    • [56]: index path: ({length = 2, path = 1 - 14}); frame = (532.333, 145.667, 59.1429, 72.8333);
    • [57]: index path: ({length = 2, path = 1 - 15}); frame = (532.333 218.667; 59.1429, 72.8333);
    • [58]: index path: ({length = 2, path = 1 - 16}); frame = (532.333, 291.333; 59.1429, 72.8333);
    • [59]: index path: ({length = 2, path = 1 - 17}); frame = (532.333 364.333; 59.1429, 72.8333);
    • [60]: index path: ({length = 2, path = 1 - 18}); frame = (591.333, 59.1429, 72.8333);
    • [61]: index path: ({length = 2, path = 1 - 19}); frame = (591.333 73, 59.1429, 72.8333);
    • [62]: index path: ({length = 2, path = 1 - 20}); frame = (591.333 145.667; 59.1429 72.8333);
    • [63]: index path: ({length = 2, path = 1 - 21}); frame = (591.333 218.667; 59.1429, 72.8333);
    • [64]: index path: ({length = 2, path = 1 - 22}); frame = (591.333 291.333; 59.1429, 72.8333);
    • [65]: index path: ({length = 2, path = 1 - 23}); frame = (591.333 364.333; 59.1429, 72.8333);
    • [66]: index path: ({length = 2, path = 1 - 24}); frame = (650.667 0; 59.1429, 72.8333);
    • [67]: index path: ({length = 2, path = 1 - 25}); frame = (650.667 73, 59.1429, 72.8333);
    • [68]: index path: ({length = 2, path = 1 - 26}); frame = (650.667 145.667; 59.1429 72.8333);
    • [69]: index path: ({length = 2, path = 1 - 27}); frame = (650.667 218.667; 59.1429 72.8333);
    • [70]: index path: ({length = 2, path = 1 - 28}); frame = (650.667 291.333; 59.1429, 72.8333);
    • [71]: index path: ({length = 2, path = 1 - 29}); frame = (650.667 364.333; 59.1429 72.8333);
    • [72]: index path: ({length = 2, path = 1 - 30}); frame = (709.667 0; 59.1429, 72.8333);
    • [73]: index path: ({length = 2, path = 1 - 31}); frame = (709.667 73, 59.1429, 72.8333);
    • [74]: index path: ({length = 2, path = 1 - 32}); frame = (709.667, 145.667, 59.1429, 72.8333);
    • [75]: index path: ({length = 2, path = 1 - 33}); frame = (709.667 218.667; 59.1429, 72.8333);
    • [76]: index path: ({length = 2, path = 1 - 34}); frame = (709.667, 291.333, 59.1429, 72.8333);
    • [77]: index path: ({length = 2, path = 1 - 35}); frame = (709.667 364.333; 59.1429 72.8333);

I see that the function asks for the index paths: 0-0 to 0-41 for section 0, which is true. BUT, it ONLY requests the index paths 1-0 to 1-35 for another section. He lacks the other 6 index patches. It only displays the remaining 6 paths when the user scrolls through the missing cells in the field of view. Is there a reason it doesn't return the whole index? Is this even the right way to detect cells in a rectangle?

+7
ios objective-c cocoa-touch swift uicollectionview
source share
2 answers

There is a fundamental flaw with your JTAppleCalendarFlowLayout .

UICollectionView relies internally on layoutAttributesForElementsInRect() to find out which cells to request from layoutAttributesForItemAtIndexPath . Since you pass rect to super unchanged, it misunderstands which cells should be visible.

To demonstrate the problem better, I made a second copy of the collection, which is in sync, but disabled the applyLayoutAttributes modification below. I also stained cells that appear too late.

demo animation

The part you need to understand is that cells that appear β€œtoo late” in the top collection are not yet visible in the original / unmodified layout. View the collection adds them only when he thinks that they will be needed in the near future. He does not understand that you are actually making them visible already.

So ... how do you fix this?

Since your layout is so simple, I would recommend you not to worry with UICollectionViewFlowLayout , because it is not designed to handle "pages" in this way. Just subclass UICollectionViewLayout and implement layoutAttributesForElementsInRect and layoutAttributesForItemAtIndexPath yourself with custom calculations.

Or, if you are really configured to use UICollectionViewFlowLayout, you will need to change the rect address that you switch to super so that in the original rect layout it includes all the cells that will be visible under your changed location.

+6
source share

If someone wants to see the implementation I made with the excellent JTBandes answer, they can find the answer here on github . The layout was done for the iOS calendar project.

There are 7 columns in the calendar view, a custom number of rows is 1, 2, 3 or 6. The above problem arose when the calendar was in horizontal mode.

The decision made based on JTBandes answer was a subclass of UICollectionViewLayout, not UICollectionViewFlowLayout. All code can be found in this file in the repository. You can also try the whole project by checking Cocoapod for this link . Thanks to everyone.

-one
source share

All Articles