In this page

Custom Puck View

This example shows how to customize your location puck view.

  1. To achieve a custom Puck view, you can inherit from the NGLUserLocationAnnotationView class.

  2. Replace the custom puck view of the NGLMapView

For all code examples, refer to Maps Code Examples

CustomPuckViewController view source

1import UIKit
2import Nbmap
3class CustomPuckViewController: UIViewController {
4    var nbMapView: NGLMapView! {
5        didSet {
6            oldValue?.removeFromSuperview()
7            if let mapView = nbMapView {
8                view.insertSubview(mapView, at: 0)
9            }
10        }
11    }
12    override func viewDidLoad() {
13        super.viewDidLoad()
14        nbMapView = NGLMapView(frame:self.view.bounds)
15        nbMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
16        nbMapView.delegate = self
17        nbMapView.userTrackingMode = .follow
18    }
19}
20
21// MARK: - NGLMapViewDelegate
22extension CustomPuckViewController: NGLMapViewDelegate {
23 
24    /**
25     Asks the user styling options for each default user location annotation view.
26     
27     This method is called many times during gesturing, so you should avoid performing
28     complex or performance-intensive tasks in your implementation.
29     
30     @param mapView The map view that is tracking the user's location.
31     */
32    func mapView(styleForDefaultUserLocationAnnotationView mapView: NGLMapView) -> NGLUserLocationAnnotationViewStyle {
33        let locationStyle =  NGLUserLocationAnnotationViewStyle()
34        /**
35         The fill color for the puck view.
36         */
37        locationStyle.puckFillColor = UIColor.blue
38        /**
39         The shadow color for the puck view.
40         */
41        locationStyle.puckShadowColor = UIColor.red
42        /**
43         The shadow opacity for the puck view.
44         Set any value between 0.0 and 1.0.
45         The default value of this property is equal to `0.25`
46         */
47        locationStyle.puckShadowOpacity = 0.25
48        /**
49         The fill color for the arrow puck.
50         */
51        locationStyle.puckArrowFillColor = UIColor.black
52        /**
53         The fill color for the puck view.
54         */
55        locationStyle.haloFillColor = UIColor.white
56       
57        if #available(iOS 14, *) {
58            /**
59             The halo fill color for the approximate view.
60             */
61            locationStyle.approximateHaloFillColor = UIColor.white
62            /**
63             The halo border color for the approximate view.
64             */
65            locationStyle.approximateHaloBorderColor = UIColor.white
66            /**
67             The halo border width for the approximate view.
68             The default value of this property is equal to `2.0`
69             */
70            locationStyle.approximateHaloBorderWidth = 2.0
71            /**
72             The halo opacity for the approximate view.
73             Set any value between 0.0 and 1.0
74             The default value of this property is equal to `0.15`
75             */
76            locationStyle.approximateHaloOpacity = 0.15
77        }
78      
79        return locationStyle
80    }
81    
82    /**
83     Returns a view object to mark the given point annotation object on the map.
84     Implement this method to mark a point annotation with a view object. If you
85     want to mark a particular point annotation with a static image instead, omit
86     this method or have it return `nil` for that annotation, then implement
87     `-mapView:imageForAnnotation:` instead.
88     Annotation views are compatible with UIKit, Core Animation, and other Cocoa
89     Touch frameworks. On the other hand, static annotation images use less memory
90     and draw more quickly than annotation views.
91     The user location annotation view can also be customized via this method. When
92     `annotation` is an instance of `NGLUserLocation` (or equal to the map view's
93     `userLocation` property), return an instance of `NGLUserLocationAnnotationView`
94     (or a subclass thereof).
95     @param mapView The map view that requested the annotation view.
96     @param annotation The object representing the annotation that is about to be
97        displayed.
98     @return The view object to display for the given annotation or `nil` if you
99        want to display an annotation image instead.
100     */
101    func mapView(_ mapView: NGLMapView, viewFor annotation: NGLAnnotation) -> NGLAnnotationView?  {
102        let annotationView = CustomUserLocationAnnotationView(frame: CGRect.zero)
103        annotationView.frame = CGRectMake(0, 0, annotationView.intrinsicContentSize.width, annotationView.intrinsicContentSize.height);
104        return annotationView
105    }
106  
107}

