I'm building a small app with Swift where I want to achieve following: - MapView with several Annotations - Click on one of the annotations shows some Details in an overlay that is moving in from the bottom and fill approx. half of the screen - The upper half of the display still shows the MapView. A click on the map closes the details
Now I've read a lot about different possibilities. First I've tried using a ContainerView with something like this:
@IBOutlet weak var detailsContainerYPosition: NSLayoutConstraint!
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
self. detailsContainerYPosition.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
}
The problem with that was, that the viewDidLoad() method was only fired once and I wanted to use that to build up the Details page after passing some data to the controller class.
So next I've played around with a custom segue and cam up with this:
class DetailsSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
var mapView = self.sourceViewController.view as UIView!
var detailsView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
detailsView.frame = CGRectMake(0.0, screenHeight, screenWidth, 140)
// Access the app's key window and insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(detailsView, aboveSubview: mapView)
// Animate the transition.
UIView.animateWithDuration(0.4, animations: { () -> Void in
detailsView.frame = CGRectOffset(detailsView.frame, 0.0, -140)
}, completion: nil)
}
}
This seems to be the better solution because I could send some data to the new controller within the prepareForSegue() function AND the viewDidLoad() method is fired every time this segue is initiated but now I'm struggling: - Everytime the annotation is clicked and the segue is called, a new View is inserted. But if ONE detail view is already visible, I would like to just update this one - I can't find any way to unwind this segue when didDeselectAnnotationView is called from the MapView.
But perhaps anybody here has even a better way to achieve an UI like this.
I've finally managed to get what I wanted by manually adding a ViewController and a view like this:
let detailsWidth: CGFloat = view.bounds.width
let detailsHeight: CGFloat = 150
let detailsViewFrame: CGRect = CGRectMake(0, view.bounds.height, detailsWidth, detailsHeight)
detailsController = storyboard?.instantiateViewControllerWithIdentifier("details") as! DetailsViewController
detailsController.descriptionText = "I'm a text that was passed from the MainViewController"
self.addChildViewController(detailsController)
detailsController.view.frame = detailsViewFrame
view.addSubview(detailsController.view)
detailsController.didMoveToParentViewController(self)
And when dismissing the Details, I remove the Controller like this:
self.detailsController.removeFromParentViewController()
I created a small sample to demonstrate my solution: https://github.com/flavordaaave/iOS-Container-View-Overlay
来源:https://stackoverflow.com/questions/29633740/viewcontroller-as-overlay-on-mapview-in-swift-like-google-maps-application-user