ios UICollectionViewFlowLayout 在方向改变时不会失效

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16341778/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:25:39  来源:igfitidea点击:

UICollectionViewFlowLayout not invalidating right on orientation change

iosuicollectionviewuicollectionviewlayout

提问by xxtesaxx

i have a UICollectionView with a UICollectionViewFlowLayout. I also implement the UICollectionViewDelegateFlowLayout protocol.

我有一个带有 UICollectionViewFlowLayout 的 UICollectionView。我还实现了 UICollectionViewDelegateFlowLayout 协议。

In my datasource i have a bunch of UIViewControllers which respond to a custom protocol so i can ask for their size and some other stuff.

在我的数据源中,我有一堆 UIViewControllers 响应自定义协议,因此我可以询问它们的大小和其他一些东西。

In in the FlowLayout delegate when it asks for the sizeForItemAtIndexPath:, i return the item size which i get from my protocol. The ViewControllers which implement my protocol return a different item size depending on the orientation.

在 FlowLayout 委托中,当它要求 sizeForItemAtIndexPath: 时,我返回从我的协议中获得的项目大小。实现我的协议的 ViewControllers 根据方向返回不同的项目大小。

Now if i change the device orientation from portrait to landscape theres no problem (Items are larger in landscape) but if i change it back, i get this warning:

现在,如果我将设备方向从纵向更改为横向,则没有问题(横向项目较大)但如果我将其改回来,则会收到此警告:

    the item width must be less that the width of the UICollectionView minus the section insets left and right values.
    Please check the values return by the delegate.

It still works but i don't like it to get warnings so maybe you can tell me what i am doing wrong. Also there is another problem. If i don't tell my collectionViews collectionViewLayout to invalidate in willAnimateRotationToInterfaceOrientation: the sizeForItemAtIndexPath: is never called.

它仍然有效,但我不喜欢它收到警告,所以也许你可以告诉我我做错了什么。另外还有一个问题。如果我不告诉我的 collectionViews collectionViewLayout 在 willAnimateRotationToInterfaceOrientation 中失效: sizeForItemAtIndexPath: 永远不会被调用。

Hope you understand what i mean. If you need additional information let me know :)

希望你明白我的意思。如果您需要其他信息,请告诉我:)

采纳答案by kwigbo

One solution is to use KVO and listen for a change to the frame of the superview for your collection view. Calling -reloadData will work and get rid of the warnings. There is an underlying timing issue here...

一种解决方案是使用 KVO 并侦听集合视图的超级视图框架的更改。调用 -reloadData 将起作用并消除警告。这里有一个潜在的时间问题......

// -loadView
[self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

// -dealloc
[self.view removeObserver:self forKeyPath:@"frame"];

// KVO Method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [collectionView_ reloadData];
}

回答by Bob Wakefield

This answer is late, but the accepted answer didn't work for me. I sympathize with the OP in wanting a fire-and-forget UICollectionViewFlowLayout. I suggest that invalidating the layout in the view controller is in fact the best solution.

这个答案很晚,但接受的答案对我不起作用。我对 OP 想要一个即发即忘的 UICollectionViewFlowLayout 表示同情。我建议使视图控制器中的布局无效实际上是最好的解决方案。

I wanted a single horizontal scrolling line of cells, centered in the view, in both portrait and landscape.

我想要一个水平滚动的单元格线,在视图中居中,纵向和横向。

I subclassed UICollectionViewFlowLayout.

我继承了 UICollectionViewFlowLayout。

I overrode prepareLayout to recalculate the insets and then call [super prepareLayout].

我覆盖了 prepareLayout 以重新计算插图,然后调用 [super prepareLayout]。

I overrode the getter for collectionViewContentSize to make certainthe content size was correct.

我推翻了getter方法collectionViewContentSize使某些内容大小是正确的。

The layout didn't invalidate on its own even though the bounds were changing with the reorientation.

即使边界随着重新定向而改变,布局也不会自行失效。

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // does the superclass do anything at this point?
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    // do whatever else you need before rotating toInterfaceOrientation

    // tell the layout to recalculate
    [self.collectionViewLayout invalidateLayout];
}

The UICollectionViewFlowLayout maintains the scroll position between orientations. The same cell will be centered onlyif it is square.

UICollectionViewFlowLayout 维护方向之间的滚动位置。只有当它是正方形时,相同的单元格才会居中。

