I'm looking to create similar functionality to Apple's maps application in Swift. Is there anyway to integrate a UISearchController in to a regular view (i.e.: not a UITableView). Dropping one in through Storyboard results in a crash after clicking inside the connected searchbar. Or is there some way I can achieve this outcome with a UITableView?
I added a Search Bar and Search Display Controller in my View Controller in the storyboard. The view controller contains only the search bar and search display controller and does not have it's own TableView. When you add the search bar in your view controller, it sets your view controller as it's delegate automatically.
Now the Search Bar and Search Display Controller has a table view of itself which it uses to display the search results when you click inside the box and start typing. This table view expects your view controller to provide the implementations of the numberOfRowsInSection and cellForRowAtIndexPath functions for it to display the data properly.
When you run your project without these and tap inside the search bar, you will get the following error:-
tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x7fbf63449660 *** Terminating app due to uncaught exception 'NSInvalidArgumentException'
If you see, the error is at the numberOfRowsInSection method.
Change your view controller definition from
class ViewController: UIViewController
to
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
and implement the required methods which are:-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
I have just added default return values in the above methods.
Now if you filter out your data source in your searchviewdelegate methods and set up your number of rows and cell info in the above two methods properly, it should work.
Hope this helps!
UISearchDisplayController
? You keep saying "search display controller", but that class is deprecated in iOS 8. –
Rew UISearchDisplayController
(deprecated in iOS 8). Though implementing the methods in the answer above are still necessary for a results table's delegate, they aren't the full picture with the new UISearchController
in iOS 8. See adauguet's answer for the rest. –
Willumsen If you want to use UISearchController with a non UITableView, here is how I did it.
Since the UISearchController
is not (yet!) supported by IB, you do not need to add anything in it, like a UISearchBar
.
@interface UIViewControllerSubclass () <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate, UISearchControllerDelegate, UISearchResultsUpdating>
@property (strong, nonatomic) UISearchController *searchController;
@end
@implementation UIViewControllerSubclass
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any custom init from here...
// Create a UITableViewController to present search results since the actual view controller is not a subclass of UITableViewController in this case
UITableViewController *searchResultsController = [[UITableViewController alloc] init];
// Init UISearchController with the search results controller
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsController];
// Link the search controller
self.searchController.searchResultsUpdater = self;
// This is obviously needed because the search bar will be contained in the navigation bar
self.searchController.hidesNavigationBarDuringPresentation = NO;
// Required (?) to set place a search bar in a navigation bar
self.searchController.searchBar.searchBarStyle = UISearchBarStyleMinimal;
// This is where you set the search bar in the navigation bar, instead of using table view's header ...
self.navigationItem.titleView = self.searchController.searchBar;
// To ensure search results controller is presented in the current view controller
self.definesPresentationContext = YES;
// Setting delegates and other stuff
searchResultsController.tableView.dataSource = self;
searchResultsController.tableView.delegate = self;
self.searchController.delegate = self;
self.searchController.dimsBackgroundDuringPresentation = NO;
self.searchController.searchBar.delegate = self;
}
@end
I hope it is enough to work :-)
Then of course you need at least to implement UITableViewDelegate, UITableViewDataSource, UISearchResultsUpdater methods.
Enjoy!
self.definesPresentationContext = true
(Swift). That fixed it. –
Interlay Trying to figure out UISearchController
myself. Setting it to the titleView
is convenient, but on one of my pages, I had to put the searchBar
near the top of the UIViewController
:
// Add a normal View into the Storyboard.
// Set constraints:
// - height: 44
// - leading and trailing so that it spans the width of the page
// - vertical position can be anywhere based on your requirements, like near the top
@IBOutlet weak var searchContainerView: UIView!
var searchResultsController = UISearchController()
override func viewDidLoad() {
// TODO: set the searchResultsController to something
let controller = UISearchController(searchResultsController: nil)
// have the search bar span the width of the screen
controller.searchBar.sizeToFit()
// add search bar to empty View
searchContainerView.addSubview(controller.searchBar)
searchResultsController = controller
}
UPDATE:
After implementing UISearchController
in a project or two, I found myself gravitating toward @adauguet's approach of embedding the search bar into the Navigation Bar.
Here's the code in Swift. One difference though is that it doesn't set the searchBar delegate, since searchResultsUpdater
already listens for text changes.
override func viewDidLoad() {
super.viewDidLoad()
// locationManager.delegate = self
// locationManager.desiredAccuracy = kCLLocationAccuracyBest
// locationManager.requestWhenInUseAuthorization()
// locationManager.requestLocation()
let locationSearchTable = storyboard!.instantiateViewControllerWithIdentifier("LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.titleView = resultSearchController?.searchBar
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
}
Also, I wrote a blog post that creates a project from scratch that uses UISearchController
to display map search results. It also does other things that you might want in a map project, like get the user location, drop pins, parse placemarks into a one-line address, and create callout buttons that take you to Apple Maps for driving directions.
http://www.thorntech.com/2016/01/how-to-search-for-location-using-apples-mapkit/
The blog post is quite long, so here's the associated git repo if you just want to skip to the code:
I added a Search Bar and Search Display Controller in my View Controller in the storyboard. The view controller contains only the search bar and search display controller and does not have it's own TableView. When you add the search bar in your view controller, it sets your view controller as it's delegate automatically.
Now the Search Bar and Search Display Controller has a table view of itself which it uses to display the search results when you click inside the box and start typing. This table view expects your view controller to provide the implementations of the numberOfRowsInSection and cellForRowAtIndexPath functions for it to display the data properly.
When you run your project without these and tap inside the search bar, you will get the following error:-
tableView:numberOfRowsInSection:]: unrecognized selector sent to instance 0x7fbf63449660 *** Terminating app due to uncaught exception 'NSInvalidArgumentException'
If you see, the error is at the numberOfRowsInSection method.
Change your view controller definition from
class ViewController: UIViewController
to
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource
and implement the required methods which are:-
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
return UITableViewCell()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
I have just added default return values in the above methods.
Now if you filter out your data source in your searchviewdelegate methods and set up your number of rows and cell info in the above two methods properly, it should work.
Hope this helps!
UISearchDisplayController
? You keep saying "search display controller", but that class is deprecated in iOS 8. –
Rew UISearchDisplayController
(deprecated in iOS 8). Though implementing the methods in the answer above are still necessary for a results table's delegate, they aren't the full picture with the new UISearchController
in iOS 8. See adauguet's answer for the rest. –
Willumsen I had some trouble converting from SearchDisplayController in UIViewController to SearchController in ViewController because SearchController implementation isn't so intuitive. But you can just add in searcher from SearchController itself into any view. You cannot set constraint though because the search bar would move up when you focus/select it (if you know how to set the constraint to seachController.searchbar after adding it to any view, LET ME KNOW!). Below I am sharing a checklist that I found very important/valuable when implementing SearchController in ViewController.
//you can just add searcher to any view. It would automatically move up to show the tableView like magic. But for this reason, you cannot set constraint to the search bar to the placeholder view that you are adding it to.
[self.searchBarPlaceHolderView addSubview:self.searchController.searchBar];
//you need this to prevent search bar to drop down when you focus/select it. You would want to set this to NO if you are adding searchBar to the navigation bar's titleview.
self.searchController.hidesNavigationBarDuringPresentation = YES;
//make sure you set this in your viewController
self.extendedLayoutIncludesOpaqueBars = true;
self.definesPresentationContext = YES;
// you also need to give the search controller a tableViewController that can be displayed. You can also do just self.searchResultsController = [[UITableView alloc] init] for a generic one.
self.searchResultsController = (UITableViewController *)[ [UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"searchResultsTableViewController"];
self.searchResultsController.tableView.dataSource = self;
self.searchResultsController.tableView.delegate = self;
self.searchResultsController.definesPresentationContext = NO;
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.searchResultsController];
© 2022 - 2024 — McMap. All rights reserved.