Home

Awesome

MapViewPlus

Swift 5.0 iOS 10.0+ Version License Platform

<img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/basic_example_ld.gif" width=320> <img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/default_callout_ld.gif" width=320>

About

MapViewPlus gives you the missing methods of MapKit which are: imageForAnnotation and calloutViewForAnnotationView delegate methods.

Requirements

Installation

MapViewPlus is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'MapViewPlus'

If you don't use CocoaPods, you can drag and drop all the classes and use it in your project.

How to use

1) If you are using Interface Builder, set your map view's class and module as MapViewPlus:

<img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/settings_and_module.png">

2) Setup your Callout View Model:

import UIKit
import MapViewPlus

// CalloutViewModel is a protocol of MapVieWPlus. Currently, it has no requirements to its conformant classes.
class YourCalloutViewModel: CalloutViewModel {

  var title: String
  var image: UIImage

  init(title: String, image: UIImage) {
    self.title = title
    self.image = image
  }
}

3) Setup your CalloutView:

<img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/calloutview_example.png">
import UIKit
import MapViewPlus

// CalloutViewPlus is a protocol of MapViewPlus. 
// Currently, it has just one requirement -> func configureCallout(_ viewModel: CalloutViewModel)
class YourCalloutView: UIView, CalloutViewPlus {

  @IBOutlet weak var label: UILabel!
  @IBOutlet weak var imageView: UIImageView!

  func configureCallout(_ viewModel: CalloutViewModel) {
    let viewModel = viewModel as! YourCalloutViewModel

    label.text = viewModel.title
    imageView.image = viewModel.image
  }
}

4) Setup your Annotations in your View Controller:

import UIKit
import CoreLocation
import MapViewPlus

class YourViewController: UIViewController {

  @IBOutlet weak var mapView: MapViewPlus!

  override func viewDidLoad() {
    super.viewDidLoad()

    //Required
    mapView.delegate = self

    let viewModel = YourCalloutViewModel(title: "Cafe", image: UIImage(named: "cafe.png")!)

    let annotation = AnnotationPlus(viewModel: viewModel,
                                    coordinate: CLLocationCoordinate2DMake(50.11, 8.68))

    var annotations: [AnnotationPlus] = []
    annotations.append(annotation)

    mapView.setup(withAnnotations: annotations)
  }
}

5) Return the image for Annotation and the View for Callout

extension YourViewController: MapViewPlusDelegate {

  func mapView(_ mapView: MapViewPlus, imageFor annotation: AnnotationPlus) -> UIImage {
    return UIImage(named: "your_annotation_image.png")!
  }

  func mapView(_ mapView: MapViewPlus, calloutViewFor annotationView: AnnotationViewPlus) -> CalloutViewPlus{
    let calloutView = Bundle.main.loadNibNamed("YourCalloutView", owner: nil, options: nil)!.first as! YourCalloutView
    return calloutView
  }
}

This is it. You are all set and ready to go now.

Customization (Optional)

MapViewPlus is highly customizable:

CalloutViewCustomizerDelegate

func mapView(_ mapView: MapViewPlus, centerForCalloutViewOf annotationView: AnnotationViewPlus) -> CalloutViewPlusCenter
func mapView(_ mapView: MapViewPlus, boundsForCalloutViewOf annotationView: AnnotationViewPlus) -> CalloutViewPlusBound
func mapView(_ mapView: MapViewPlus, insetFor calloutView: CalloutViewPlus) -> CGFloat
func mapView(_ mapView: MapViewPlus, animationTypeForShowingCalloutViewOf annotationView: AnnotationViewPlus) -> CalloutViewShowingAnimationType
func mapView(_ mapView: MapViewPlus, animationTypeForHidingCalloutViewOf annoationView: AnnotationViewPlus) -> CalloutViewHidingAnimationType

AnchorViewCustomizerDelegate

func mapView(_ mapView: MapViewPlus, heightForAnchorOf calloutView: CalloutViewPlus) -> CGFloat
func mapView(_ mapView: MapViewPlus, fillColorForAnchorOf calloutView: CalloutViewPlus) -> UIColor

Notes

DefaultCalloutView

Forwarding Delegate Methods

MapViewPlus uses methods from MKMapViewDelegate, but not all of them. It forwards all of the delegate methods except mapView:viewForAnnotation:. This method is used internally and won't be redirected to your subclass.

Normally, MapViewPlus will abstract you from MapKit when you don't want to use the other methods of MKMapViewDelegate. But when you want to use the other methods from MKMapViewDelegate, you can easily do that without any extra efforts. Just write them down and they will get called by MapViewPlusDelegate. Please see how mapView(_:regionDidChangeAnimated:) method is being called in DefaultCalloutViewController.swift (in the example project) even if you don't conform to MKMapViewDelegate. If in the future, some new methods are added to MKMapViewDelegate, they will be automagically forwarded to you by MapViewPlusDelegate without a new version of the framework. There is no wrapping occuring in the background.

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Screenshots

<img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/basic_example_new.png" width=320> <img src="https://github.com/okhanokbay/MapViewPlus/blob/master/Example/default_callout_example_new.png" width=320>

License

MapViewPlus is available under the MIT license. See the LICENSE file for more info.