AnnotationView open when searching - ios

I have a map search controller that when I search for some annotation, its zooming on it .
Also I would like that when I press the result it will zoom on the correct pin and automatically open the annotationView of the specific annotation
here is my code :
https://github.com/hellzkitchen/stackoverflow-041917.git
import UIKit
import MapKit
class LocationSearchTable : UITableViewController {
var matchingItems = [CustomAnnotations]()
var mapView: MKMapView? = nil
var handleMapSearchDelegate:HandleMapSearch? = nil
func parseAddress(selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
func search(keywords:String) {
self.matchingItems.removeAll()
for annotation in self.mapView!.annotations {
if annotation.isKindOfClass(CustomAnnotations) {
//Just an example here for searching annotation by title, you could add other filtering actions else.
if (annotation.title??.rangeOfString(keywords) != nil) {
self.matchingItems.append(annotation as! CustomAnnotations)
}
}
}
self.tableView.reloadData()
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MapSearchCell", forIndexPath: indexPath)
let selectedItem = matchingItems[indexPath.row]
cell.textLabel?.text = selectedItem.title
cell.detailTextLabel?.text = selectedItem.subtitle
return cell
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = matchingItems[indexPath.row]//.placemark
handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
dismissViewControllerAnimated(true, completion: nil)
}
}

After you search for a designated annotation, there is a method selectAnnotation:
So when you tap on that result, use
mapView.selectAnnotation(resultAnnotation, animated: true)

Related

Not getting directions router when didSelectRow is tapped in Swift?

I am creating an app where I have a detailViewController where there is an address of place and when the user clicks on the address - it opens the apple maps (which what I want) but it does not show that router as a destination to go to that address. I am getting to: -118.277.... and from myLocation (which is good), except the to part & it also tells directions not available. So, I want the address that didSelectRow is pressed to bring the destination directions in apple maps when clicked. thank you for the help.
UPDATED I just debug and created a watch for place.location?.coordinate I see some result but they are just longitude and latitude. But, I want to show the address instead of lat & long.
Here is my code:
import UIKit
import Social
import CoreLocation
class DetailsViewController: BaseViewController, UITableViewDelegate, UITableViewDataSource{
#IBOutlet weak var tableView: UITableView!
var nearMeIndexSelected = NearMeIndexTitle()
var place: Place!
var coordinate :CLLocationCoordinate2D?
var nearMeRequest : NearMeRequest!
var locationManager = CLLocationManager()
let infoCellId = "info-cell"
let interactiveCellId = "interactive-cell"
let directionsSegueId = "directions-segue"
let gallerySegueId = "gallery-segue"
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var annotation: ARAnnotation!
override func viewDidLoad() {
super.viewDidLoad()
if self.place != nil {
self.title = place.placeName
self.tableView.delegate = self
self.tableView.dataSource = self
self.loadDetailInfo()
print("Place is not nil")
} else {
print("Place is nil")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSections(in tableView: UITableView) -> Int {
return 4
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 4
}
else if section == 1 {
return 0
}
else if section == 2 {
return 1
}
else {
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cellId = infoCellId
if indexPath.section != 0 {
cellId = interactiveCellId
}
let cell: UITableViewCell = self.tableView.dequeueReusableCell(withIdentifier: cellId)! as UITableViewCell
var key = "Not Available"
var value = "Not Available"
if indexPath.section == 0 {
if indexPath.row == 0 {
key = "Name"
if self.place.placeName.characters.count > 0 {
value = self.place.placeName
}
} else if indexPath.row == 1 {
key = "Address"
if let address = self.place.address {
if address.characters.count > 0 {
value = address
}
}
} else if indexPath.row == 2 {
key = "Phone number"
if let phoneNumber = self.place.phoneNumber {
if phoneNumber.characters.count > 0 {
value = phoneNumber
}
}
} else if indexPath.row == 3 {
key = "Website"
if let website = self.place.website {
if website.characters.count > 0 {
value = website
}
}
}
}
else if indexPath.section == 2 {
key = "Get Directions"
} else {
key = "Photos of \(self.place.placeName)"
}
if indexPath.section == 0 {
cell.textLabel?.text = key
cell.detailTextLabel?.text = value
} else {
cell.textLabel?.text = key
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
if indexPath.row == 1 {
print("it works!")
if let coordinate = place.location?.coordinate {
if let url = URL(string:"http://maps.apple.com/maps?daddr=\(coordinate.longitude),\(coordinate.latitude)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
if indexPath.row == 2 {
guard let phoneNumber = self.place.phoneNumber,
phoneNumber.count > 0,
let url = URL(string: "tel:\(phoneNumber.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)")
else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
} else { return }
}
else if indexPath.section == 2 {
self.performSegue(withIdentifier: directionsSegueId, sender: self)
} else {
if let photoReferences = self.place.photoReferences {
if photoReferences.count != 0 {
self.performSegue(withIdentifier: gallerySegueId, sender: self)
return
}
}
// View Photo button
let alertController = UIAlertController(title: "No photos", message: "Sorry, but there are no photos found for this place.", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Ok", style: .cancel) { action in
// ...
}
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section == 0 {
return " Info"
}
else if section == 1{
//return " Request a Uber"
return ""
}
else if section == 2 {
return " Navigation"
} else {
return " Photo"
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == directionsSegueId {
let controller = segue.destination as! DirectionsViewController
controller.place = self.place
} else if segue.identifier == gallerySegueId {
let navController = segue.destination as! UINavigationController
let galleryController = navController.viewControllers[0] as! GalleryViewController
galleryController.place = self.place
}
}
func generatePlaceUrl() -> URL {
let placeNamePlus = self.place.placeName.replacingOccurrences(of: " ", with: "+")
var urlString = "https://www.google.com/maps/place/" + placeNamePlus
if let addressPlus = self.place.address?.replacingOccurrences(of: " ", with: "+") {
urlString = urlString + "+" + addressPlus
}
let url = URL.init(string: urlString)
if let finalUrl = url {
return finalUrl
}
return URL.init(string: "https://www.maps.google.com")!
}
func loadDetailInfo() {
let loader = PlacesLoader()
self.activityIndicator.startAnimating()
self.tableView.isHidden = true
loader.loadDetailInformation(forPlace: self.place) { (dictionary, error) in
guard dictionary != nil else {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.tableView.isHidden = false
self.tableView.reloadData()
}
return
}
if let resultDictionary = dictionary!["result"] as? NSDictionary {
self.place.address = (resultDictionary["formatted_address"] as? String)!
self.place.phoneNumber = resultDictionary["formatted_phone_number"] as? String
self.place.website = resultDictionary["website"] as? String
if let photosArr = resultDictionary["photos"] as? [NSDictionary] {
for photoDict in photosArr {
let photoReference = photoDict["photo_reference"] as? String
if self.place.photoReferences == nil {
self.place.photoReferences = [String]()
}
self.place.photoReferences?.append(photoReference!)
}
}
}
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.tableView.isHidden = false
self.tableView.reloadData()
}
}
}
}
Your latitude and longitude values are the wrong way round, try this:
if let url = URL(string:"http://maps.apple.com/maps?daddr=\(coordinate.latitude),\(coordinate.longitude)") {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}

Search Controller Not Displaying Search Results

I'm creating a map app that autocompletes searches in the search bar. However, the autocomplete results are not displaying.
Here is my code on the ViewController with the mapView:
import UIKit
import MapKit
import CoreLocation
class BarMapVC: UIViewController {
#IBOutlet weak var doneBtn: UIBarButtonItem!
#IBOutlet weak var locationBtn: UIButton!
#IBOutlet weak var barMapView: MKMapView!
var locationManager = CLLocationManager()
let authorizationStatus = CLLocationManager.authorizationStatus()
let regionRadius: Double = 1000
var resultSearchController:UISearchController? = nil
override func viewDidLoad() {
super.viewDidLoad()
barMapView.showsUserLocation = true
barMapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
configureLocationServices()
centerMapOnUserLocation()
let locationSearchTable =
storyboard!.instantiateViewController(withIdentifier:
"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
locationSearchTable.mapView = barMapView
}
#IBAction func locationBtnWasPressed(_ sender: Any) {
if authorizationStatus == .authorizedAlways || authorizationStatus
== .authorizedWhenInUse {
centerMapOnUserLocation()
}
}
}
extension BarMapVC: MKMapViewDelegate {
func centerMapOnUserLocation() {
guard let coordinate = locationManager.location?.coordinate else {
return }
let coordinateRegion =
MKCoordinateRegionMakeWithDistance(coordinate, regionRadius * 2.0,
regionRadius * 2.0)
barMapView.setRegion(coordinateRegion, animated: true)
}
}
extension BarMapVC: CLLocationManagerDelegate {
func configureLocationServices() {
if authorizationStatus == .notDetermined {
locationManager.requestAlwaysAuthorization()
} else {
return
}
}
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) {
centerMapOnUserLocation()
}
}
And here is my code from the UITableViewController file that displays the autocompleted search results:
import UIKit
import MapKit
class LocationSearchTable : UITableViewController {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
func parseAddress(selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil &&
selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil ||
selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea
!= nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil &&
selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
}
func updateSearchResultsForSearchController(searchController:
UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else {
return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return matchingItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = parseAddress(selectedItem:
selectedItem)
return cell
}
}
Is there somewhere I entered an incorrect variable or did something wrong? I've been stuck on this error for two days. Please help thanks!

How to filter the data in model class and display accurately according to letters not words in table view?

Here I need to place a local search for my model class data that displayed in the table view and I tried to implement the search for that I placed successfully but the search was should be like when I enter the first letter it should filter depending on the first letter, not with th letter in all objects
for example in brand names I am having Fastrack, Microsoft, Dell, apple, fila
if I type f then it should display only fastrack and fila but it displays fastrack,fila and Microsoft which containing f
but I need like when the first letter should be equal to f then it should display fastback and file only not Microsoft and if the second letter equals with any another word then it should display filtered results
Here is my code
var bannerModel = [BrandBanners]()
var searchActive : Bool?
var filteredData = [BrandBanners]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchActive == true {
return filteredData.count
}
else {
return bannerModel.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! superBrandTableViewCell
if searchActive == true {
cell.brandImageView.kf.indicatorType = .activity
let item = filteredData[indexPath.row]
if URL(string: (item.brandImageforMobile)!) != nil {
let resource = ImageResource(downloadURL: URL(string: (item.brandImageforMobile)!)!, cacheKey: item.brandImageforMobile)
cell.brandImageView.kf.setImage(with: resource, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
}
else {
cell.brandImageView.image = #imageLiteral(resourceName: "placeholder")
}
cell.activityIndicator.stopAnimating()
self.activityIndicator.stopAnimating()
}
else {
cell.brandImageView.kf.indicatorType = .activity
let item = bannerModel[indexPath.row]
if URL(string: (item.brandImageforMobile)!) != nil {
let resource = ImageResource(downloadURL: URL(string: (item.brandImageforMobile)!)!, cacheKey: item.brandImageforMobile)
cell.brandImageView.kf.setImage(with: resource, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
}
else {
cell.brandImageView.image = #imageLiteral(resourceName: "placeholder")
}
cell.activityIndicator.stopAnimating()
self.activityIndicator.stopAnimating()
}
return cell
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
self.filteredData = self.bannerModel
} else {
var lowerCase = searchBar.text!
self.filteredData = bannerModel.filter({($0.brand?.lowercased().contains(lowerCase.lowercased()))!})
}
self.searchActive = true
brandTableView.reloadData()
}
You should use hasPrefix Method to achieve this
Example
struct User {
var userName : String = ""
var password : String = ""
}
var array = [User.init(userName:"Test",password:"123456"),User.init(userName:"TEMP",password:"123456"),User.init(userName:"Prashant",password:"123456"),User.init(userName:"Test",password:"123456")]
array = array.filter{$0.userName.hasPrefix("T")} // Observe hasPrefix instead of contains
print(array)
For your code
self.filteredData = bannerModel.filter({($0.brand?.lowercased().hasPrefix(lowerCase.lowercased()))!})
EDIT For Type Safety
self.filteredData = bannerModel.filter({($0.brand ?? "").lowercased().hasPrefix("H".lowercased())}
Hope it is helpful
You can do this:
self.filteredData = bannerModel.filter({($0.brand?.lowercased().hasPrefix(lowerCase.lowercased()))!})

Searching Annotations in MapView

I followed a how-to-search-for-location-using-apples-mapkit about searching annotations in mapView
and its searching around the world with MKLocalSearch.
However, I don't want to to search with MKLocalSearch but search my own annotations i added myself like these for example:
let LitzmanLocation = CLLocationCoordinate2DMake(32.100668,34.775192)
// Drop a pin
let Litzman = MKPointAnnotation()
Litzman.coordinate = LitzmanLocation
Litzman.title = "Litzman Bar"
Litzman.subtitle = "נמל תל אביב 18,תל אביב"
mapView.addAnnotation(Litzman)
let ShalvataLocation = CLLocationCoordinate2DMake(32.101145,34.775163)
// Drop a pin
let Shalvata = MKPointAnnotation()
Shalvata.coordinate = ShalvataLocation
Shalvata.title = "Shalvata"
Shalvata.subtitle = "האנגר 28,נמל תל אביב"
mapView.addAnnotation(Shalvata)
let MarkidLocation = CLLocationCoordinate2DMake(32.074961,34.781679)
// Drop a pin
let Markid = MKPointAnnotation()
Markid.coordinate = MarkidLocation
Markid.title = "Markid"
Markid.subtitle = "אבן גבירול 30,תל אביב"
mapView.addAnnotation(Markid)
Here is my Code:
MapViewController.Swift:
import UIKit
import MapKit
import CoreLocation
protocol HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark)
}
class MapViewController: UIViewController,MKMapViewDelegate, CLLocationManagerDelegate,UISearchBarDelegate{
#IBOutlet var mapView: MKMapView!
var resultSearchController:UISearchController? = nil
var selectedPin:MKPlacemark? = nil
#IBAction func MapSearchController(sender: AnyObject) {
resultSearchController!.hidesNavigationBarDuringPresentation = false
self.resultSearchController!.searchBar.delegate = self
presentViewController(resultSearchController!, animated: true, completion: nil)
self.resultSearchController!.searchBar.barTintColor = UIColor.blackColor()
self.resultSearchController!.searchBar.placeholder = "חפש ברים"
self.resultSearchController!.dimsBackgroundDuringPresentation = true
self.resultSearchController!.searchBar.sizeToFit()
}
override func viewDidLoad() {
super.viewDidLoad()
let locationSearchTable = storyboard!.instantiateViewControllerWithIdentifier("LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
locationSearchTable.mapView = mapView
locationSearchTable.handleMapSearchDelegate = self
}
}
}
extension MapViewController: HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark){
// cache the pin
selectedPin = placemark
// clear existing pins
mapView.removeAnnotations(mapView.annotations)
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "(city) (state)"
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
LocalSearchTable.Swift:
import UIKit
import MapKit
class LocationSearchTable : UITableViewController {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
var handleMapSearchDelegate:HandleMapSearch? = nil
func parseAddress(selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResultsForSearchController(searchController: UISearchController) {
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MapSearchCell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = parseAddress(selectedItem)
return cell
}
}
extension LocationSearchTable {
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedItem = matchingItems[indexPath.row].placemark
handleMapSearchDelegate?.dropPinZoomIn(selectedItem)
dismissViewControllerAnimated(true, completion: nil)
}
}
you just refer to this link
http://www.coderzheaven.com/2016/02/14/mapkit-demo-swift-annotation-custom-annotation-custom-annotation-with-button-search-showing-directions-apple-maps-ios/
you will understand the following topics
Show Annotation in Maps.
Show Custom Annotation in Maps.
Show Custom Annotation with Custom Button in Maps.
TL;DR
Example project: https://github.com/JakubMazur/SO40539590
Ok, so for the beginning I will suggest you separate data from your controller code. I choose json format as most universal one. So:
[
{
"title":"Litzman Bar",
"subtitle":"נמל תל אביב 18,תל אביב",
"coordinates":{
"lat":32.100668,
"lon":34.775192
}
},
{
"title":"Shalvata",
"subtitle":"האנגר 28,נמל תל אביב",
"coordinates":{
"lat":32.101145,
"lon":34.775163
}
},
{
"title":"Markid",
"subtitle":"אבן גבירול 30,תל אביב",
"coordinates":{
"lat":32.074961,
"lon":34.781679
}
}
]
This is basically your database.
Now let's parse it into an Array to use inside your ViewConttroller. Again I will suggest you split it into Model objects like Location and Coordinate. Let's have a look in one class of it as example:
class Location: NSObject {
var title : String = String()
var subtitle : String = String()
var coordinates : Coordinate = Coordinate()
public class func locationFromDictionary(_ dictionary : Dictionary<String, AnyObject>) -> Location {
let location : Location = Location()
location.title = dictionary["title"] as! String
location.subtitle = dictionary["subtitle"] as! String
location.coordinates = Coordinate.coordinateFromDictionary(dictionary["coordinates"] as! Dictionary<String, AnyObject>)
return location;
}
}
I will not paste the code for parsing json file to this objects, because that's what is not this question is about. You will find in into repository.
And now let's focus on question.
I will recommend you not to search annotations, but search your data model and redraw annotations when needed
In order to do that (i will use UISearchBar for it):
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
self.displayArray = self.locationsDatabase.filter() {
return $0.title.contains("i")
}
Then when you ovverride a setter like this:
var displayArray : Array<Location> = [] {
didSet {
self.mapView .removeAnnotations(self.mapView.annotations)
for location in displayArray {
let coords : Coordinate = location.coordinates
let point = MKPointAnnotation()
point.coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(coords.latitude),CLLocationDegrees(coords.longitude))
point.title = location.title
point.subtitle = location.subtitle
mapView.addAnnotation(point)
}
}
}
You can redraw your annotations. The edge cases like empty search field, case sensivity, dismissing keyboard I leave to you. But hope you will get the general idea. You may think that it's overengineered but having objects as a separate classes and universal format as an data input may benefit in the future.
Full project: https://github.com/JakubMazur/SO40539590

Filter an Array of data for use in a UITableView

I'm trying to filter an array that I create based on objects (as Strings) that I queried. They show up great, now I just want to filter and remake the array so that I can filter out the information I need. I'm not sure why I'm getting "Result of call 'filter' Is unused" in Xcode. I looked around but I can't figure this one out.
import UIKit
class RegionStoreTableViewController: UITableViewController, UISearchBarDelegate {
var selectedRegionStore : String? = nil
var selectedRegionStoreIndex : Int? = nil
var dataArray = [String]()
var filteredArray = [String]()
var employeeType : String? = nil
var searchText = ""
#IBOutlet weak var regionStoreSearchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
let prefs = NSUserDefaults.standardUserDefaults()
if prefs.valueForKey("EmployeeType") != nil {
employeeType = prefs.valueForKey("EmployeeType") as! String
// Employee Type
// Retail
if employeeType == "Retail" {
self.navigationItem.title = "Store Selector"
let query = PFQuery(className: "Stores")
query.orderByAscending("rStoreNumber")
query.findObjectsInBackgroundWithBlock({ (store: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for store in store! {
let theStore = store["storeName"] as! String
let storeNumber = store["rStoreNumber"] as! String
let storeString = storeNumber + " - " + theStore
print(theStore)
self.dataArray.append(storeString)
self.tableView.reloadData()
}
}
})
}
if employeeType == "Corporate" {
let query = PFQuery(className: "Regions")
query.orderByAscending("regionName")
query.findObjectsInBackgroundWithBlock { (region: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
for region in region! {
let theRegion = region["regionName"] as! String
print(theRegion)
self.dataArray.append(theRegion)
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(dataArray.count)
return dataArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("RegionStoreCell", forIndexPath: indexPath)
if searchText.isEmpty {
cell.textLabel?.text = dataArray[indexPath.row]
}
if searchText != "" {
dataArray.filter() {nil != $0.containsString(searchText) }
}
if indexPath.row == selectedRegionStoreIndex {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
return cell as UITableViewCell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let index = selectedRegionStoreIndex {
let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
cell?.accessoryType = .None
}
selectedRegionStoreIndex = indexPath.row
selectedRegionStore = dataArray[indexPath.row]
let cell = tableView.cellForRowAtIndexPath(indexPath)
cell?.accessoryType = .Checkmark
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SaveSelectedRegionStore" {
let cell = sender as! UITableViewCell
let indexPath = tableView.indexPathForCell(cell)
selectedRegionStoreIndex = indexPath?.row
if let index = selectedRegionStoreIndex {
selectedRegionStore = dataArray[index]
}
}
}
// MARK: Search Bar
// delegate in story board
func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
searchBar.showsCancelButton = true
}
func searchBarTextDidEndEditing(searchBar: UISearchBar) {
searchBar.showsCancelButton = false
}
func searchBarSearchButtonClicked(searchBar: UISearchBar) {
// add minimum length of search
searchText = searchBar.text!
self.tableView.reloadData()
}
func searchBarCancelButtonClicked(searchBar: UISearchBar) {
// clear out search box
searchBar.text = nil
// clear out search variable
searchText = ""
// reload the table
self.tableView.reloadData()
// hide keyboard
searchBar.resignFirstResponder()
}
}
Any suggestions?
I think you need to store your filtered array into another array.
let filterArray = dataArray.filter() {nil != $0.containsString(searchText)

Resources