我目前正在尝试实现与搜索功能关联的地图。对于包含表格视图的叠加层,我决定选择一个名为FloatingPanel的库。无论如何,这并不重要,因为它不会影响基本代码等。
正在SearchResultsTableViewController
内部读取数据,并由MapViewController
传递给passData()
。
在passData()
内部,正在调用addAnnotationToMap()
中名为MapViewController
的函数来处理数据-在函数内部打印将始终返回正确的值。
在该函数的内部,我试图向地图添加注释,但是它不起作用。什么都没发生-但是当我执行print(mapView.annotations)
时,将返回包含我的注释数组。
顺便说一句,我不得不在loadViewIfNeeded()
上添加MapViewController
,因为没有mapView
返回nil(使用覆盖视图之后)。
很抱歉,代码量-但是可能会有一些相关的代码。我没有包含表格视图的代码。
MapViewController
class MapViewController: UIViewController, FloatingPanelControllerDelegate, UISearchBarDelegate {
var fpc: FloatingPanelController!
var searchVC = SearchResultTableViewController()
private enum AnnotationReuseID: String {
case pin
}
@IBOutlet private var mapView: MKMapView!
var mapItems: [MKMapItem]?
var boundingRegion: MKCoordinateRegion?
override func viewDidLoad() {
super.viewDidLoad()
if let region = boundingRegion {
mapView.region = region
}
fpc = FloatingPanelController()
fpc.delegate = self
// Initialize FloatingPanelController and add the view
fpc.surfaceView.backgroundColor = .clear
fpc.surfaceView.cornerRadius = 9.0
fpc.surfaceView.shadowHidden = false
searchVC = (storyboard?.instantiateViewController(withIdentifier: "SearchPanel") as! SearchResultTableViewController)
// Set a content view controller
fpc.set(contentViewController: searchVC)
fpc.track(scrollView: searchVC.tableView)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Add FloatingPanel to a view with animation.
fpc.addPanel(toParent: self, animated: true)
fpc.move(to: .tip, animated: true)
// Must be here
searchVC.searchController.searchBar.delegate = self
}
func addAnnotationToMap() {
loadViewIfNeeded()
guard let item = mapItems?.first else { return }
guard let coordinate = item.placemark.location?.coordinate else { return }
let annotation = MKPointAnnotation()
annotation.title = item.name
annotation.coordinate = coordinate
mapView.addAnnotation(annotation)
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searchBar.resignFirstResponder()
searchBar.showsCancelButton = false
fpc.move(to: .tip, animated: true)
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
searchBar.showsCancelButton = true
searchVC.tableView.alpha = 1.0
fpc.move(to: .full, animated: true)
searchVC.hideHeader()
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
fpc.move(to: .half, animated: true)
searchVC.showHeader()
}
func floatingPanelDidMove(_ vc: FloatingPanelController) {
let y = vc.surfaceView.frame.origin.y
let tipY = vc.originYOfSurface(for: .tip)
if y > tipY - 44.0 {
let progress = max(0.0, min((tipY - y) / 44.0, 1.0))
self.searchVC.tableView.alpha = progress
}
}
func floatingPanelWillBeginDragging(_ vc: FloatingPanelController) {
if vc.position == .full {
searchVC.searchBar.showsCancelButton = false
searchVC.searchBar.resignFirstResponder()
}
}
func floatingPanelDidEndDragging(_ vc: FloatingPanelController, withVelocity velocity: CGPoint, targetPosition: FloatingPanelPosition) {
UIView.animate(withDuration: 0.25,
delay: 0.0,
options: .allowUserInteraction,
animations: {
if targetPosition == .tip {
self.searchVC.tableView.alpha = 0.0
self.searchVC.hideHeader()
} else if targetPosition == .half {
self.searchVC.tableView.alpha = 1.0
self.searchVC.showHeader()
} else {
self.searchVC.tableView.alpha = 1.0
self.searchVC.hideHeader()
}
}, completion: nil)
}
}
SearchViewController
class SearchResultTableViewController: UIViewController {
@IBOutlet weak var searchBar: UISearchBar!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var visualEffectView: UIVisualEffectView!
private enum CellReuseID: String {
case resultCell
}
private var places: [MKMapItem]? {
didSet {
tableView.reloadData()
}
}
private var suggestionController: SuggestionsTableTableViewController!
var searchController: UISearchController!
private var localSearch: MKLocalSearch? {
willSet {
places = nil
localSearch?.cancel()
}
}
private var boundingRegion: MKCoordinateRegion?
override func awakeFromNib() {
super.awakeFromNib()
suggestionController = SuggestionsTableTableViewController()
suggestionController.tableView.delegate = self
searchController = UISearchController(searchResultsController: suggestionController)
searchController.searchResultsUpdater = suggestionController
searchController.searchBar.isUserInteractionEnabled = false
searchController.searchBar.alpha = 0.5
}
override func viewDidLoad() {
super.viewDidLoad()
searchBar.addSubview(searchController.searchBar)
searchController.searchBar.searchBarStyle = .minimal
searchController.searchBar.tintColor = .black
searchController.searchBar.isUserInteractionEnabled = true
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
hideHeader()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if #available(iOS 10, *) {
visualEffectView.layer.cornerRadius = 9.0
visualEffectView.clipsToBounds = true
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
func showHeader() {
changeHeader(height: 116.0)
}
func hideHeader() {
changeHeader(height: 0.0)
}
func changeHeader(height: CGFloat) {
tableView.beginUpdates()
if let headerView = tableView.tableHeaderView {
UIView.animate(withDuration: 0.25) {
var frame = headerView.frame
frame.size.height = height
self.tableView.tableHeaderView?.frame = frame
}
}
tableView.endUpdates()
}
func passData() {
guard let mapViewController = storyboard?.instantiateViewController(withIdentifier: "map") as? MapViewController else { return }
guard let mapItem = places?.first else { return }
guard let coordinate = mapItem.placemark.location?.coordinate else { return }
let span = MKCoordinateSpan(latitudeDelta: coordinate.latitude, longitudeDelta: coordinate.longitude)
let region = MKCoordinateRegion(center: coordinate, span: span)
mapViewController.boundingRegion = region
mapViewController.mapItems = [mapItem]
mapViewController.addAnnotationToMap()
}
private func search(for suggestedCompletion: MKLocalSearchCompletion) {
let searchRequest = MKLocalSearch.Request(completion: suggestedCompletion)
search(using: searchRequest)
}
private func search(for queryString: String?) {
let searchRequest = MKLocalSearch.Request()
searchRequest.naturalLanguageQuery = queryString
search(using: searchRequest)
}
private func search(using searchRequest: MKLocalSearch.Request) {
if let region = boundingRegion {
searchRequest.region = region
}
UIApplication.shared.isNetworkActivityIndicatorVisible = true
localSearch = MKLocalSearch(request: searchRequest)
localSearch?.start { [weak self] (response, error) in
if error == nil {
self?.passData()
} else {
self?.displaySearchError(error)
return
}
self?.places = response?.mapItems
self?.boundingRegion = response?.boundingRegion
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
private func displaySearchError(_ error: Error?) {
if let error = error as NSError?, let errorString = error.userInfo[NSLocalizedDescriptionKey] as? String {
let alertController = UIAlertController(title: "Could not find any places.", message: errorString, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alertController, animated: true, completion: nil)
}
}
}