将设备旋转到横向时的UICollectionViewFlowLayout大小警告

时间:2013-01-22 22:29:19

标签: ios objective-c uicollectionview uicollectionviewlayout

我们正在使用UICollectionView来显示覆盖整个屏幕的单元格(减去状态和导航栏)。单元格大小设置为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;
}

这会为每个设备设置正确的大小。每个单元格都定义为没有插入,并且布局没有页眉或页脚。但是,当我们从纵向旋转到横向时,我们得到以下“投诉”:

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.

现在我理解了这个错误,但是我们重置了单元格的大小并使用了在旋转过渡中构建的流程布局:

-(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.
}

细胞在风景中正确渲染。

似乎Flow Layout看到旧的单元格大小(因为它太高而导致投诉),但是会在didRotateFromInterfaceOrientation中读取/渲染新的单元格大小。

有没有办法摆脱投诉?

我们尝试在设备旋转转换期间找到另一个挂钩,可以访问正确的目标屏幕尺寸(与当前屏幕尺寸相比),但没有运气。调试输出显示投诉发生在willRotateToInterfaceOrientation之后但didRotateFromInterfaceOrientation之前。

我们也证实了这一点;如果我们将单元格高度设置为小于横向屏幕高度的固定大小,则不会发生投诉。此外,从横向旋转回到肖像时,不会发生投诉。

一切运行正常,渲染正确。然而,这个抱怨让我们担心。其他人有任何想法或解决方案吗?

21 个答案:

答案 0 :(得分:36)

我收到同样的警告。对“reloadData”方法不满意,我发现在设置集合视图的框架之前调用[self.collectionView.collectionViewFlowLayout invalidateLayout] 会使警告静音并产生预期结果。

答案 1 :(得分:9)

不要在这个装满但尚未接受的芭比娃娃上扔另一只虾。

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

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

在返回单元格大小之前设置内容插入对我来说非常有用。

注意:

我在故事板中使用容器视图来加载UIViewController中的集合视图。我尝试在故事板中的flowLayout对象上设置它。故事板中的集合视图。并覆盖其中一个UICollectionViewDelegateFlowLayout;虽然我不记得哪一个。我也不确定这是否适用于垂直布局。

答案 2 :(得分:6)

[UIViewController willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration]

我打电话给[UICollectionViewLayout invalidateLayout]并且看起来效果很好。

答案 3 :(得分:4)

许多解决方案都建议将invalidateLayout添加到willAnimateRotationToInterfaceOrientation - 但自iOS 8以来已弃用。

对于iOS 8及更高版本,请使用:

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

    [self.collectionView.collectionViewLayout invalidateLayout];
}

感谢@ user7097242的评论,这里有一个swift4版本:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    self.collectionView.collectionViewLayout.invalidateLayout()
}

答案 4 :(得分:4)

我解决了。

你应该让flowLayout的高度低于collcetionView.frame.size.height。

 [flowLayout setItemSize:CGSizeMake(width, height)];


 collectionView.frame = CGRectMake(12, 380, 290, 80);

答案 5 :(得分:2)

这适合我:(并希望它也适合你!)

- (void)viewWillLayoutSubviews 
{     
     self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height);    
     [self.flowLayout invalidateLayout];
}

- (void)viewDidLayoutSubviews     
{
    self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height); 
}

答案 6 :(得分:2)

我遇到了同样的问题。如果在纵向方向上显示集合视图,则在旋转到横向时单元格将消失。我在这里做了其他答案的组合来解决这个问题。

我将我的视图(包含UICollectionView的视图)设置为接收UIDeviceOrientationDidChangeNotification通知。在响应该通知的方法中,在调整UICollectionView帧之后,我执行了以下操作:

- (void)orientationChanged:(NSNotification *)notification
{
    CGSize size = (CGSize){self.frame.size.width - 2*kContentMargin, self.frame.size.height - 2*kContentMargin};
    [self.collectionView.collectionViewLayout setItemSize:size];

    [self.collectionView.collectionViewLayout invalidateLayout];
    [self.collectionView reloadData];
}

请注意,UICollectionView的框架在旋转时会自动设置,因为它的resizingMask在初始化时设置为:

