Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UISearchController in a UIViewController

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?

like image 442
UncleAlfonzo Avatar asked Oct 17 '14 03:10

UncleAlfonzo


People also ask

What is a UIViewController?

A UIViewController is an object which manages the view hierarchy of the UIKit application. The UIViewController defines the shared behavior and properties for all types of ViewController that are used in the iOS application. The UIViewController class inherits the UIResponder class.

What is obscuresBackgroundDuringPresentation?

obscuresBackgroundDuringPresentation. A Boolean indicating whether to obscure the underlying content during a search.

How do I customize the search bar in Swift?

Change Search Bar Default Image Color The left hand default search image in UISearchBar represents the left view of the UITextField. The Image is rendered to change it to the desired colour. @IBOutlet weak var searchBar: UISearchBar! Hope it will help you in customising the UISearchBar in your app.


3 Answers

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!

like image 159
adauguet Avatar answered Oct 21 '22 21:10

adauguet


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:

https://github.com/ThornTechPublic/MapKitTutorial

like image 31
Robert Chen Avatar answered Oct 21 '22 21:10

Robert Chen


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!

like image 28
Rajeev Bhatia Avatar answered Oct 21 '22 23:10

Rajeev Bhatia