Reload UICollectionView header or footer?

前端 未结 10 1104
余生分开走
余生分开走 2020-12-08 02:35

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 suc

相关标签:
10条回答
  • 2020-12-08 03:09
    let headerView = collectionView.visibleSupplementaryViews(ofKind: UICollectionView.elementKindSectionHeader)[0] as! UICollectionReusableView
    

    I've used above method to get current header, and successfully updated subviews on it.

    0 讨论(0)
  • 2020-12-08 03:10

    You can also use (the lazy way)

    collectionView.collectionViewLayout.invalidateLayout() // swift
    
    [[_collectionView collectionViewLayout] invalidateLayout] // objc
    

    More complex would be to provide a context

    collectionView.collectionViewLayout.invalidateLayout(with: context) // swift
    
    [[_collectionView collectionViewLayout] invalidateLayoutWithContext:context] // objc
    

    You can then make a or configure the context yourself to inform about what should be updated see: UICollectionViewLayoutInvalidationContext

    It has a function in there that you can override:

    invalidateSupplementaryElements(ofKind:at:) // swift
    

    Another option is (if you have already loaded the correct header/footer/supplementary view) and you only want to update the view with the new data than you can use one of the following functions to retrieve it:

    supplementaryView(forElementKind:at:) // get specific one visibleSupplementaryViews(ofKind:) // all visible ones

    Same goes for visible cells with visibleCells. The advantage of just getting the view and not reloading a view entirely is that the cells retains it state. This is espically nice with table view cells when they use swipe to delete/edit/etc since that state is lost after reloading the cell.

    If you feel fanatic you can of course also write some extensions to retrieve only cells/supplementary views of a given kind using generics

    if let view = supplementaryView(forType: MySupplementaryView.self, at: indexPath) {
        configure(view, at indexPath)
    }
    

    this assumes that you have a function that registers/dequeues views in example with their class name. I made a post about this here

    0 讨论(0)
  • 2020-12-08 03:11

    Swift 3/4/5 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.reloadData()
    collectionView.collectionViewLayout.invalidateLayout()
    
    0 讨论(0)
  • 2020-12-08 03:12

    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))  
                }
    
    0 讨论(0)
  • 2020-12-08 03:13

    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];
    }];
    
    0 讨论(0)
  • 2020-12-08 03:14

    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];
    
    0 讨论(0)
提交回复
热议问题