Reload UICollectionView header or footer?
Reload UICollectionView header or footer?
I have some data that is fetched in another thread that updates a UICollectionView's header. However, I've not found an efficient way of reloading a supplementary view such as a header or footer.
I can call collectionView reloadSections:
, but this reloads the entire section which is unnecessary. collectionView reloadItemsAtIndexPaths:
only seems to target cells (not supplementary views). And calling setNeedsDisplay
on the header itself doesn't appear to work either. Am I missing something?
collectionView reloadSections:
collectionView reloadItemsAtIndexPaths:
setNeedsDisplay
8 Answers
8
You can also use
[[_collectionView collectionViewLayout] invalidateLayout]
perfect answer..!
– Pratik Jamariya
Jul 5 '16 at 13:56
That reloads the entire layout. Regardless of supplementary views or not.
– TheCodingArt
Jul 26 '16 at 18:12
This is a much more appropriate answer: stackoverflow.com/questions/26998761/…
– TheCodingArt
Jul 26 '16 at 18:13
I just ran into the same problem, and I ended up looking up the view using its tag to edit a label:
UICollectionReusableView *footer = (UICollectionReusableView*)[self.collectionView viewWithTag:999];
UILabel *footerLabel = (UILabel*)[footer viewWithTag:100];
Like you said it is unnecessary to reload an entire section, which cancels out any animation on cells as well. My solution isn't ideal, but it's easy enough.
I'd be afraid doing so would inhibit header reuse, or cause memory issues.
– akaru
Jan 14 '13 at 3:29
@akaru it works fine for me. Not sure why there'd be memory issues as it's just a tag.
– Ben Clayton
Nov 28 '13 at 15:37
Worst answer next to KVO
– TheCodingArt
Feb 16 '17 at 19:56
I got the same problem. I tried @BobVorks's answer and it is working fine, if only the cell was reused else it won't. So, I tried finding a more cleaner way to achieve this and I came up reloading the whole UICollectionView after the performBatchUpdate (completion block) and it is working great. It reloads the Collection Without any cancellation of animation in the insertItemsAtIndexPath. Actually I personally up voted recent 2 answers cause i find it working but in my case, this is the cleanest way to do it.
[self.collectionView performBatchUpdates:^{
// perform indexpaths insertion
} completion:^(BOOL finished) {
[self.collectionView reloadData];
}];
Here are two ways you could do it.
1.
Create a mutable model to back the data that will eventually be available. Use KVO in inherited class of UICollectionReusableView to observe the changes and update the header view with the new data as it comes available.
[model addObserver:headerView
forKeyPath:@"path_To_Header_Data_I_care_about"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
then implement listener method in header view
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
2.
add notification listener to the view and post a notification when the data has successfully come available. Downside is that this is application wide and not a clean design.
// 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];
I've not used KVO, but I have listened for updates to the data, set the fields on my custom header class, and called setNeedsDisplay on it, but nothing changed. I'm having trouble more with getting the header to refresh itself than I am with architecting how to update it.
– akaru
Dec 12 '12 at 23:46
Have you tried calling invalidateLayout. Does that refresh your header?
– Samuel
Dec 14 '12 at 22:44
that didn't work either.
– akaru
Dec 17 '12 at 16:53
at the simplest level what are you wanting to display when the data comes back?
– Samuel
Dec 17 '12 at 18:50
I just have some UILabels that need to be updated. I had to rewrite it to use cells instead of the header--headers just refused to update for some reason, unless I reloaded the entire section.
– akaru
Dec 20 '12 at 19:26
Here's what I did to update only the section headers that are currently loaded in memory:
NSMapTable
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
This question is very old but a simple way to do it is to just set a delay that covers the time your view is animating and disabling the animation while you update the view...usually a delete or insert takes about .35 seconds so just do:
delay(0.35){
UIView.performWithoutAnimation{
self.collectionView.reloadSections(NSIndexSet(index: 1))
}
[UIView performWithoutAnimation:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:4]];
}];
[UIView performWithoutAnimation:^{
[self.collectionView reloadData];
}];
Swift 3/4 version:
collectionView.collectionViewLayout.invalidateLayout()
Caution!
If you change the number of collectionView
items at the same time (for example you show the footer only if all cells were loaded), it will crash. You need to reload the data first, to make sure that the number of items is the same before and after invalidateLayout()
:
collectionView
invalidateLayout()
collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
this should really be the the accepted answer
– tropicalfish
Feb 1 '16 at 5:39