Objective c setCollectionViewLayout:动画导致调试错误:快照未渲染的视图会导致空快照

Objective c setCollectionViewLayout:动画导致调试错误:快照未渲染的视图会导致空快照,objective-c,ios7,uicollectionviewlayout,Objective C,Ios7,Uicollectionviewlayout,我正忙于iOS7中的UICollectionView 我正在两个不同的布局之间更改集合视图的布局。是UICollectionViewFlowLayout的子类 这是我在点击按钮时更改视图的方式: -(void)changeViewLayoutButtonPressed { self.changeLayout = !self.changeLayout; if (self.changeLayout){ [self.tableViewLayout invalidate

我正忙于iOS7中的UICollectionView

我正在两个不同的布局之间更改集合视图的布局。是UICollectionViewFlowLayout的子类

这是我在点击按钮时更改视图的方式:

-(void)changeViewLayoutButtonPressed
{
    self.changeLayout = !self.changeLayout;

    if (self.changeLayout){
        [self.tableViewLayout invalidateLayout];
        [self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];

    }

    else {

        [self.grideLayout invalidateLayout];
        [self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];

    }
}
此方法按预期工作,并通过漂亮的动画更改视图。但是,在控制台中,我收到以下消息:

对尚未渲染的视图进行快照将导致空视图 快照确保您的视图以前至少已渲染过一次 屏幕更新后的快照或快照

你知道是什么引起的吗

当方法
-(UIView*)SnapshotViewAfterScreen更新时:
在尚未渲染的视图上生成此控制台警告

您正在某处调用此方法,或者更可能的情况是,
setCollectionViewLayout:animated:
正在使用此方法,并且集合视图在使用时尚未呈现


我的建议是不要在集合视图上调用
-(void)invalidateLayout
,看看会发生什么。

请不要扩展上面的评论

关于我收到的警告,奇怪的是我的简历有三个可能的数据源。所有这些数据基本上与:收集1、收集2、收集1和收集2相同

当我刚刚查看集合1或集合2时,没有收到任何警告。只有当我看到这两个组合时,我才收到警告。奇怪的是,没有什么东西可以展示一切。集合1和集合2是从“整体”中提取的,因此需要在其中多做一步。不知道这意味着什么

无论如何,我不再收到这些信息了

以下是我最初的想法:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self arrangeCollectionView];
}

- (void)arrangeCollectionView
{
    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.cv.collectionViewLayout;
    if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
    {
        flowLayout.sectionInset =  UIEdgeInsetsMake(16, 16, 16, 16);
        flowLayout.minimumInteritemSpacing = 16;
        flowLayout.minimumLineSpacing = 16;
    }
    else
    {
        flowLayout.sectionInset =  UIEdgeInsetsMake(26, 26, 26, 26);
        flowLayout.minimumInteritemSpacing = 26;
        flowLayout.minimumLineSpacing = 26;
    }
    self.cv.collectionViewLayout = flowLayout;    
}
这给了我关于旋转到风景的信息,而不是旋转到肖像的信息

这两项更改都不会产生更多消息:

1。 使用

didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

调用
arrangeCollectionView
而不是

willAnimateRotationInterfaceOrientation:(UIInterfaceOrientation)到InterfaceOrientation持续时间:(NSTimeInterval)持续时间

解决了警告问题,但使转换更加粗糙

2。 摆脱对
UICollectionViewFlowLayout
的显式更改,使用
DelegateFlowLayout
方法并从
willAnimateRotation…
调用
reloadData

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self.cv reloadData];
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGSize retval = CGSizeMake(172, 256);
    return retval;
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? UIEdgeInsetsMake(16 + 66, 16, 16, 16) : UIEdgeInsetsMake(26 + 66, 26, 26, 26);
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 16 : 28;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return UIInterfaceOrientationIsPortrait(self.interfaceOrientation) ? 16 : 28;
}
这对我最有效。没有更多的消息,从一个布局到另一个布局的过渡相当平稳

希望这能帮助你找到它。我猜在流布局失效期间会拍摄一个屏幕截图并呈现给用户,同时在
willRotate…
中重置布局时拍摄此快照。。。也许吧。仍然不确定为什么我得到的2个数据子集的消息总是为零,而整个数据集的消息总是一致的


祝你好运,希望有帮助。

我遇到了同样的问题。我在我的收藏视图中使用了
UICollectionViewFlowLayout
。我想根据界面方向来安排布局

像往常一样,我超越了
WillAnimateRotationInterfaceOrientation:duration

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
    layout.itemSize = aNewSize;
    [layout invalidateLayout];
}
控制台中出现了相同的消息。也许是动画让快照失败了。我想,子视图没有足够的时间被捕获