self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

答案 7 :(得分:2)

刚遇到并修复了同样的问题。由于我的解决方案更符合您的要求并且与现有答案不符,我已在此处发布。

#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];
}

答案 8 :(得分:2)

我的修复就像取消选中IB中视图控制器的“调整滚动视图插图”一样简单,因为我需要我的导航栏是半透明的。

答案 9 :(得分:2)

这解决了我:

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

我正在使用此委托方法设置大小:

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

我可以从那里使用旋转后的正确框架:

self.collectionView.frame.size

答案 10 :(得分:1)

我遇到了同样的问题。 这是我如何解决它。希望它有所帮助

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(    NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    [self.collectionView reloadData];
}

答案 11 :(得分:1)

试试这个......

UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout;

layout.itemSize = CGSizeMake(0.1, 0.1);

这对我有用。

答案 12 :(得分:0)

以下代码为我修好了:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}

答案 13 :(得分:0)

我使用了UICollectionViewFlowLayoutInvalidationContext,我在其中计算新的偏移量,使其保持相同的内容偏移量。我自己的函数collectionViewSizeForOrientation:返回适当的大小。它并不完美,但至少它并不粗略:

- (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:如下,假设insets和item spacing为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);

}

答案 14 :(得分:0)

您可以继承UICollectionView并覆盖setBounds:。在调用[super setBounds:]之前,可以将项目大小调整为新边界 您应检查边界的大小是否已更改,因为在滚动时也会调用setBounds:

答案 15 :(得分:0)

我知道这是一个老问题,但我遇到了同样的问题并花了一个小时来解决它。我的问题是,看起来UICollectionView的框架大小始终是错误的(高度与容器不匹配),而我在调用UICollectionView Flow布局委托之前设置框架大小。因此,我在方法上再次设置UICollectionView帧大小:

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

这就是诀窍。高度现在正确显示,警告消失。

答案 16 :(得分:0)

试图在iOS 7上找到解决这些警告的解决方案对我来说很难。我最终使用了UICollectionView的子类,并添加了以下代码。

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

    [super setFrame:frame];
}

有些人可能希望使用CGSizeEqualToSize()进行整体尺寸检查。

答案 17 :(得分:0)

只是为了抑制警告,并且(可能不确定)在返回

中的尺寸之前提高性能

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

检查视图控制器是否正在旋转,如果它返回一些相对较小的大小,如CGSizeMake(0.1, 0.1)

答案 18 :(得分:0)

在didRotateFromInterfaceOrientation结束时尝试:

[self.collectionView setNeedsLayout]

如果它不起作用尝试将所有这些东西从didRotateFromInterfaceOrientation移动到willRotateToInterfaceOrientation

答案 19 :(得分:0)

注意:就我而言,我发现我们正在设置相关preferredContentSize UIViewController 属性。如果您发现这是您的情况,您可能必须处理UICollectionViewDelegateFlowLayout协议的以下方法:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize size = collectionViewLayout.collectionView.bounds.size;
    ...
    return size;
}

答案 20 :(得分:0)

在调整UICollectionView的框架大小时遇到​​了同样的问题。如果我使用FlowLayout上的委托方法返回单元格的大小(将根据包含的UICollectionView的大小进行更新),当我调整大小(更小)UICollectionView的框架时,我会收到错误消息,因为它在抱怨之前似乎没有要求代表提供更新的尺寸信息。它最终会在重绘时询问委托方法的大小信息,但在我分配新的帧方法时它仍然会发出警告。为了摆脱警告,我在之前显式设置了UICollectionViewFlowLayout对象的itemSize属性。我将帧设置为一个新的较小值。我的情况很简单,我认为我可以在这一点上完成itemSize计算(因为我在集合视图中的所有项目都是相同的大小),而不是依赖于委托方法。只是在实现委托方法的同时设置itemSize属性 not 解决了问题,因为我认为如果它检测到委托方法已经实现(如果它知道它在那里,则忽略了itemSize的值)为什么不称它?!)。希望这会有所帮助 - 也许你也可以在轮换前明确设置itemSize。