Reputation: 32054
I have a master/detail application running on an iPad. When in landscape mode, I have both views up side-by-side. The right/detail view controller contains an MKMapView
.
The issue is that when selecting a different table cell in the left/master view controller, and essentially re-performing the segue, the entire detail view controller is reinstantiated.
This means that the MKMapView
I was using loses the user's position, and essentially starts from scratch, zooming in from the country scale to the street scale.
Is there a way to determine, prior to performing the segue, whether the detail view being displayed is already the one I want, and simply providing it new data and telling it to refresh?
For example:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
segueParkName = parkNames[indexPath.row]
self.performSegueWithIdentifier("showParkDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showParkDetails" {
let controller = (segue.destinationViewController as UINavigationController).topViewController as ParkDetailsController
NSLog("Controller: \(controller)") // Different instance every time!
controller.parkName = segueParkName
}
}
I would like to either:
Somehow tell iOS that by the time prepareForSegue
is reached, I'm okay with being provided a reused view controller, especially (!) if it's already displayed.
In the didSelectRowAtIndexPath
method, perform a custom segue and do my own pushing. But I really like the idea of using the built-in system segues so I don't have to be specific about what I'm pushing and where. It seems more device-agnostic to use Show Detail (eg. Replace)
than defining my own.
Upvotes: 1
Views: 472
Reputation: 30582
You can cancel a segue by implementing shouldPerformSegue however that is for the case where the park name is invalid for some reason, to prevent showing a view controller for an invalid park.
In this case the solution is use the reference to the detail controller in your master controller that the built-in master/detail template does for you. Then in prepareForSegue take the map from the old detail controller and put it on the new one.
As your app gets more complex it may no longer be suitable for the master to maintain a reference to the detail controller. For example, if you make a root controller that pushes a new master, then the master will not find the detail when the app is in portrait like the template app can. Thus in that case your class that implements the split controller delegate can also maintain the context for your master/detail (something that is initWithSplitViewController). By setting an owningContext param self on the splitViewController via a category in the init for this class, then you can access it from where you need to. E.g. setting the mapView on it from the master. And getting the mapView from it in the loadView of the detail.
Upvotes: 0
Reputation: 21536
I think, in your first suggestion, it will be troublesome if not impossible to abandon the segue once you are in prepareForSegue. So I would go with your second option. But you don't need to trigger a segue at all, if the detail viewController you want is already in place. So rather than
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
segueParkName = parkNames[indexPath.row]
self.performSegueWithIdentifier("showParkDetails", sender: self)
}
you might have something like...
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
segueParkName = parkNames[indexPath.row]
self.detailViewController.parkName = segueParkName
}
This assumes that you already have a property detailViewController pointing to your detail ViewController. It also assumes that the detailViewController will always be the one you need - if necessary, check the detailViewController class to see whether it is the MKMapView you want. Finally, if setting parkName doesn't achieve everything you need (e.g. animating the change), then just implement a new method in your MkMapView and call that in place of setting parkName.
EDIT Just to expand on that, you can use:
if self.detailViewController.isKindOfClass(yourMKMapViewSubclass) {
self.detailViewController.parkName = segueParkName
}
to test whether detailViewController is indeed your MkMapView.
Upvotes: 1