In the WWDC 2014 talk " A Look Inside Presentation Controllers" the presenters showed how to setup a UISearchController in a UITableView. They do this by setting the searchController's searchBar's frame, then setting it as the tableView's tableHeaderView. Unfortunately, there isn't an equivalent of tableHeaderView for UICollectionView. With UISearchDisplayController, this would be simple: create a UISearchBar and add it to a custom UICollectionView section header, then initialize the UISearchDisplayController with the search bar. The problem is, you can't initialize a UISearchController with a UISearchBar, or even set the searchBar because it's a read-only property. I guess my real question is, what are my options? Is there a "good" way to implement search without UISearchDisplayController or UISearchController?
The answer accepted here seems outdated and not keeping several things in mind while answered. Those are as following:
UISearchController.searchbar
view as supplementary view, but you can't because there is a type mismatch.UISearchController.searchbar
in your dequeued supplementary header view as subview, but wait, you have to remove this view from other supplementary views, because this supplementary view may be re-used in other sections (it is pretty much common that you use multiple section in your collectionView)Recently i was working on to achieve such a user experience to search on a collectionView which has several section. Here is my solution.
In Storyboard, Use UIViewController
instead of using UICollectionViewController
. In the ViewController i add one UIView
& one UICollectionView
like following
---------ViewController---------- | | | UIView | | | |===============================| | | | | | | | | | | | UICollectionView | | | | | | | | | | | | | ---------------------------------
The top UIView
has a height constraint and pinned to top
, left
& right
. Bottom CollectionView
is pinned to bottom
, left
& right
and vertical distance between top view and bottom collection view is set to zero. The top view is subclassed as FillSubView
which is as follows
FillSubView.swift
import UIKit
class FillSubView: UIView {
override func didAddSubview(_ subview: UIView) {
subview.frame = self.bounds
}
override func layoutSubviews() {
super.layoutSubviews()
subviews.first?.frame = self.bounds
}
}
What it does, every time a subview added or the layout of the FillSubView
changes, the first subview is resized to that content. You may find why i don't use constraint to pin the subview. My intention is to add UISearchController.searchbar as a subview in FillSubView. While the UISearchController get active and deactivated this searchbar is removed and re-added to the previous parent view. In that case if you strictly wanted to pin the searchbar to FillSubView you could override
- (void)didAddSubview:(UIView *)subview;
pin the subview (searchbar) to the parent view (top,bottom,left,right). While removing from superview constraints will be auto removed.
Now say the view controller is subclassed as MyViewController
. I took an IBOutlet
of top UIView, CollectionView and the top view height constraint as following
@IBOutlet weak var searchBarContainer: UIView! // top view
@IBOutlet weak var collectionView: UICollectionView! // bottom collection view
@IBOutlet weak var searchContainerHeightConstraint: NSLayoutConstraint! // top view height constraint
And in the MyViewController
i added a function to setup the searchController as follows
private func setupSearchController() {
searchController = UISearchController.init(searchResultsController: nil)
searchController?.searchResultsUpdater = self
searchController?.dimsBackgroundDuringPresentation = false
searchController?.searchBar.placeholder = "Search"
if let searchbar = searchController?.searchBar {
searchBarContainer.addSubview(searchbar)
searchContainerHeightConstraint.constant = searchbar.bounds.height
}
definesPresentationContext = true
}
i call this method from my ViewDidLoad
overloaded method of ViewController. Here the whole point is the FillSubView
class, which filled the searchbar along the whole view.
Delegate implementation of searchResultUpdater
pretty common just filtering out the content which i wanted to show while the user typed in any text in searchbar.
This way i achieved my goal and it also conform to the 3 problem described earlier.
Hope it helps.
My first question: Why can't you flip your logic and instead of creating a search bar, create a UISearchController first, grab its search bar and add it to the section header?
Second, collection views layouts aren't as simple as table views. It's hard for search to guess at your intentions with collection view layouts whereas a table view is pretty straightforward. So in the case of a collection view, you are free to add a search bar to the view but animating it to an active state will require some work. (Subclass UISearchController and return your own animation controller, then do whatever animation you want. Or just implement the methods from UIViewControllerAnimatedTransitioning on your subclass and don't call super, both should work)
Something you might want to try though is having the search bar appear from off screen. This is a built-in animation UISearchController supports when setActive: is called and the search bar isn't anywhere in the view hierarchy. Calendar does this... it's pretty cool. Instead of having a giant search bar always present, you can reduce search to an icon that drives presentation.
Finally, there are bound to be bugs. Please file bugs when you can't get things to work that you think should. I know, it's a broken record, but it really is a necessity.
With UISearchDisplayController, this would be simple: create a UISearchBar and add it to a custom UICollectionView section header, then initialize the UISearchDisplayController with the search bar
The search bar in a UISearchController is created for you. When you are asked the supplementary view in the data source method
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
add the searchController.searchBar as a subview of the supplementary view. Don't forget to call
[searchController.searchBar sizeToFit]
in order to give the search bar the appropriate size.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With