4

I am trying to develop a UICollectionViewFlowLayout subclass that lays out items like a UITableView. The items use auto layout, and I also support supplementary header views.

open override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let attributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) else { return nil }
    print("Section Attributes: \(attributes)")
    return attributes
}

override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    let attributes = super.layoutAttributesForItem(at: indexPath)
    if let attributes = attributes {
        guard let collectionView = collectionView else { return attributes }
        attributes.bounds.size.width = collectionView.bounds.width - sectionInset.left - sectionInset.right
    }
    return attributes
}

override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
{
    print("\nlayoutAttributesForElements: \(rect)")

    let allAttributes = super.layoutAttributesForElements(in: rect)
    return allAttributes?.flatMap { attributes in
        switch attributes.representedElementCategory {
        case .cell:
            print("\ncell: \(attributes.indexPath)");
            print("cell_old Attributes: \(attributes)")
            let a = layoutAttributesForItem(at: attributes.indexPath)
            print("cell_new Attributes: \(a)")
            return a
        case .supplementaryView:
            print("\nsupp: \(attributes.indexPath)");
            print("supp_old Attributes: \(attributes)")
            let a = layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: attributes.indexPath)
            print("supp_new Attributes: \(a)")
            return a
        default: return attributes
        }
    }
}

Additionally, my UICollectionViewCell subclass overrides preferredLayoutAttributesFitting:

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes
{
    layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
    return layoutAttributes
}

This...sort of works. The items are laid out correctly, but section header views draw at the wrong y coordinates. I can hack it by calling collectionView.reloadData() in the view's controller, but this obviously requires everything to be loaded twice, which is wrong.

Debug output shows that layoutAttributesForElements gets called three times before I call reloadData():

layoutAttributesForElements: (0.0, 0.0, 375.0, 667.0)

cell: [0, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fa100> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); frame = (6 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fd000> index path: (<NSIndexPath: 0x60000023de40> {length = 2, path = 0 - 0}); frame = (-150.5 56; 363 50); )

