1

I'm very new to IOS development so some of this might seem obvious. I've tried combining the examples for customize the user annotation and marking a place on the map with an image

I feel like I need to add the following lines of code and somehow attach this code to the user annotation described in the first link, but I have no idea how to do this. I'm guessing I could also insert some of these functions into the customUserLocationAnnotationView, but there is no obvious indicator of where to place this within that class.

func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -> MGLAnnotationImage? {
var annotationImage = mapView.dequeueReusableAnnotationImage(withIdentifier: "pisa")

if annotationImage == nil {
          var image = UIImage(named: "pisavector")!
          image = image.withAlignmentRectInsets(UIEdgeInsets(top: 0, left: 0, bottom: image.size.height/2, right: 0))
          annotationImage = MGLAnnotationImage(image: image, reuseIdentifier: "pisa")
     }
     return annotationImage
}

func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
     return true
}

EDIT

I don't just want to put an image in a random location like this

enter image description here

I want the image to be centered on the user annotation, and when the user moves, the image will also move, like the image below

enter image description here

As a side note

I'm also getting the error 'Failed to render and update auto auto layout status for ViewController (BYZ-38-tOr): The agent crashed Main.storyboard' but I don't think that's important, because my program still builds and runs on the simulator fine.

Sam
  • 1,765
  • 11
  • 82
  • 176

2 Answers2

3
override func viewDidLoad() {
    super.viewDidLoad()
    let mapView = MGLMapView(frame: view.bounds)
    mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    mapView.delegate = self

    mapView.userTrackingMode = .followWithHeading
    mapView.showsUserHeadingIndicator = true
    view.addSubview(mapView)
}  

func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
// Substitute our custom view for the user location annotation. This custom view is defined below.
    if annotation is MGLUserLocation && mapView.userLocation != nil {
        return CustomUserLocationAnnotationView()
    }
    return nil
}

// Create a subclass of MGLUserLocationAnnotationView.
class CustomUserLocationAnnotationView: MGLUserLocationAnnotationView {
    ...
}

Take a look at this example: https://www.mapbox.com/ios-sdk/maps/examples/user-location-annotation/

There is a method called setupLayers in CustomUserLocationAnnotationView. variable dot is a CALayer, so you can add a UIImage to a CALayer. Change the code in private func setupLayers() like below:

dot = CALayer()
let myImage = UIImage(named: "star")?.cgImage
dot.contents = myImage
layer.addSublayer(dot)
Hasti Ranjkesh
  • 643
  • 10
  • 26
  • When I add the bottom code to my viewDidLoad function I receive the error `Cannot use optional chaining on non-optional value of type 'MGLMapView'`. When I remove the optional chaining the build just fails. ....... Also, I'm trying to change the userAnnotation, is this going to do that? I'm concerned about the specific latitude and longitude coordinates because the image is supposed to be set on the coordinates of the user. – Sam Sep 26 '18 at 18:13
  • 1
    @Jacob I edited the bottom code, the error was because I defined my mapView optional and you defined non-optional. for the coordinate you need to set user location instead of the example coordinate.Use this code for user location: annotation.coordinate = mapView.userLocation.coordinate and the top code will change annotation image. – Hasti Ranjkesh Sep 26 '18 at 21:08
  • That's not quite what I want. I posted an edit in the question for you to look at. I want to be able to change that blue dot in the center so that it displays my image. When the user changes location, the image with move with the user. – Sam Sep 30 '18 at 00:51
  • @Jacob Ok you want to customize the user location annotation, I edited the answer. – Hasti Ranjkesh Sep 30 '18 at 06:06
  • Yeah I posted a link to thank example in my question, but I don't really know what to change in the CustomUserLocationAnnotation in order to make the annotation an image. – Sam Sep 30 '18 at 17:59
  • @Jacob If you wish to add the image in a layer, you can do it like this: https://stackoverflow.com/questions/13272512/add-uiimage-in-calayer, I also edited the answer – Hasti Ranjkesh Oct 01 '18 at 08:01
  • How can I use MGLUserLocationAnnotationView with camera MGLMapCamera? I want wo use pitch but the arrow is still in the 2d perspective. – desmeit Jul 16 '20 at 13:49
1

This code works for me (using Mapbox iOS SDK 3.6.0/Swift 4.2/iOS 12.1). The image used is a 24-bit PNG. Saving it at either 2X or 4X the nominal size makes for a clean, non-jaggy image (I can't tell the difference between the two).

sample

func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        // Substitute our custom view for the user location annotation. This custom view is defined below.
        if annotation is MGLUserLocation  { // && mapView.userLocation != nil
            let reuseIdentifier = "userLocationView"

            // For better performance, always try to reuse existing annotations.
            var userLocAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)

            // If there’s no reusable annotation view available, initialize a new one.
            if userLocAnnotationView == nil {
                userLocAnnotationView =  CustomUserLocationAnnotationView(reuseIdentifier: reuseIdentifier)
            }

            return userLocAnnotationView
        }
        else if annotation is MGLAnnotationView{
            // return another kind of annotation
        }

        return nil
    }


    class CustomUserLocationAnnotationView: MGLUserLocationAnnotationView {
        let size: CGFloat = 36
        var dot: CALayer!

        // -update is a method inherited from MGLUserLocationAnnotationView. It updates the appearance of the user location annotation when needed. This can be called many times a second, so be careful to keep it lightweight.
        override func update() {
            if frame.isNull {
                frame = CGRect(x: 0, y: 0, width: size, height: size)
                return setNeedsLayout()
            }

            setupLayers()
        }

        private func setupLayers() {
            // This dot forms the base of the annotation.
            if dot == nil {
                dot = CALayer()
                dot.bounds = CGRect(x: 0, y: 0, width: size, height: size)

                let image = UIImage(named: "locationPip")?.cgImage

                dot.contents = image

                dot.contentsScale = UIScreen.main.scale
                layer.addSublayer(dot)
            }
        }
    }
Stonetip
  • 1,150
  • 10
  • 20