CustomUserLocationAnnotationView

1import UIKit
2import Nbmap
3let CustomUserLocationDotSize: CGFloat = 10
4class CustomUserLocationAnnotationView: NGLUserLocationAnnotationView {
5    
6    override init(frame: CGRect) {
7        super.init(frame: frame)
8        self.backgroundColor = .clear
9    }
10    
11    required init?(coder aDecoder: NSCoder) {
12        fatalError("init(coder:) has not been implemented")
13    }
14    
15    override func update() {
16        updateFrame(with: intrinsicContentSize)
17        setNeedsDisplay()
18    }
19    
20    override var intrinsicContentSize: CGSize {
21        let carSize = CGSize(width: 30, height: 60)
22        return (mapView?.userTrackingMode == .followWithCourse) ? carSize : dotSize()
23    }
24    
25    func dotSize() -> CGSize {
26        let minDotSize: CGFloat = 30
27        let dotSize = max(minDotSize, accuracyInPoints())
28        return CGSize(width: dotSize, height: dotSize)
29    }
30    
31    func updateFrame(with size: CGSize) {
32        if frame.size.equalTo(size) {
33            return
34        }
35        
36        // Update frame size, keeping the existing center point.
37        var newFrame = frame
38        let oldCenter = center
39        newFrame.size = size
40        frame = newFrame
41        center = oldCenter
42    }
43    
44    func accuracyInPoints() -> CGFloat {
45        guard let mapView = mapView, let userLocation = userLocation else {
46            return 0
47        }
48        let metersPerPoint = mapView.metersPerPoint(atLatitude: userLocation.coordinate.latitude)
49        return CGFloat((userLocation.location?.horizontalAccuracy ?? 0) / metersPerPoint)
50    }
51    
52    override func draw(_ rect: CGRect) {
53        if mapView?.userTrackingMode == .followWithCourse {
54            drawCar()
55        } else {
56            drawDot()
57        }
58    }
59    
60    func drawDot() {
61        // Accuracy
62        let accuracy = accuracyInPoints()
63        let center = bounds.size.width / 2.0 - accuracy / 2.0
64        let accuracyPath = UIBezierPath(ovalIn: CGRect(x: center, y: center, width: accuracy, height: accuracy))
65        let accuracyColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.4)
66        accuracyColor.setFill()
67        accuracyPath.fill()
68        
69        // Dot
70        let dotCenter = bounds.size.width / 2.0 - CustomUserLocationDotSize / 2.0
71        let dotPath = UIBezierPath(ovalIn: CGRect(x: dotCenter, y: dotCenter, width: CustomUserLocationDotSize, height: CustomUserLocationDotSize))
72        UIColor.green.setFill()
73        dotPath.fill()
74        
75        UIColor.black.setStroke()
76        dotPath.lineWidth = 1
77        dotPath.stroke()
78        
79        // Accuracy text
80        let font = UIFont.systemFont(ofSize: 11)
81        let attributes: [NSAttributedString.Key: Any] = [
82            .font: font,
83            .backgroundColor: UIColor(white: 0, alpha: 0.5),
84            .foregroundColor: UIColor.white
85        ]
86        let accuracyText = NSString(format: "%.0f", accuracy)
87        accuracyText.draw(at: CGPoint.zero, withAttributes: attributes)
88    }
89    
90    func drawCar() {
91        let fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
92        let strokeColor = UIColor(red: 0.592, green: 0.592, blue: 0.592, alpha: 1)
93        let fillColor2 = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
94        
95        let bezier2Path = UIBezierPath()
96        bezier2Path.move(to: CGPoint(x: 30, y: 7.86))
97        bezier2Path.addLine(to: CGPoint(x: 30, y: 52.66))
98        bezier2Path.addCurve(to: CGPoint(x: 0, y: 52.66), controlPoint1: CGPoint(x: 30, y: 62.05), controlPoint2: CGPoint(x: 0, y: 62.84))
99        bezier2Path.addCurve(to: CGPoint(x: 0, y: 7.86), controlPoint1: CGPoint(x: 0, y: 42.48), controlPoint2: CGPoint(x: 0, y: 17.89))
100        bezier2Path.addCurve(to: CGPoint(x: 30, y: 7.86), controlPoint1: CGPoint(x: -0, y: -2.17), controlPoint2: CGPoint(x: 30, y: -3.05))
101        bezier2Path.close()
102        bezier2Path.usesEvenOddFillRule = true
103        
104        fillColor.setFill()
105        bezier2Path.fill()
106        
107        let bezier3Path = UIBezierPath()
108        bezier3Path.move(to: CGPoint(x: 30, y: 7.86))
109        bezier3Path.addLine(to: CGPoint(x: 30, y: 52.66))
110        bezier3Path.addCurve(to: CGPoint(x: 0, y: 52.66), controlPoint1: CGPoint(x: 30, y: 62.05), controlPoint2: CGPoint(x: 0, y: 62.84))
111        bezier3Path.addCurve(to: CGPoint(x: 0, y: 7.86), controlPoint1: CGPoint(x: 0, y: 42.48), controlPoint2: CGPoint(x: 0, y: 17.89))
112        bezier3Path.addCurve(to: CGPoint(x: 30, y: 7.86), controlPoint1: CGPoint(x: 0, y: -2.17), controlPoint2: CGPoint(x: 30, y: -3.05))
113        bezier3Path.close()
114        strokeColor.setStroke()
115        bezier3Path.lineWidth = 1
116        bezier3Path.stroke()
117        
118        let bezier4Path = UIBezierPath()
119        bezier4Path.move(to: CGPoint(x: 15.56, y: 4.26))
120        bezier4Path.addCurve(to: CGPoint(x: 26, y: 6), controlPoint1: CGPoint(x: 21, y: 4.26), controlPoint2: CGPoint(x: 26, y: 6))
121        bezier4Path.addCurve(to: CGPoint(x: 23, y: 21), controlPoint1: CGPoint(x: 26, y: 6), controlPoint2: CGPoint(x: 29, y: 17))
122        bezier4Path.addCurve(to: CGPoint(x: 16, y: 21), controlPoint1: CGPoint(x: 20.03, y: 22.98), controlPoint2: CGPoint(x: 16, y: 21))
123        bezier4Path.addCurve(to: CGPoint(x: 7, y: 21), controlPoint1: CGPoint(x: 16, y: 21), controlPoint2: CGPoint(x: 9.02, y: 23.53))
124        bezier4Path.addCurve(to: CGPoint(x: 4, y: 6), controlPoint1: CGPoint(x: 3, y: 16), controlPoint2: CGPoint(x: 4, y: 6))
125        bezier4Path.addCurve(to: CGPoint(x: 15.56, y: 4.26), controlPoint1: CGPoint(x: 4, y: 6), controlPoint2: CGPoint(x: 10.12, y: 4.26))
126        bezier4Path.close()
127        bezier4Path.usesEvenOddFillRule = true
128        
129        fillColor2.setFill()
130        bezier4Path.fill()
131        
132        let rectanglePath = UIBezierPath()
133        rectanglePath.move(to: CGPoint(x: 25, y: 46))
134        rectanglePath.addCurve(to: CGPoint(x: 21, y: 55), controlPoint1: CGPoint(x: 31, y: 46), controlPoint2: CGPoint(x: 28.5, y: 55))
135        rectanglePath.addCurve(to: CGPoint(x: 9, y: 55), controlPoint1: CGPoint(x: 13.5, y: 55), controlPoint2: CGPoint(x: 14, y: 55))
136        rectanglePath.addCurve(to: CGPoint(x: 5, y: 46), controlPoint1: CGPoint(x: 4, y: 55), controlPoint2: CGPoint(x: 0, y: 46))
137        rectanglePath.addCurve(to: CGPoint(x: 25, y: 46), controlPoint1: CGPoint(x: 10, y: 46), controlPoint2: CGPoint(x: 19, y: 46))
138        rectanglePath.close()
139        UIColor.white.setFill()
140        rectanglePath.fill()
141        
142        let bezierPath = UIBezierPath()
143        UIColor.white.setFill()
144        bezierPath.fill()
145        
146        let rectangle2Path = UIBezierPath()
147        rectangle2Path.move(to: CGPoint(x: 2, y: 35))
148        rectangle2Path.addCurve(to: CGPoint(x: 4.36, y: 35), controlPoint1: CGPoint(x: 2, y: 39), controlPoint2: CGPoint(x: 4.36, y: 35))
149        rectangle2Path.addCurve(to: CGPoint(x: 4.36, y: 22), controlPoint1: CGPoint(x: 4.36, y: 35), controlPoint2: CGPoint(x: 5.55, y: 26))
150        rectangle2Path.addCurve(to: CGPoint(x: 2, y: 22), controlPoint1: CGPoint(x: 3.18, y: 18), controlPoint2: CGPoint(x: 2, y: 22))
151        rectangle2Path.addCurve(to: CGPoint(x: 2, y: 35), controlPoint1: CGPoint(x: 2, y: 22), controlPoint2: CGPoint(x: 2, y: 31))
152        rectangle2Path.close()
153        UIColor.white.setFill()
154        rectangle2Path.fill()
155        
156        let rectangle3Path = UIBezierPath()
157        rectangle3Path.move(to: CGPoint(x: 28, y: 35))
158        rectangle3Path.addCurve(to: CGPoint(x: 25.64, y: 35), controlPoint1: CGPoint(x: 28, y: 39), controlPoint2: CGPoint(x: 25.64, y: 35))
159        rectangle3Path.addCurve(to: CGPoint(x: 25.64, y: 22), controlPoint1: CGPoint(x: 25.64, y: 35), controlPoint2: CGPoint(x: 24.45, y:26))
160        rectangle3Path.addCurve(to: CGPoint(x: 28, y: 22), controlPoint1: CGPoint(x: 25.82, y: 18), controlPoint2: CGPoint(x: 28, y:22))
161        rectangle3Path.addCurve(to: CGPoint(x: 28, y: 35), controlPoint1: CGPoint(x: 28, y: 22), controlPoint2: CGPoint(x: 28, y:31))
162        rectangle3Path.close()
163        UIColor.white.setFill()
164        rectangle3Path.fill()
165    }
166}

This example provided code is for a view controller (CustomPuckViewController) that sets up a map view (NGLMapView) and customizes the user location style and view.

Initialization of MapView:

  1. The nbMapView property is initialized as an instance of NGLMapView with the frame matching the bounds of the view controller's view.

  2. The autoresizingMask is set to allow the map view to resize automatically with the view controller's view.

  3. The delegate property is set to self to receive map view delegate callbacks.

  4. The userTrackingMode is set to .follow to enable user location tracking and center the map on the user's location.

Custom User Location Style:

  1. The mapView(styleForDefaultUserLocationAnnotationView:) method is implemented to provide custom styling options for the default user location annotation view.

  2. An instance of NGLUserLocationAnnotationViewStyle is created and configured with various properties to customize the appearance of the user location puck, arrow, and halo.

  3. The method returns the configured NGLUserLocationAnnotationViewStyle object.

Custom user location view:

  1. The mapView(_:viewFor:) method is implemented to provide a custom annotation view for the user location.

  2. An instance of NGLUserLocationAnnotationView is created and its frame is set based on the intrinsic content size.

  3. The method returns the CustomUserLocationAnnotationView object as the view for the user location annotation.

DIDN'T FIND WHAT YOU LOOKING FOR?