Integrating A UISearchController Is Easy With UITableView But Not With UICollectionView
The Problem
Apple’s iOS class UITableView works very well with a UISearchController and its UISearchBar, but almost all developers throw up their hands when trying to visually integrate a search bar with their UICollectionView objects.
I found this very issue when developing an app that was responsible for showing thumbnails of photos from a large organization’s REST services, and I wanted to have the same layout that I get with a UITableView. Based on StackOverflow and other postings, most people either don’t bother with trying to visually integrate the classes, or they overload a section header.
The Solution
Since I wanted my search bar to show and hide on demand in an animated manner, as well as stay correctly sized with the associated collection view in the context of a UICollectionViewController, this required quite a bit more work than I was expecting to support iOS 9 and 10, but I did work out a solution for my needs.
To implement this, I wrote a subclass of UISearchController called DSSSearchController that adds a number of methods for showing and hiding the search bar. I designed it to work with any view, but I’ve only tested it with my particular collection view. Below you’ll find the Objective-C header and source as well as a version in Swift.
DSSSearchController.h
DSSSearchController.m
DSSSearchController.swift
As you can see, there’s lots of code needed to make sure the search bar is correctly sized in relation to the collection view. In iOS 9 and 10, it appears Apple doesn’t use AutoLayout with collection views managed by a UICollectionViewController, so that’s why there’s lots of calculations using frames—trying to use AutoLayout proved too problematic. Early on, I found some of the animation would produce some ghosting or overlaying of the search bar that I didn’t like, so that’s why certain operations are ordered the way they are.
To use my class, I wrote a method named configureSearchController that I call during viewDidLoad to initialize the class and set some options on the search bar. In my original code, after getting the searchBar property, I changed its barTintColor to a deep sea blue, its tintColor to white, and its search field’s tintColor to deep sea blue. Also, my navigation bar has UIBarStyleBlack for its barStyle property.
configureSearchController
When the user taps on the magnifying glass icon in the navigation bar, the method toggleSearchBar: is called.
toggleSearchBar:
I found that when I was about to present a view controller, I needed to know if my search controller was the one being presented, which happens when the the search field has focus.
Before calling presentViewController:animated:completion:
I’m sure my solution can be improved upon, and it may break with later releases of iOS, but for now, it works. Hopefully the successor to iOS 10 will have a solution built in.