cell: [0, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fac00> index path: (<NSIndexPath: 0xc000000000200016> {length = 2, path = 0 - 1}); frame = (68.5 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fb200> index path: (<NSIndexPath: 0x60000023de20> {length = 2, path = 0 - 1}); frame = (-88 56; 363 50); )

cell: [0, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fa400> index path: (<NSIndexPath: 0xc000000000400016> {length = 2, path = 0 - 2}); frame = (131 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fd900> index path: (<NSIndexPath: 0x60000023dde0> {length = 2, path = 0 - 2}); frame = (-25.5 56; 363 50); )

cell: [1, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fa900> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); frame = (6 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fd800> index path: (<NSIndexPath: 0x60000023de00> {length = 2, path = 1 - 0}); frame = (-150.5 178; 363 50); )

cell: [1, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001faf00> index path: (<NSIndexPath: 0xc000000000200116> {length = 2, path = 1 - 1}); frame = (68.5 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fdb00> index path: (<NSIndexPath: 0x60000023dcc0> {length = 2, path = 1 - 1}); frame = (-88 178; 363 50); )

cell: [1, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fb600> index path: (<NSIndexPath: 0xc000000000400116> {length = 2, path = 1 - 2}); frame = (131 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fda00> index path: (<NSIndexPath: 0x600000026760> {length = 2, path = 1 - 2}); frame = (-25.5 178; 363 50); )

supp: [0, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fb700> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6000001fe100> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000001fe100> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; )

supp: [1, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fb800> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6180001fb900> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fb900> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; )

layoutAttributesForElements: (-667.0, -667.0, 1042.0, 1334.0)

cell: [0, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fba00> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); frame = (6 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc200> index path: (<NSIndexPath: 0x61800002b7a0> {length = 2, path = 0 - 0}); frame = (-150.5 56; 363 50); )

cell: [0, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fbb00> index path: (<NSIndexPath: 0xc000000000200016> {length = 2, path = 0 - 1}); frame = (68.5 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc300> index path: (<NSIndexPath: 0x61800002b820> {length = 2, path = 0 - 1}); frame = (-88 56; 363 50); )

cell: [0, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fbc00> index path: (<NSIndexPath: 0xc000000000400016> {length = 2, path = 0 - 2}); frame = (131 56; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc400> index path: (<NSIndexPath: 0x61800002b680> {length = 2, path = 0 - 2}); frame = (-25.5 56; 363 50); )

cell: [1, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fbd00> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); frame = (6 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc500> index path: (<NSIndexPath: 0x61800002b600> {length = 2, path = 1 - 0}); frame = (-150.5 178; 363 50); )

cell: [1, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fbe00> index path: (<NSIndexPath: 0xc000000000200116> {length = 2, path = 1 - 1}); frame = (68.5 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc600> index path: (<NSIndexPath: 0x6180000281a0> {length = 2, path = 1 - 1}); frame = (-88 178; 363 50); )

cell: [1, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fbf00> index path: (<NSIndexPath: 0xc000000000400116> {length = 2, path = 1 - 2}); frame = (131 178; 50 50); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc700> index path: (<NSIndexPath: 0x6180000296c0> {length = 2, path = 1 - 2}); frame = (-25.5 178; 363 50); )

supp: [0, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fc000> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6180001fc800> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6180001fc800> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; )

supp: [1, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6180001fc100> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6100001fa900> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6100001fa900> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 122; 375 40); zIndex = 10; )

layoutAttributesForElements: (-667.0, -667.0, 1042.0, 1334.0)

cell: [0, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e0e00> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); frame = (6 56; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000003e1600> index path: (<NSIndexPath: 0x600000233120> {length = 2, path = 0 - 0}); frame = (6 56; 363 45); )

cell: [0, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e0f00> index path: (<NSIndexPath: 0xc000000000200016> {length = 2, path = 0 - 1}); frame = (6 107; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000003e1700> index path: (<NSIndexPath: 0x600000233180> {length = 2, path = 0 - 1}); frame = (6 107; 363 45); )

cell: [0, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1000> index path: (<NSIndexPath: 0xc000000000400016> {length = 2, path = 0 - 2}); frame = (6 158; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000003e1800> index path: (<NSIndexPath: 0x6000002331a0> {length = 2, path = 0 - 2}); frame = (6 158; 363 45); )

cell: [1, 0]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1100> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); frame = (6 275; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6080001fa800> index path: (<NSIndexPath: 0x608000035300> {length = 2, path = 1 - 0}); frame = (6 275; 363 45); )

cell: [1, 1]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1200> index path: (<NSIndexPath: 0xc000000000200116> {length = 2, path = 1 - 1}); frame = (6 326; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6080001fb200> index path: (<NSIndexPath: 0x608000035500> {length = 2, path = 1 - 1}); frame = (6 326; 363 45); )

cell: [1, 2]
cell_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1300> index path: (<NSIndexPath: 0xc000000000400116> {length = 2, path = 1 - 2}); frame = (6 377; 363 45); 
cell_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6080001fb100> index path: (<NSIndexPath: 0x608000034d00> {length = 2, path = 1 - 2}); frame = (6 377; 363 45); )

supp: [0, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1400> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1900> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000003e1900> index path: (<NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 375 40); zIndex = 10; )

supp: [1, 0]
supp_old Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1500> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 219; 375 40); zIndex = 10; 
Section Attributes: <UICollectionViewLayoutAttributes: 0x6000003e1a00> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 219; 375 40); zIndex = 10; 
supp_new Attributes: Optional(<UICollectionViewLayoutAttributes: 0x6000003e1a00> index path: (<NSIndexPath: 0xc000000000000116> {length = 2, path = 1 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 219; 375 40); zIndex = 10; )

In the final pass of layoutAttributesForElements, the values are correct for both the items and supplementary views, but for some reason the supplementary views are not drawing at that position. They draw at the position in the first two passes.

What's going on here? Why the multiple passes, and why isn't the third pass being used for the supplementary views? Is there a better way to implement this?

GoldenJoe
  • 7,874
  • 7
  • 53
  • 92
  • Hey, I had the same issue if I remeber right. Check this out: https://stackoverflow.com/a/44226917/1105464 – choli Sep 12 '18 at 05:38

0 Answers0