我有一个UICollectionViewLayout
子类,它指定了单元格周围和部分开头和结尾的补充视图。出于这个问题的目的,我创建了一个sample project,其中包含了我的布局子类的精炼版本。我还创建了a video,它直观地展示了我将要描述的问题。
示例项目如下所示([0, 1]
= section 0
item 1
):
[0, 0]
类型HEYCollectionViewElementKindHeaderA
[0, 0]
类型HEYCollectionViewElementKindCellA
[0, 0]
此索引路径的单元格。[0, 1]
另一种CellA品种[0, 1]
此索引路径的单元格。在示例项目中,我在HEYCollectionViewLayout
类中注释了一些其他标题。如果取消注释它们,您可以看到更多未正确重用的视图。
所有标题视图中都有一个UITextField
,但我能够使用UITextView
和我自己的UIView
同时重现此问题inputView
设置并成为第一响应者。当键盘在CellA [0, 0]
元素中的文本字段处于活动状态时,您将其上方的补充视图滚动到屏幕外,然后重新打开,视图不会重新添加到集合视图中。
在我的调查中,我发现UICollectionReusableView
子类被放入重用队列中,并在视图层次结构中显示为hidden=YES
,正如等待重用时所预期的那样。当键盘处于非活动状态时,我重新打开元素,之前显示的同一个类实例将被重用并显示在屏幕上。
我已经尝试让UICollectionViewLayout
要求在每个边界更改时重新布局(通过从YES
返回-shouldInvalidateLayoutForBoundsChange:
);这没有任何效果,元素仍然隐藏起来。
我一直无法在互联网上找到有关此问题的任何其他报告,但我已在Xcode 5.1.1中的iOS模拟器7.1和运行iOS 7.1.2的iPod Touch上复制了它。
我已经使用可能的解决方法更新了上面的回购:
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (BOOL)resignFirstResponder {
BOOL didResign = [super resignFirstResponder];
if (didResign) {
// This works around http://stackoverflow.com/q/25189751/551420
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView performBatchUpdates:^{
} completion:^(BOOL finished) {
}];
});
}
return didResign;
}
我认为问题是UICollectionView
中某种破坏的内部状态,只要UICollectionViewController
辞职第一响应者(因为孩子成为第一响应者)就会强制更新。
这似乎不是正确的解决方案,所以我没有将其标记为答案,但是如果其他人遇到问题,这就是我目前正在努力解决的问题。
答案 0 :(得分:0)
在我看来,这似乎是某种处理第一响应者的内部状态。
我发现的一个解决方法是通过添加额外的索引使每个补充视图的索引路径唯一:
- (void)prepareLayout {
[super prepareLayout];
[self.supplementaryViewAttributes removeAllObjects];
[self.cellAttributes removeAllObjects];
CGFloat minimumY = 0;
for (NSInteger sectionIdx = 0; sectionIdx < self.collectionView.numberOfSections; sectionIdx++) {
NSIndexPath *sectionIndexPath = [NSIndexPath indexPathForItem:0 inSection:sectionIdx];
NSInteger suppViewIdx = 0;
NSIndexPath *suppIndexPath = [sectionIndexPath indexPathByAddingIndex:suppViewIdx];
suppViewIdx++;
[self newAttributesForSupplementaryViewAtIndexPath:suppIndexPath
kind:HEYCollectionViewElementKindHeaderA
height:HEYCollectionViewHeaderHeight
minimumY:&minimumY];
suppIndexPath = [sectionIndexPath indexPathByAddingIndex:suppViewIdx];
suppViewIdx++;
[self newAttributesForSupplementaryViewAtIndexPath:suppIndexPath
kind:HEYCollectionViewElementKindHeaderB
height:HEYCollectionViewHeaderHeight
minimumY:&minimumY];
for (NSInteger itemIdx = 0; itemIdx < [self.collectionView numberOfItemsInSection:sectionIdx]; itemIdx++) {
NSIndexPath *itemIndexPath = [NSIndexPath indexPathForItem:itemIdx inSection:sectionIdx];
suppIndexPath = [itemIndexPath indexPathByAddingIndex:suppViewIdx];
suppViewIdx++;
[self newAttributesForSupplementaryViewAtIndexPath:suppIndexPath
kind:HEYCollectionViewElementKindCellA
height:HEYCollectionViewCellHeight
minimumY:&minimumY];
suppIndexPath = [itemIndexPath indexPathByAddingIndex:suppViewIdx];
suppViewIdx++;
[self newAttributesForSupplementaryViewAtIndexPath:suppIndexPath
kind:HEYCollectionViewElementKindCellB
height:HEYCollectionViewCellHeight
minimumY:&minimumY];
[self newAttributesForCellAtIndexPath:itemIndexPath minimumY:&minimumY];
}
}
}
如果这是一个内部错误,那么对我来说这似乎是一个不错的解决方法,因为它不会改变您的索引路径“section
或item
值,这也意味着您没有继承UICollectionView
以强制它在第一个响应者辞职时更新。
但是,如果您当前compare:
正在或测试索引路径上的相等性(例如,使用isEqual:
),那么此解决方法可能会破坏它。您必须确保仅比较section
和item
以保持相同的功能。