UICollectionViewFlowLayout Size Warning When Rotat

2020-05-17 00:20发布

We are using a UICollectionView to display cell that cover the full screen (minus the status and nav bar). The cell size is set from self.collectionView.bounds.size:

- (void)viewWillAppear:(BOOL)animated
{
    //
    // value isn't correct with the top bars until here
    //
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
}

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    return _currentCellSize;
}

This sets the correct sizing for each device. Each cell is defined to have no insets, and the layout has no header or footer. However, when we rotate from portrait to landscape we get the following "complaint":

the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.

Now I understand this error, however we reset the size of the cell and use the flow layouts built in rotation transition:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    //self.collectionView.bounds are still the last size...not the new size here
}
- (void)didRotateFromInterfaceOrientation: UIInterfaceOrientation)fromInterfaceOrientation
{
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
    [self.collectionView performBatchUpdates:nil completion:nil];//this will force the redraw/size of the cells.
}

The cells render correctly in landscape.

It seems as though the Flow Layout sees the old cell size (which causes the complaint since it will be too tall), but does read/render the new cell size set in didRotateFromInterfaceOrientation.

Is there a way to get rid of the complaint?

We've tried finding another hook during a device rotate transition that has access to the correct target screen size (vs the current screen size) with no luck. Debug output shows the complaint happens after willRotateToInterfaceOrientation but before didRotateFromInterfaceOrientation.

We've also verified the obvious; if we set up the cell height to be a fixed size less than the landscape screen height, the complaint doesn't occur. Also, the complaint does not occur when rotating from landscape back to portrait.

Everything runs fine, and renders correctly. However this complaint worries us. Anyone else have any ideas or solutions?

21条回答
甜甜的少女心
2楼-- · 2020-05-17 00:48

A lot of the solutions suggest adding invalidateLayout to willAnimateRotationToInterfaceOrientation - but this is deprecated since iOS 8.

For iOS 8 and higher, use:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [self.collectionView.collectionViewLayout invalidateLayout];
}

Thanks to @user7097242's comment here is a swift4 version:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    self.collectionView.collectionViewLayout.invalidateLayout()
}
查看更多
狗以群分
3楼-- · 2020-05-17 00:48

I ran into the same problem when resizing the frame of a UICollectionView. If I used the delegate method on FlowLayout to return the size of the cell (which would be updated based on the size of the containing UICollectionView), I would get the error message when I resized (smaller) the frame of the UICollectionView, since it didn't seem to ask the delegate for updated size information before complaining. It would eventually ask the delegate method for size info when redrawing, but it would still issue the warning at the time I assigned a new frame method. To get rid of the warning, I explicitly set the itemSize property of the UICollectionViewFlowLayout object before I set the frame to a new smaller value. My situation is simple enough that I think I can get away with doing the itemSize calculation at this point (since all my items in the collection view are the same size), instead of depending on the delegate method. Just setting the itemSize property while leaving the delegate method implemented did not solve the problem, as I think it ignored the value of itemSize if it detected that the delegate method was implemented (if it knows it is there, why doesn't it call it?!). Hopefully this helps - perhaps you can also explicitly set the itemSize before rotation.

查看更多
时光不老,我们不散
4楼-- · 2020-05-17 00:49

Trying to find a solution to silence these warnings on iOS 7 was proving difficult for me. I ended up resorting to subclassing my UICollectionView and added the following code.

- (void)setFrame:(CGRect)frame
{
    if (!iOS8 && (frame.size.width != self.frame.size.width))
    {
        [self.collectionViewLayout invalidateLayout];
    }

    [super setFrame:frame];
}

Some might want to do a whole size check with CGSizeEqualToSize().

查看更多
登录 后发表回答