Reputation: 85
In short, the user taps a row in a tableview (which contains data from an location) and I want to display the map with the appropriate annotation when the user taps the row. So far no luck.
I have implemented the suggestions to use a delegate pattern. Now that I have implemented it, the app keeps crashing.
I will post the code, but after browsing through dozens of stack overflow questions, I am not able to solve it. Hope you guys can help!
//MAIN CONTROLLER
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, UISearchBarDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
//SearchTable initiation
let searchTable = storyboard!.instantiateViewController(withIdentifier: "SearchTableViewController") as! SearchTableViewController
searchController = UISearchController(searchResultsController: searchTable)
searchController?.searchResultsUpdater = searchTable
searchController?.hidesNavigationBarDuringPresentation = false
searchController?.dimsBackgroundDuringPresentation = true
searchController?.definesPresentationContext = true
//Searchbar initiation
let searchBar = searchController!.searchBar
searchBar.sizeToFit()
searchBar.keyboardAppearance = UIKeyboardAppearance.dark
searchBar.placeholder = "Search locations"
navigationItem.titleView = searchController?.searchBar
searchBar.delegate = self
searchTable.mapView = mapView
searchTable.handleMapSearchDelegate = self
}
//Update the map
func updateMap(_ location: CLLocation) {
let region = MKCoordinateRegion.init(center: location.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
mapView.setRegion(region, animated: true)
}
//Cancel searchBar
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.text = nil
searchBar.setShowsCancelButton(false, animated: true)
searchBar.endEditing(true)
}
}
//Center the map on current user's location
extension ViewController: MKMapViewDelegate {
func centerMapOnUserLocation() {
guard let coordinate = locationManager.location?.coordinate else { return }
let coordinateRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: regionRadius * 2.0, longitudinalMeters: regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
}
//Zoom into selected location from search
extension ViewController: HandleMapSearch {
func dropPinZoomIn(placemark: MKAnnotation) {
updateMap(CLLocation(latitude: placemark.coordinate.latitude, longitude: placemark.coordinate.longitude))
searchController.searchBar.text = placemark.title!
}
}
//Zoom into selected location from selectedRow
extension ViewController: HandleSelectedLocation {
func dropPinAndZoomIn(placemark: MKAnnotation) {
updateMap(CLLocation(latitude: placemark.coordinate.latitude, longitude: placemark.coordinate.longitude))
}
}
// TABLEVIEWCONTROLLER
import UIKit
import MapKit
protocol HandleSelectedLocation {
func dropPinAndZoomIn(placemark: MKAnnotation)
}
//Initialize the TableViewController
class CategoriesController: UIViewController, UITableViewDataSource, UITableViewDelegate {
//Variables
var mapView: MKMapView?
var selectedItem = [MKAnnotation]()
var handleSelectedLocationDelegate: HandleSelectedLocation? = nil
//Outlet
@IBOutlet var tableView: UITableView!
//Load view
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return location.count
}
//Fill cells with locations
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "allCell") ?? UITableViewCell(style: .subtitle, reuseIdentifier: "allCell")
let locations = location[indexPath.row]
cell.textLabel?.text = locations.title
cell.textLabel?.textColor = UIColor.white
cell.detailTextLabel?.text = locations.rating
cell.detailTextLabel?.textColor = UIColor.white
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let annotation = mapView!.annotations
let locationSelected = annotation[indexPath.row]
handleSelectedLocationDelegate?.dropPinAndZoomIn(placemark: locationSelected)
tableView.isHidden = true
}
}
//SEARCHTABLEVIEWCONTROLLER
**protocol HandleMapSearch {
func dropPinZoomIn(placemark: MKAnnotation)
}
//Make searches appear
class SearchTableViewController: UITableViewController, UISearchResultsUpdating, UISearchBarDelegate {
//Variables
var mapView: MKMapView? = nil
var matchingItems = [MKAnnotation]()
var handleMapSearchDelegate: HandleMapSearch? = nil
func updateSearchResults(for searchController: UISearchController) {
guard let _ = mapView,
let searchBarText = searchController.searchBar.text else { return }
matchingItems = self.mapView!.annotations.filter { annotation -> Bool in
if annotation.title!?.range(of: searchBarText, options: .caseInsensitive) != nil {
return true
}
if annotation.subtitle!?.range(of: searchBarText, options: .caseInsensitive) != nil {
return true
}
return false
}
self.tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
//Fill tableView with location provided by search
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
let selectedItem = matchingItems[indexPath.row]
cell.textLabel?.text = selectedItem.title!
cell.textLabel?.textColor = UIColor.white
cell.detailTextLabel?.text = selectedItem.subtitle!
cell.detailTextLabel?.textColor = UIColor.white
return cell
}
//Go to map and show selected annotation
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem = matchingItems[indexPath.row]
handleMapSearchDelegate?.dropPinZoomIn(placemark: selectedItem)
dismiss(animated: true, completion: nil)
}
}**
//CustomAnnotation
import Foundation
import MapKit
class CustomAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var extraDescription: String?
var title: String?
var subtitle: String?
var country: String?
init(coordinate: CLLocationCoordinate2D) {
self.coordinate = coordinate
}
}
//Location struct
import Foundation
import UIKit
import MapKit
//Annotations
struct SomeLocation {
let title: String
let rating: String
let description: String
let country: String
let latitude: Double
let longitude: Double
}
QUESTION: Have I implemented the delegate pattern correctly? When I added the last extension, it gave me an issue with launching the app. After reversing that change, it worked but of course the delegate didn't do anything.
If you need any clarification, or extra code, please ask.
I can not seem to solve this issue, all help is appreciated greatly!
Upvotes: 0
Views: 291
Reputation: 7451
How about the delegate pattern?
e.g.
protocol SecondViewControllerDelegate: class {
func didSelectRow(at: YourLocationType)
}
class SecondViewController: UITableViewController {
weak var delegate: SecondViewControllerDelegate?
...
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Get the location
delegate?.didSelectRow(at: location)
}
...
}
MainViewController:
...
let vc = SecondViewController
vc.delegate = self
...
extension MainViewController: SecondViewControllerDelegate {
func didSelectRow(at: YourLocationType) {
// Direct user to annotation when cell of tableview tapped
}
}
Upvotes: 2