MapView에 표시 할 장소의 배열이 있습니다. 오른쪽 버튼을 표시하기 위해 MKAnnotationView를 만들었습니다. 그런 다음 detailView를 표시하고 segue를 통해 데이터를 전달하지만 잘못된 위치를 표시하고 있습니다. 내가 선택한 애너테이션에 문제가 있다고 생각합니다. 사용자는 한 번에 하나의 주석 만 선택할 수 있습니다.MKAnnotation View Segue
전체 클래스
import UIKit
import MapKit
class MapViewController: UIViewController, PlacesModelDelegate, CLLocationManagerDelegate {
@IBOutlet weak var mapView: MKMapView!
var places = [Place]()
var model:PlacesModel?
var locationManager:CLLocationManager?
var lastKnownLocation:CLLocation?
var selectedAnnotation: Place?
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
// Set map properties
mapView.showsUserLocation = true
// Instantiate location manager
locationManager = CLLocationManager()
locationManager?.delegate = self
// Instantiate places model if it is nil
if model == nil {
model = PlacesModel()
model?.delegate = self
}
// Call get places
model?.getPlaces()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Functions
func plotPins() {
var pinsArray = [MKPointAnnotation]()
// Go through the array of places and plot them
for p in places {
// Create a pin
let pin = MKPointAnnotation()
// Set its properties
pin.coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(p.lat), longitude: CLLocationDegrees(p.long))
pin.title = p.name
pin.subtitle = p.getTypeName()
// Add it to the map
mapView.addAnnotation(pin)
// Store the pin in the pinsArray
pinsArray.append(pin)
}
// Center the map
mapView.showAnnotations(pinsArray, animated: true)
}
func displaySettingsPopup() {
// Create alert controller
let alertController = UIAlertController(title: "Couldn't find your location",
message: "Location Services is turned off on your device or the GuideBookApp doesn't have permission to find your location. Please check your Privacy settings to continue.",
preferredStyle: .alert)
// Create settings button action
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (alertAction) in
if let appSettings = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.open(appSettings, options: [:], completionHandler: nil)
}
}
alertController.addAction(settingsAction)
// Create cancel button action
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
// Show the alert
present(alertController, animated: true, completion: nil)
}
// MARK: - Button Handlers
@IBAction func locationButtonTapped(_ sender: UIButton) {
// Check if location services are enabled
if CLLocationManager.locationServicesEnabled() {
// They're enabled, now check the authorization status
let status = CLLocationManager.authorizationStatus()
if status == .authorizedAlways || status == .authorizedWhenInUse {
// Has permission
locationManager?.startUpdatingLocation()
// Center the map on last location
if let actualLocation = lastKnownLocation {
mapView.setCenter(actualLocation.coordinate, animated: true)
}
}
else if status == .denied || status == .restricted {
// Doesn't have permission
// Tell user to check settings
displaySettingsPopup()
}
else if status == .notDetermined {
// Ask the user for permission
locationManager?.requestWhenInUseAuthorization()
}
}
else {
// Locations services are turned off
// Tell user to check settings
displaySettingsPopup()
}
}
// MARK: - PlacesModelDelegate Methods
func placesModel(places: [Place]) {
// Set places property
self.places = places
// Plot the pins
plotPins()
}
// MARK: - CLLocationManagerDelegate Methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
if let actualLocation = location {
// Create a pin
let pin = MKPointAnnotation()
pin.coordinate = CLLocationCoordinate2D(latitude: actualLocation.coordinate.latitude, longitude: actualLocation.coordinate.longitude)
// Center the map, only if it's the first time locating the user
if lastKnownLocation == nil {
mapView.setCenter(actualLocation.coordinate, animated: true)
}
// Save the pin
lastKnownLocation = actualLocation
}
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// See what the user has answered
if status == .denied {
// Tell user that this app doesn't have permission. They can change it in their settings
displaySettingsPopup()
}
else if status == .authorizedWhenInUse || status == .authorizedAlways {
// Permission granted
locationManager?.startUpdatingLocation()
}
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "marker"
var view: MKMarkerAnnotationView
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
performSegue(withIdentifier: "mapSegue", sender: view)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let selectedRow = mapView.selectedAnnotations.endIndex
let selectedPlace = places[selectedRow]
let detailModel = DetailModel()
detailModel.place = selectedPlace
let detailVC = segue.destination as! VenueDetailViewController
detailVC.model = detailModel
}
}
환경 모델
import UIKit
protocol PlacesModelDelegate {
func placesModel(places:[Place])
}
class PlacesModel: NSObject, FirebaseManagerDelegate {
// Properties
var delegate:PlacesModelDelegate?
var firManager:FirebaseManager?
func getPlaces() {
// Get places from FirebaseManager
if firManager == nil {
firManager = FirebaseManager()
firManager!.delegate = self
}
// Tell firebase manager to fetch places
firManager!.getPlacesFromDatabase()
}
func checkDataVersion() {
// Get version from FirebaseManager
if firManager == nil {
firManager = FirebaseManager()
firManager!.delegate = self
}
firManager!.getVersionFromDatabase()
}
// MARK: - FirebaseManager Delegate Methods
func firebaseManager(places: [Place]) {
// Notify the delegate
if let actualDelegate = delegate {
actualDelegate.placesModel(places: places)
}
}
}
FirebaseManager
import UIKit
import Firebase
@objc protocol FirebaseManagerDelegate {
@objc optional func firebaseManager(places:[Place])
@objc optional func firebaseManager(metaDataFor place:Place)
@objc optional func firebaseManager(imageName:String, imageData:Data)
}
class FirebaseManager: NSObject {
// MARK: - Properties
var ref: FIRDatabaseReference!
var delegate:FirebaseManagerDelegate?
// MARK: - Initializers
override init() {
// Initialize the database reference
ref = FIRDatabase.database().reference()
super.init()
}
// MARK: - Places Functions
func getPlacesFromDatabase() {
// Create an array to store all the places
var allPlaces = [Place]()
// Before we retrieve from Firebase, check cachemanager
if let cachedPlacesDict = CacheManager.getPlacesFromCache() {
// We have data in cache, parse that instead
// Call function to parse places dictionary
allPlaces = parsePlacesFrom(placesDict: cachedPlacesDict)
// Now return the places array
// Dispatch this code to be done on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(places: allPlaces)
}
} // End DispatchQueue
return
}
// Retrieve the list of Places from the database
ref.child("places").observeSingleEvent(of: .value, with: { (snapshot) in
let placesDict = snapshot.value as? NSDictionary
// See if data is actually present
if let actualPlacesDict = placesDict {
// We actually have a places dictionary
// Before working with the data, save it into cache
CacheManager.putPlacesIntoCache(data: actualPlacesDict)
// Call function to parse places dictionary
allPlaces = self.parsePlacesFrom(placesDict: actualPlacesDict)
// Now return the places array
// Dispatch this code to be done on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(places: allPlaces)
}
} // End DispatchQueue
}
}) // End observeSingleEvent
} // End getForYouFromDatabase
// MARK: - Meta Functions
func getMetaFromDatabase(place:Place) {
// Before fetching from firebase, check cache
if let cachedMetaDict = CacheManager.getMetaFromCache(placeId: place.id) {
// Parse the meta data
parseMetaFrom(metaDict: cachedMetaDict, place: place)
// Notify the delegate the the meta data has been fetched
// Dispatch this code to be done on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(metaDataFor: place)
}
} // End DispatchQueue
return
}
ref.child("meta").child(place.id).observe(.value, with: { (snapshot) in
// Get the dictionary from the snapshot
if let metaDict = snapshot.value as? NSDictionary {
// Save data into cache
CacheManager.putMetaIntoCache(data: metaDict, placeId: place.id)
// Parse firebase results
self.parseMetaFrom(metaDict: metaDict, place: place)
// Notify the delegate the the meta data has been fetched
// Dispatch this code to be done on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(metaDataFor: place)
}
} // End DispatchQueue
}
}) // End observeSingleEvent
} // End getMetaFromDatabase
func getImageFromDatabase(imageName:String) {
// Get the image
// Check cache first
if let imageData = CacheManager.getImageFromCache(imageName: imageName) {
// Notify the delegate on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(imageName: imageName, imageData: imageData)
}
} // End DispatchQueue
return
}
// Create the storage and file path references
let storage = FIRStorage.storage()
let imagePathReference = storage.reference(withPath: imageName)
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
imagePathReference.data(withMaxSize: 1 * 1024 * 1024) { data, error in
if error != nil {
// Uh-oh, an error occurred!
} else if data != nil {
// Data for the image is returned
// Save the image data into cache
CacheManager.putImageIntoCache(data: data!, imageName: imageName)
// Notify the delegate on the main thread
DispatchQueue.main.async {
// Notify the delegate
if let actualDelegate = self.delegate {
actualDelegate.firebaseManager?(imageName: imageName, imageData: data!)
}
} // End DispatchQueue
}
}
}
func closeObserversForPlace(placeId:String) {
// Remove observers from that place node
ref.child("meta").child(placeId).removeAllObservers()
}
// MARK: - Version Functions
func getVersionFromDatabase() {
// Get the version from the database
ref.child("version").observeSingleEvent(of: .value, with: { (snapshot) in
let versionString = snapshot.value as? String
if let databaseVersion = versionString {
let cachedVersion = CacheManager.getVersionFromCache()
if cachedVersion != nil {
// Compare the cached version number to the database version
if databaseVersion > cachedVersion! {
// Remove all cached data
CacheManager.removeAllCachedData()
CacheManager.putVersionIntoCache(version: databaseVersion)
}
}
else {
// Save the database version number to cache
CacheManager.putVersionIntoCache(version: databaseVersion)
}
}
})
}
// MARK: - Helper Functions
func parsePlacesFrom(placesDict:NSDictionary) -> [Place] {
// Declare an array to store the parsed out places
var allPlaces = [Place]()
// Loop through all of the KVPs of the placesDict
for (placeid, placedata) in placesDict {
let placeDataDict = placedata as! NSDictionary
// Create a Place object for each and add it to an array to be returned
let place = Place()
place.id = placeid as! String
place.name = placeDataDict["name"] as! String
place.addr = placeDataDict["address"] as! String
place.lat = placeDataDict["lat"] as! Float
place.long = placeDataDict["long"] as! Float
place.type = PlaceType(rawValue: placeDataDict["type"] as! Int)!
place.cellImageName = placeDataDict["imagesmall"] as! String
place.createDate = placeDataDict["creationDate"] as! Int
// Put this place object into an array for returning
allPlaces += [place]
}
return allPlaces
}
func parseMetaFrom(metaDict:NSDictionary, place:Place) {
place.desc = metaDict["desc"] as! String
place.detailImageName = metaDict["imagebig"] as! String
}
} // End class
동의합니다. 현재 선택된 주석을 어떻게 선택할 수 있는지 알고 있습니까? –
'mapView (_ mapView : MKMapView, annotationView view : MKAnnotationView, calloutAccessoryControlTapped 컨트롤 : UIControl) 내부에서 로직을 조작해야합니다. ' 나는 거기서 데이터를 얻고 나중에'prepare (segue : UIStoryboardSegue, 발신자 : Any?) '를 호출하여 _sender_ 매개 변수를 통해 정보를 전달하는 것이 좋습니다. 전에 말했듯이 : 나에게 더 많은 도움이 필요하면 더 많은 코드를 공유하거나 더 나아질 수 있습니다. 전체 레포를 공유하면 살펴볼 것입니다. 나는 더 많은 문맥이 필요하다. –
좋아, 고마워, 난 내 모델과 firebasemanager 클래스 플러스 전체 클래스를 포함하도록 내 질문을 업데이 트했습니다. 당신의 도움을 주셔서 감사합니다. –