后来,我尝试了你的解决方案。我使用委托方法,而不是直接指定项目大小。然而,模拟器仍然告诉我有警告。这有点奇怪。在调试期间,我发现如果单元格总数只有4个,控制台将不会显示警告消息。当我把号码改为10时,信息又出现了。不知道为什么

让我们继续。我想这个问题可能是因为在动画制作过程中做了一些布局工作。因此,我将代码移动到
willRotateToInterfaceOrientation:duration:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
    layout.itemSize = aNewSize;
    [self.collectionViewLayout invalidateLayout];
} 
此方法是在第一个方法之前调用的。而且,消息不再显示了


顺便说一句,如果要动态排列单元格,则应注意视图边界的变化。在
willRotateToInterfaceOrientation:duration:
中,
interfaceOrientation
属性和视图边界仍然是旧值。但是当事情发展到
WillAnimateRotationInterfaceOrientation:duration:
时,这些值被更新了。

这个问题也发生在我身上。 我通过调用:
UIScreen.mainScreen().SnapshotViewAfterScreen更新(true)
before
setCollectionViewLayout
方法来解决这个问题。 我也不知道为什么。
我希望它能有所帮助。

我遇到了这个问题,因为我只在横向模式下使用UICollectionView,第一次旋转到横向模式时(也是第一次),我会得到许多关于快照的日志条目。我意识到日志条目的数量与单元格+标题视图的数量匹配,因此我将此代码添加到自定义单元格类和标题视图类中,日志条目消失了:

- (UIView*)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates
{
    UIGraphicsBeginImageContext(self.bounds.size);

    [self drawRect:self.bounds];

    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIImageView* aView = [[UIImageView alloc] initWithFrame:self.bounds];

    [aView setImage:viewImage];

    return aView;
}

我需要做更多的测试,看看这是否是正确的方法,但现在它正在工作。

当我试图自己解决这个问题时,由于同样的调试错误以及在动画中看到一些flash,我尝试了以下方法,效果很好:

private var isListLayout = true{
    didSet{
        UIScreen.main.snapshotView(afterScreenUpdates: true)
        collectionView!.collectionViewLayout.invalidateLayout()
        if isListLayout {
            DispatchQueue.main.async {
               self.collectionView!.setCollectionViewLayout(ListLayout(), animated: true)
            }
        }else{
            DispatchQueue.main.async {
                self.collectionView!.setCollectionViewLayout(GridLayout(), animated: true)
            }
        }
    }
}
如果忽略var,关键是调用方法:

UIScreen.main.snapshotView(AfterScreen更新:true)
以及在下一个主框架上更新新布局:
DispatchQueue.main.async{
self.collectionView!.setCollectionViewLayout(ListLayout(),动画:true)
}
这很有效
    let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout

    flowLayout?.invalidateLayout()

    DispatchQueue.main.async { [weak self] in
          self?.collectionView.layoutIfNeeded()
    }
override func viewWillTransition(
    to size: CGSize,
    with coordinator: UIViewControllerTransitionCoordinator)
{
    super.viewWillTransition(to: size, with: coordinator)
    let contentOffset = collectionView.contentOffset

    let visibleFrame = view.safeAreaLayoutGuide.layoutFrame

    collectionView.visibleCells.forEach { cell in

        guard let customCell = cell as? CustomCell else {
            return
        }

        let minY = customCell.frame.minY - contentOffset.y
        let maxY = customCell.frame.maxY - contentOffset.y

        if
            visibleFrame.contains(CGPoint(x: visibleFrame.minX, y: minY)) ||
            visibleFrame.contains(CGPoint(x: visibleFrame.minX, y: maxY))
        {
            customCell.shouldSnapshot = true
        }
    }

}
class CustomCell: UICollectionViewCell {

    var shouldSnapshot = false

    override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
        if shouldSnapshot {
            shouldSnapshot = false
            return super.snapshotView(afterScreenUpdates: afterUpdates)
        } else {
            return nil
        }
    }
}
[pCollectionView snapshotViewAfterScreenUpdates:YES];
pCollectionFlowLayot = [[UICollectionViewFlowLayout alloc] init];
[pCollectionFlowLayot setMinimumLineSpacing:10];
[pCollectionFlowLayot setMinimumLineSpacing:10];
[pCollectionFlowLayot setSectionInset:UIEdgeInsetsMake(10, 10, 10, 10)];

pCollectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:pCollectionFlowLayot];
[pCollectionView snapshotViewAfterScreenUpdates:YES];