lionheart / openradar-mirror

A mirror of radars pulled from http://openradar.me/.
245 stars 17 forks source link

27519174: UICollectionViewLayout not respecting element attributes z-index #15453

Open openradar-mirror opened 8 years ago

openradar-mirror commented 8 years ago

Description

Summary: We’ve got a custom UICollectionViewFlowLayout subclass that consists of a number of decoration views as “section” backgrounds, interactive supplementary views that act as tappable headers to expand/collapse sections, and obviously cells, which are arranged in a section in a simple grid flow layout. In iOS 8 and 9 this layout has worked perfectly, but using it on iOS 10, we noticed that cells were missing. In debugging this problem we’ve found that it appears that in iOS 10 the UICollectionViewLayoutAttributes zIndex property isn’t being properly obeyed, and some cells, and supplementary views (headers) are being presented behind our decoration view backgrounds.

Setting the section decoration view background to translucent shows the cells happily placed underneath. Also, inspecting the views in with the View Hierarchy debugger, shows the cells on the stack, but not in the proper hierarchy.

Steps to Reproduce:

  1. Implement UICollectionViewFlowLayout which lays out a single decoration view for each section, supplementary view for each section, and multiple cells.
  2. Provide a zIndex value of -1 (or 0) to the decoration view attributes in layoutAttributesForDecorationViewOfKind.
  3. Provide a zIndex value of 10 to the cell attributes in layoutAttributesForItemAtIndexPath.
  4. Provide a zIndex value of 20 to the section header (supplementary view) attributes in layoutAttributesForSupplementaryViewOfKind.
  5. While running, if the layout is invalidated/reloaded, cells/supplementary views will randomly appear behind the decoration view, even though elements of both kinds have a higher int value for zIndex

Expected Results: When the layout is invalidated/reloaded, cells and supplementary/decoration views are presented top to bottom layout order obeys the statement of “Items with higher index values appear on top of items with lower values.” as described in the UICollectionViewLayoutAttributes documentation.

Actual Results: When the layout is invalidated/reloaded, cells/supplementary views will randomly appear behind the decoration view, even though elements of both kinds have a higher int value for zIndex.

Version: iOS 10.0 (14A5309d)

Notes: Attached are two screenshots captured during the expand/collapse (ie. add/delete of section items) animations, showing cells being presented under the decoration view background.

Configuration: iPhone 6s / iPad Air 2, iOS Sim

Attachments: 'section-expand.png' and 'section-collapsed.png' were successfully uploaded.

Product Version: iOS 10.0 Created: 2016-07-25T05:37:50.166650 Originated: 2016-07-25T00:00:00 Open Radar Link: http://www.openradar.me/27519174

maksa commented 7 years ago

In my experience attribute zIndex is obeyed when attributes are returned from layoutAttributesForElementsInRect(...). However attributes returned from layoutAttributesForSupplementaryViewOfKind and layoutAttributesForItemAtIndexPath are for some reason ignored. My current workaround is to explicitly set the layer.zPosition in code when returning the cell for item and supplementary view in the UICollectionViewDataSource implementation and setting layer.zPosition for the decoration view in the Interface Builder as a custom attribute.

However it's still unpleasant and is a real issue.

yanhsiah commented 6 years ago

I got another workaround. Since all the cells belong to the same superview, calling bringSubviewToFront : when cell displaying works. Specifically, by looking into Debug View Hierarchy, though UICollectionViewLayout not renders cells according to zIndex, it still shows cells according to the reverse order that each subview being added to it's super view.

mahboud commented 6 years ago

I find that it is best to set the zIndex of the cell layer either in the

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath;
  {
...
    cell.layer.zPosition = indexPath.item;
    return cell;

}

or in the cell class:

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
    [super applyLayoutAttributes:layoutAttributes];
    self.layer.zPosition = layoutAttributes.zIndex;
}
rafalur commented 6 years ago

In my case the problem caused overlapping cells by background supplementary view. My workaround for this is:

    public func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {
        // workaround for https://github.com/lionheart/openradar-mirror/issues/15453
        collectionView.sendSubview(toBack: view)
    }
YK-Unit commented 6 years ago

I try the solution from @mahboud and @rafalur, but it don't work. At last, I solve it by this way:

here is the code

class YourCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()

        var decorationViews: [UIView] = []

        self.subviews.forEach { (subview) in
            if let decorationView = subview as? YourDecorationReusableView {
                decorationViews.append(decorationView)
            }
        }

        decorationViews.forEach { (decorationView) in
            self.sendSubview(toBack: decorationView)
        }
    }
}

Here is my Demo Code

pqteru commented 6 years ago

@YK-Unit your solution works great! Thanks so much

iosdec commented 2 years ago

i Find tha tit is best to set the zIndex of the cell layer either in the

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath;
  {
...
    cell.layer.zPosition = indexPath.item;
    return cell;

}

or in the cell class:

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
    [super applyLayoutAttributes:layoutAttributes];
    self.layer.zPosition = layoutAttributes.zIndex;
}

I recently experienced z indexing issue with both supplementary / decoration views, this solved the problem perfectly. I will use this in every supplementary / decoration subclass in future!

Thanks so much!