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:38

I was getting the same warning. Unsatisfied with the "reloadData" approach, I found that calling [self.collectionView.collectionViewFlowLayout invalidateLayout] before setting the frame of the collection view silenced the warning and yielded the expected results.

查看更多
Lonely孤独者°
3楼-- · 2020-05-17 00:39

Just encountered and fixed the same problem. Since my solution is more along the lines you were asking for and doesn't match any existing answer, I've posted it here.

#define NUMBER_OF_CELLS_PER_ROW 1

- (UICollectionViewFlowLayout *)flowLayout {
    return (UICollectionViewFlowLayout *)self.collectionViewLayout;
}

- (CGSize)itemSizeInCurrentOrientation {
    CGFloat windowWidth = self.collectionView.window.bounds.size.width;

    CGFloat width = (windowWidth - (self.flowLayout.minimumInteritemSpacing * (NUMBER_OF_CELLS_PER_ROW - 1)) - self.flowLayout.sectionInset.left - self.flowLayout.sectionInset.right)/NUMBER_OF_CELLS_PER_ROW;

    CGFloat height = 80.0f;

   return CGSizeMake(width, height);
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.flowLayout invalidateLayout];
    self.flowLayout.itemSize = [self itemSizeInCurrentOrientation];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // Now that the rotation is complete, load the cells.
    [self.collectionView reloadData];
}
查看更多
我想做一个坏孩纸
4楼-- · 2020-05-17 00:40

This solved it for me:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

And I am using this delegate method for setting the size:

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

And I can from there use this with the correct frame after rotation:

self.collectionView.frame.size
查看更多
女痞
5楼-- · 2020-05-17 00:40

I've used an UICollectionViewFlowLayoutInvalidationContext, in which I calculate the new offset such that it maintains the same content offset. My own function collectionViewSizeForOrientation: returns the proper size. Its not perfect, but at least it's not sketchy:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    CGSize fromCollectionViewSize = [self collectionViewSizeForOrientation:[self interfaceOrientation]];
    CGSize toCollectionViewSize = [self collectionViewSizeForOrientation:toInterfaceOrientation];

    CGFloat currentPage = [_collectionView contentOffset].x / [_collectionView bounds].size.width;
    NSInteger itemCount = [_collectionView numberOfItemsInSection:0];

    UICollectionViewFlowLayoutInvalidationContext *invalidationContext = [[UICollectionViewFlowLayoutInvalidationContext alloc] init];

    [invalidationContext setContentSizeAdjustment:CGSizeMake(toCollectionViewSize.width * itemCount - fromCollectionViewSize.width * itemCount, toCollectionViewSize.height - fromCollectionViewSize.height)];
    [invalidationContext setContentOffsetAdjustment:CGPointMake(currentPage * toCollectionViewSize.width - [_collectionView contentOffset].x, 0)];

    [[_collectionView collectionViewLayout] invalidateLayoutWithContext:invalidationContext];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

collectionViewSizeForOrientation: in my case is the following, assuming that insets and item spacing are 0:

- (CGSize)collectionViewSizeForOrientation:(UIInterfaceOrientation)orientation
{
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;

    CGFloat width = UIInterfaceOrientationIsLandscape(orientation) ? MAX(screenSize.width, screenSize.height) : MIN(screenSize.width, screenSize.height);
    CGFloat height = UIInterfaceOrientationIsLandscape(orientation) ? MIN(screenSize.width, screenSize.height) : MAX(screenSize.width, screenSize.height);

    return CGSizeMake(width, height);

}

查看更多
地球回转人心会变
6楼-- · 2020-05-17 00:42

Not to throw another shrimp on this loaded, yet unaccepted, barbie.

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

    [collectionView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
    return CGSizeMake(100, collectionView.frame.size.height);
}

Setting the content insets just before returning the cell size did the trick for me.

Note:

I am using a container view in a storyboard to load the collection view within a UIViewController. I tried setting this on the flowLayout object in the storyboard. The collection view in the storyboard. And overriding one of the UICollectionViewDelegateFlowLayout; though I do not remember which one. I'm also not sure if this will work for a vertical layout.

查看更多
ゆ 、 Hurt°
7楼-- · 2020-05-17 00:42

My fix was as simple as unchecking 'Adjust Scroll View Insets' for the view controller in IB, since I needed my navigation bar to be translucent.

查看更多
登录 后发表回答