我有一些数据是在另一个更新UICollectionView标头的线程中获取的。但是,我没有找到一种重新加载补充视图(如页眉或页脚)的有效方法。
我可以调用collectionView reloadSections:
,但这会重新加载整个部分,这是不必要的。 collectionView reloadItemsAtIndexPaths:
似乎只针对细胞(不是补充观点)。并且在标题上调用setNeedsDisplay
似乎也不起作用。我错过了什么吗?
答案 0 :(得分:24)
你也可以使用(懒惰的方式)
collectionView.collectionViewLayout.invalidateLayout() // swift
[[_collectionView collectionViewLayout] invalidateLayout] // objc
更复杂的是提供上下文
collectionView.collectionViewLayout.invalidateLayout(with: context) // swift
[[_collectionView collectionViewLayout] invalidateLayoutWithContext:context] // objc
然后,您可以自行创建或配置上下文,以告知应更新的内容,请参阅:UICollectionViewLayoutInvalidationContext
它有一个功能可以覆盖:
invalidateSupplementaryElements(ofKind:at:) // swift
答案 1 :(得分:10)
我遇到了同样的问题,最后我用标签查找视图来编辑标签:
UICollectionReusableView *footer = (UICollectionReusableView*)[self.collectionView viewWithTag:999];
UILabel *footerLabel = (UILabel*)[footer viewWithTag:100];
就像你说的那样,没有必要重新加载整个部分,这也取消了细胞上的任何动画。我的解决方案并不理想,但它很容易。
答案 2 :(得分:3)
我遇到了同样的问题。我尝试了@ BobVorks的答案,它工作正常,如果只重复使用单元格,否则它不会。所以,我尝试找到一种更清晰的方法来实现这一点,然后我在performBatchUpdate(完成块)之后重新加载整个UICollectionView,它运行良好。它会重新加载Collection而不会在insertItemsAtIndexPath中取消任何动画。事实上,我个人最近投了两个答案,因为我觉得它有效但在我的情况下,这是最干净的方法。
[self.collectionView performBatchUpdates:^{
// perform indexpaths insertion
} completion:^(BOOL finished) {
[self.collectionView reloadData];
}];
答案 3 :(得分:3)
Swift 3/4 版本:
collectionView.collectionViewLayout.invalidateLayout()
警告!
如果您同时更改collectionView
个项目的数量(例如,仅在加载所有单元格时才显示页脚),它将崩溃。您需要先重新加载数据,以确保invalidateLayout()
前后的项目数相同:
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
答案 4 :(得分:2)
这有两种方法可以做到。
1。 创建一个可变模型来支持最终可用的数据。在继承的UICollectionReusableView类中使用KVO来观察更改并使用新数据更新标题视图。
[model addObserver:headerView
forKeyPath:@"path_To_Header_Data_I_care_about"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
然后在标题视图中实现监听器方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
2。 向视图添加通知侦听器,并在数据成功可用时发布通知。缺点是这是应用范围广泛而不是简洁的设计。
// place in shared header file
#define HEADER_DATA_AVAILABLE @"Header Data Available Notification Name"
// object can contain userData property which could hole data needed.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(headerDataAvailable:) name:HEADER_DATA_AVAILABLE object:nil];
[[NSNotificationCenter defaultCenter] postNotificationName:HEADER_DATA_AVAILABLE object:nil];
答案 5 :(得分:2)
[UIView performWithoutAnimation:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:4]];
}];
[UIView performWithoutAnimation:^{
[self.collectionView reloadData];
}];
答案 6 :(得分:1)
这就是我所做的只更新当前在内存中加载的节标题:
NSMapTable
。创建标题时,使用indexPath对象将标题添加为弱持有键。如果我们重用标头,我们将更新indexPath。NSMapTable
枚举对象/键。
@interface YourCVController ()
@property (nonatomic, strong) NSMapTable *sectionHeaders;
@end
@implementation YourCVContoller
- (void)viewDidLoad {
[super viewDidLoad];
// This will weakly hold on to the KEYS and strongly hold on to the OBJECTS
// keys == HeaderView, object == indexPath
self.sectionHeaders = [NSMapTable weakToStrongObjectsMapTable];
}
// Creating a Header. Shove it into our map so we can update on the fly
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
PresentationSectionHeader *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"presentationHeader" forIndexPath:indexPath];
// Shove data into header here
...
// Use header as our weak key. If it goes away we don't care about it
// Set indexPath as object so we can easily find our indexPath if we need it
[self.sectionHeaders setObject:indexPath forKey:header];
return header;
}
// Update Received, need to update our headers
- (void) updateHeaders {
NSEnumerator *enumerator = self.sectionHeaders.keyEnumerator;
PresentationSectionHeader *header = nil;
while ((header = enumerator.nextObject)) {
// Update the header as needed here
NSIndexPath *indexPath = [self.sectionHeaders objectForKey:header];
}
}
@end
答案 7 :(得分:0)
这个问题已经很老了但是一个简单的方法就是设置一个延迟来覆盖视图动画的时间并在更新视图时禁用动画...通常删除或插入大约需要.35这么做就是这样:
delay(0.35){
UIView.performWithoutAnimation{
self.collectionView.reloadSections(NSIndexSet(index: 1))
}
答案 8 :(得分:0)
当布局无效时补充视图的帧大小发生变化时,我的问题就出现了。 似乎补充视图没有刷新。事实证明它们是,但是我是以编程方式构建UICollectionReusableView
对象的,并且我没有删除旧的UILabel
子视图。因此,当集合视图使每个标题视图出队时,UILabel会堆积起来,从而导致外观不稳定。
解决方案是完全在viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
方法内构建每个UICollectionReusableView,首先是a)从出队单元中删除所有子视图,然后b)从项目的布局属性中获取框架大小以允许添加新的子视图
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
yourClass *header = (yourClass *)[collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"identifier" forIndexPath:indexPath];
[[header viewWithTag:1] removeFromSuperview]; // remove additional subviews as required
UICollectionViewLayoutAttributes *attributes = [collectionView layoutAttributesForSupplementaryElementOfKind:kind atIndexPath:indexPath];
CGRect frame = attributes.frame;
UILabel *label = [[UILabel alloc] initWithFrame: // CGRectMake based on header frame
label.tag = 1;
[header addSubview:label];
// configure label
return header;
}
答案 9 :(得分:0)
let headerView = collectionView.visibleSupplementaryViews(ofKind: UICollectionView.elementKindSectionHeader)[0] as! UICollectionReusableView
我已经使用上述方法获取当前标题,并成功更新了其子视图。
答案 10 :(得分:0)
我有一个完美的解决方案:
let footerView = self.collectionView.visibleSupplementaryViews(ofKind: UICollectionView.elementKindSectionFooter)
现在您可以访问footerView的所有子视图:
footerView[0].subviews[0]
如果您的页脚视图中有标签,则:
let label: UILabel = footerView[0].subviews[0] as? UILabel ?? UILabel()
最后一步:
label.text = "Successfully Updated Footer."
答案 11 :(得分:0)
if let footerView = collectionView.subviews.first(where: {$0 is LoadingFooterCell}) as?加载页脚单元 { footerView.isLoading = .loading }