I am trying to add search capabilities to the map based project I am working on in swift on XCode 6. I have added SearchDisplayController to my main view controller, which contains the map. I created a class called MapSearchDisplayController, which implements necessary methods. I also changed Custom Class property of the SearchDisplayController to MapSearchDisplayController in the storyboard.
In the storyboard, I dragged SearchDisplayController to my main view controller to create an IBOutlet. My main viewDidLoad for the main view controller looks like this:
class ViewController: UIViewController, CCHMapClusterControllerDelegate, MKMapViewDelegate, ADBannerViewDelegate {
@IBOutlet weak var mapSearchDisplayController1: MapSearchDisplayController!
@IBOutlet var mapSearchDisplayController: MapSearchDisplayController!
@IBOutlet weak var banner: ADBannerView!
@IBOutlet weak var mMap: MKMapView!
var clusterController:CCHMapClusterController?
let mMaxZoomLevelForClustering : Double = 13
let mMinUniqueLocationsForClustering : UInt = 1
var formatter = NSNumberFormatter()
var databasePath = NSString()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
banner.delegate = self
// Add button to navbar
var filterButton : UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Organize, target: self, action: nil)
self.navigationItem.leftBarButtonItem = filterButton
var aboutButton : UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Add, target: self, action: "aboutAction")
var searchButton : UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Search, target: self, action: "searchAction")
var rButtons : [UIBarButtonItem] = [aboutButton, searchButton]
self.navigationItem.rightBarButtonItems = rButtons
// Deal with Search Display Controller
self.mapSearchDisplayController.delegate = self.mapSearchDisplayController;
self.mapSearchDisplayController.searchResultsDataSource = self.mapSearchDisplayController;
self.mapSearchDisplayController.searchResultsDelegate = self.mapSearchDisplayController;
self.mapSearchDisplayController.searchBar.placeholder = "Search Destination";
self.mapSearchDisplayController.searchResultsTableView.delegate = self.mapSearchDisplayController
self.mapSearchDisplayController.searchResultsTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
Issue 1: I had to add an additional mapSearchDisplayController1 - otherwise my mapSearchDisplayController was nil and I had an exception when I tried to to use it. Now when I have an additional variable mapSearchDisplayController1 (it is declared, but never used) it is not throwing exceptions and some functionality is working. Tried to add/remove weak, bt it did not make any difference. I can't figure out what have I missed that leads to this behavior.
Isse 2: Even bigger problem that I have, is that the instance variables of mapSearchDisplayController, which handles the search related functionality, are nil, its init method is not being invoked, but the functionality in the delegate methods work. So, data1 variable is nil, despite being initialized to hardcoded string array. Same goes for all other members, including googleAPIKey which is a constant. If shouldReloadTableForSearchString set data1 again data1 = ["1111", "222", "ZAAAA"]
then it remains initialized, but if I assign data1 the value I get as a searchresult - it is lost. I would understand if the entire object was nil, but the methods are being invoked and working, it is just an instance variables and init which are not "working". Code below:
class MapSearchDisplayController: UISearchDisplayController, UISearchDisplayDelegate, UITableViewDataSource, UITableViewDelegate, LPGoogleFunctionsDelegate
{
var data1 : [String]! = ["1111", "222", "ZAAAA"]
var googleFunctions : LPGoogleFunctions? = LPGoogleFunctions()
let googleAPIKey = "myKey"
//MARK: UITableViewDelegate
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
println("In numberOfRowsInSection - \(data1.count)")
return self.data1.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
println("In cellForRowAtIndexPath")
var cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell
if !(cell != nil) {
println("new cellForRow")
cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
// Get the corresponding candy from our candies array
let candy = self.data1[indexPath.row]
// Configure the cell
cell!.textLabel.text = candy
cell!.detailTextLabel?.text = "Cell Details"
return cell!
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
// Dismiss search display controller
self.active = false;
// Force selected annotation to be on map
}
func tableView(tableView: UITableView, numberOfSectionsInTableView indexPath: NSIndexPath) -> Int{
println("numberOfSectionsInTableView")
return 1
}
//MARK: Search methods
func filterContentForSearchText (searchText: String) {
println(searchText)
}
func searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -> Bool {
var length : Int32 = Int32(countElements(searchString))
if (countElements(searchString) < 3 ) {
println("Short searchString. Not enough info")
return false
}
data1 = []
if (googleFunctions == nil)
{
googleFunctions = LPGoogleFunctions()
googleFunctions!.sensor = false
googleFunctions!.delegate = self
googleFunctions!.googleAPIBrowserKey = "myKey"
}
println(googleFunctions?.googleAPIBrowserKey)
googleFunctions?.loadPlacesAutocompleteWithDetailsForInput(searchString, offset: length, radius: 0, location: nil, placeType: LPGooglePlaceTypeGeocode, countryRestriction: nil, successfulBlock: {(pd : [AnyObject]!) in
println("--------------GOOGLE search success")
for place in pd{
var pl = place as LPPlaceDetails
self.data1.append(pl.name)
println(pl.name)
}
}
, failureBlock: {(status : LPGoogleStatus) in
println("---- GOOGLE failed")
})
//data1 = ["1111", "222", "ZAAAA"]
return true
}
func searchDisplayControllerWillBeginSearch(controller: UISearchDisplayController!){
controller.searchBar.showsCancelButton = true
}
func searchDisplayControllerWillEndSearch(controller: UISearchDisplayController!){
println("searchDisplayControllerWillEndSearch")
}
//MARK: LPGogleFunctions methods
override init() {
println("Initializing GoogleFunctions")
if (googleFunctions == nil){
googleFunctions = LPGoogleFunctions()
}
data1 = []
super.init()
googleFunctions!.sensor = false
googleFunctions!.delegate = self
googleFunctions!.googleAPIBrowserKey = "myKey"
}
}
Any help would be greatly appreciated.
I am answering my own question, in case someone ever has the same problem.
Issue #1 I resolved by deleteing both IBOutlets and re-adding them in the storyboard.
Issued #2: I did not made a call to searchResultsTableView.reloadData(). Since the network call was async, results were received in the lambda after the tableview was updated. Forcing an update sovled the problem.