回答by cynistersix

I used a combination of the two answers here to do what we are all trying to do really is make a collection view look like a table view but not use UITableView and get items in a single column (most likely)

我在这里使用了两个答案的组合来做我们都想做的事情,实际上是使集合视图看起来像表视图,但不使用 UITableView 并在单列中获取项目(最有可能)

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

    [self.collectionView.collectionViewLayout invalidateLayout];
}

回答by Kenny Winker

A variation on @meelawsh's answer, but in Swift, and without a self.sizeForCollectionViewproperty:

@meelawsh 答案的变体,但在 Swift 中,并且没有self.sizeForCollectionView属性:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    guard let collectionView = self.collectionView else {
        return
    }

    if size.width > collectionView.frame.width {
        // bigger

        let animation: (UIViewControllerTransitionCoordinatorContext)->(Void) = {
            context in
            collectionView.collectionViewLayout.invalidateLayout()
        }

        coordinator.animateAlongsideTransition(animation, completion: nil)
    } else {
        // smaller
        collectionView.collectionViewLayout.invalidateLayout()
    }
}

回答by meelawsh

iOS8: the only way I could silence the warning is to call invalidate during the transition when rotating to a larger size and before transition when rotating to a smaller size.

iOS8:我可以使警告静音的唯一方法是在旋转到较大尺寸时的过渡期间和旋转到较小尺寸时在过渡之前调用 invalidate。

    self.sizeForCollectionView = size; //used to determine collectionview size in sizeForItem
    if (size.width <= size.height) {
        [self.collectionView.collectionViewLayout invalidateLayout];
    }

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        if (size.width > size.height) {
            [self.collectionView.collectionViewLayout invalidateLayout];
        }
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    }];

回答by MiguelHincapieC

I don't know if it can help to someone but none of the last answer help me because I'm using some UIViewsinto a UIScrollView.

I'm opening the UIViews programmatically depending on some events. So I can't use - (void) willRotateToInterfaceOrientation:and invalidateLayoutor reloadDatabecause:
1. invalidateLayout "occurs during the next view layout update cycle."
2. reloadData "For efficiency, the collection view only displays those cells and supplementary views that are visible."

What worked for me was use the width of the collectionView's parent (an UIView) in:

我不知道它是否可以帮助某人,但最后一个答案都没有帮助我,因为我正在将一些UIViews用于UIScrollView.

我正在根据某些事件以编程方式打开 UIViews。所以我不能使用- (void) willRotateToInterfaceOrientation:invalidateLayoutreloadData因为:
1. invalidateLayout “发生在下一个视图布局更新周期。”
2. reloadData “为了效率,集合视图只显示那些可见的单元格和补充视图。”

对我有用的是在以下位置使用 collectionView 的父级(UIView)的宽度:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
int currentWidth = self.frame.size.width;

UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout sectionInset];
int fixedWidth = currentWidth - (sectionInset.left + sectionInset.right);


For some reason the collectionView frame had the width of portrait at the time to call this function... using int currentWidth = self.frame.widthhelp me to fix it.


出于某种原因,collectionView 框架在调用这个函数时有纵向的宽度......使用int currentWidth = self.frame.width帮助我修复它。

回答by Pedro Rom?o

The @0mahc0's answer worked for me, but only partially.

@0mahc0 的答案对我有用,但只是部分。

I don't have the rotate problem, because I force my app be only in Portrait mode, but my solution can help others with the same problem.

我没有旋转问题,因为我强制我的应用程序只能处于纵向模式,但我的解决方案可以帮助其他人解决同样的问题。

The warning shows every time the view appears. The reason of the warning is very clear, we have defined section insets to left and right of UICollectionView. Check interface builder and you will see in metrics those values defined.

每次出现视图时都会显示警告。警告的原因很清楚,我们在 UICollectionView 的左右两侧定义了 section insets。检查界面构建器,您将在指标中看到定义的这些值。

The @0mahc0's solution works, but I want to stretch the cell to max width, so I removed the insets of section, otherwise I will have a 'margin' left and right.

@0mahc0 的解决方案有效,但我想将单元格拉伸到最大宽度,所以我删除了部分的插图,否则我的左右会有一个“边距”。

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsZero;
}

Instead of implementing the delegate method you can go to interface builder and change value to zero of right and left insets.

您可以转到界面构建器并将左右插图的值更改为零,而不是实现委托方法。