2

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:

enter image description here

@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.

Chirag Shah
  • 3,034
  • 1
  • 30
  • 61
flavordaaave
  • 644
  • 9
  • 21
  • See https://stackoverflow.com/questions/37967555/how-can-i-mimic-the-bottom-sheet-from-the-maps-app/51768193?noredirect=1#comment94269686_51768193 – GaétanZ Dec 16 '18 at 16:24

1 Answers1

4

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

flavordaaave
  • 644
  • 9
  • 21