21

I have a UICollectionView that have section header. In the section header I have a UISearchBar. I want to filter the content in my collection view when I type in the search bar. I do this with the following method:

// The method to change the predicate of the FRC
- (void)filterContentForSearchText:(NSString*)searchText
{
    NSString *query = searchText;
    if (query && query.length) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[cd] %@ or createdAt contains[cd] %@ or noteText contains[cd] %@ or keywords contains[cd] %@", query, query, query, query];
        [self.fetchedResultsController.fetchRequest setPredicate:predicate];
    } else {
        [self.fetchedResultsController.fetchRequest setPredicate:nil];
    }
    NSError *error = nil;
    if (![[self fetchedResultsController] performFetch:&error]) {
        // Handle error
        NSLog(@"Error");
    }
    [self.collectionView reloadData];
}

This method gets called every time the search bar text changes. The line [self.collectionView reloadData] hided the keyboard for every character. Is it possible to reload only the data in a UICollection view and not reload the Supplementary views like section headers headers?

The data in my collectionView comes from a NSFetchResultController.

I'm pretty happy with how my UI works so if there is a simple way to NOT reload the section header, then that would be great!

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
Anders
  • 2,903
  • 7
  • 58
  • 114

2 Answers2

35

I finally figured this out after trying many different options. The solution is to make your header of its own section so you can reload the other sections independently without reloading the one your header is attached to.

So:

  • Section 0
    • Header
      • Search bar (or other text fields)
    • Rows
      • (None)
  • Section 1
    • Header
      • Create an empty UICollectionReusableView and override ...sizeForItemAtIndexPath:(NSIndexPath *)indexPath to return CGSizeZero
    • Rows
      • Your actual rows that would have originally gone with section 0

Then, we you need to reload your data:

[collectionView reloadSections:[NSIndexSet indexSetWithIndex:1]];

Your searchbar or text fields should retain their focus while the user is typing and you can update the results in the background.

Jason
  • 589
  • 6
  • 6
  • This is the best way to implement a search bar or text input field inside your colletionView to filter your data source. Thanks – user3378170 May 20 '16 at 16:42
  • Now, this is most intelligent solution. Thank you sir. – GeneCode Sep 29 '16 at 03:07
  • Brilliant. Thank you! – Patrick Lynch Dec 13 '16 at 16:22
  • Do you have a project that can demonstrate? I am at my eyeballs trying to get this to work. Especially since I am working with custom layout attributes for all my items and headers too. Each row of items are created in their own sections. So each section has 1 row. As a test, I delete 1 section for every character I type and there's no reload. So I figured I'd delete every section, and then insert the new sections instead but that causes a reload ! Perhaps you can take a look at the Filtering branch at http://github.com/pavankataria/SwiftDataTables ? I'd greatly appreciate it. – Pavan Mar 16 '17 at 08:43
  • 3
    Unfortunately this way it'll be impossible to keep the search bar at the top via `sectionHeadersPinToVisibleBounds` – Tamás Zahola Apr 11 '17 at 22:07
  • Smart! Just a small issue: A kind of blinking of cells is seen now. But not that important – Ashkan Sarlak Jan 02 '18 at 08:58
  • This does not seem to be working with swift 4 xcode 9. Reload data still hides keyboard – tapizquent Feb 07 '19 at 21:36
  • That's overkill but sadly the only way to do it, thanks! – Skoua Jun 19 '19 at 15:02
  • Fantastic answer! Works for Swift 5 in Aug 2020 – Lance Samaria Aug 25 '20 at 22:46
  • It works, but it's slow to compare to reloadData. – Timur Suleimanov Jan 15 '21 at 17:00
  • Is there an example about how to make the search bar a separate section? Thank you! – Jack Stark Jan 21 '23 at 09:30
  • To answer @TamásZahola's concern: To keep the search bar pinned to the top, instead of using a header from a different section, I used a "dummy" header, then pinned the real header to the dummy header's sides using constraints, then added the real header directly as a subview of the collection view. With this "hack" the real header wont resign first responder status – Peter Jul 21 '23 at 03:26
5

did you tried with one of this options?

To reload just the new items

    [self.collectionView reloadItemsAtIndexPaths:indexPaths];

To reload the complete section

    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];

Or, if it doesn't work.. make searchBar first responder again after your updates

    [self.collectionViewPackages performBatchUpdates:^{
        [self.collectionView reloadData];
    } completion:^(BOOL finished) {
        [self.searchBar becomeFirstResponder];
    }];
Camo
  • 1,778
  • 14
  • 12