Custom Puck View
This example shows how to customize your location puck view.
-
To achieve a custom Puck view, you can inherit from the NGLUserLocationAnnotationView class.
-
Replace the custom puck view of the NGLMapView
For all code examples, refer to Maps Code Examples
CustomPuckViewController view source
1import UIKit2import Nbmap3class CustomPuckViewController: UIViewController {4var nbMapView: NGLMapView! {5didSet {6oldValue?.removeFromSuperview()7if let mapView = nbMapView {8view.insertSubview(mapView, at: 0)9}10}11}12override func viewDidLoad() {13super.viewDidLoad()14nbMapView = NGLMapView(frame:self.view.bounds)15nbMapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]16nbMapView.delegate = self17nbMapView.userTrackingMode = .follow18}19}2021// MARK: - NGLMapViewDelegate22extension CustomPuckViewController: NGLMapViewDelegate {2324/**25Asks the user styling options for each default user location annotation view.2627This method is called many times during gesturing, so you should avoid performing28complex or performance-intensive tasks in your implementation.2930@param mapView The map view that is tracking the user's location.31*/32func mapView(styleForDefaultUserLocationAnnotationView mapView: NGLMapView) -> NGLUserLocationAnnotationViewStyle {33let locationStyle = NGLUserLocationAnnotationViewStyle()34/**35The fill color for the puck view.36*/37locationStyle.puckFillColor = UIColor.blue38/**39The shadow color for the puck view.40*/41locationStyle.puckShadowColor = UIColor.red42/**43The shadow opacity for the puck view.44Set any value between 0.0 and 1.0.45The default value of this property is equal to `0.25`46*/47locationStyle.puckShadowOpacity = 0.2548/**49The fill color for the arrow puck.50*/51locationStyle.puckArrowFillColor = UIColor.black52/**53The fill color for the puck view.54*/55locationStyle.haloFillColor = UIColor.white5657if #available(iOS 14, *) {58/**59The halo fill color for the approximate view.60*/61locationStyle.approximateHaloFillColor = UIColor.white62/**63The halo border color for the approximate view.64*/65locationStyle.approximateHaloBorderColor = UIColor.white66/**67The halo border width for the approximate view.68The default value of this property is equal to `2.0`69*/70locationStyle.approximateHaloBorderWidth = 2.071/**72The halo opacity for the approximate view.73Set any value between 0.0 and 1.074The default value of this property is equal to `0.15`75*/76locationStyle.approximateHaloOpacity = 0.1577}7879return locationStyle80}8182/**83Returns a view object to mark the given point annotation object on the map.84Implement this method to mark a point annotation with a view object. If you85want to mark a particular point annotation with a static image instead, omit86this method or have it return `nil` for that annotation, then implement87`-mapView:imageForAnnotation:` instead.88Annotation views are compatible with UIKit, Core Animation, and other Cocoa89Touch frameworks. On the other hand, static annotation images use less memory90and draw more quickly than annotation views.91The user location annotation view can also be customized via this method. When92`annotation` is an instance of `NGLUserLocation` (or equal to the map view's93`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 be97displayed.98@return The view object to display for the given annotation or `nil` if you99want to display an annotation image instead.100*/101func mapView(_ mapView: NGLMapView, viewFor annotation: NGLAnnotation) -> NGLAnnotationView? {102let annotationView = CustomUserLocationAnnotationView(frame: CGRect.zero)103annotationView.frame = CGRectMake(0, 0, annotationView.intrinsicContentSize.width, annotationView.intrinsicContentSize.height);104return annotationView105}106107}
CustomUserLocationAnnotationView
1import UIKit2import Nbmap3let CustomUserLocationDotSize: CGFloat = 104class CustomUserLocationAnnotationView: NGLUserLocationAnnotationView {56override init(frame: CGRect) {7super.init(frame: frame)8self.backgroundColor = .clear9}1011required init?(coder aDecoder: NSCoder) {12fatalError("init(coder:) has not been implemented")13}1415override func update() {16updateFrame(with: intrinsicContentSize)17setNeedsDisplay()18}1920override var intrinsicContentSize: CGSize {21let carSize = CGSize(width: 30, height: 60)22return (mapView?.userTrackingMode == .followWithCourse) ? carSize : dotSize()23}2425func dotSize() -> CGSize {26let minDotSize: CGFloat = 3027let dotSize = max(minDotSize, accuracyInPoints())28return CGSize(width: dotSize, height: dotSize)29}3031func updateFrame(with size: CGSize) {32if frame.size.equalTo(size) {33return34}3536// Update frame size, keeping the existing center point.37var newFrame = frame38let oldCenter = center39newFrame.size = size40frame = newFrame41center = oldCenter42}4344func accuracyInPoints() -> CGFloat {45guard let mapView = mapView, let userLocation = userLocation else {46return 047}48let metersPerPoint = mapView.metersPerPoint(atLatitude: userLocation.coordinate.latitude)49return CGFloat((userLocation.location?.horizontalAccuracy ?? 0) / metersPerPoint)50}5152override func draw(_ rect: CGRect) {53if mapView?.userTrackingMode == .followWithCourse {54drawCar()55} else {56drawDot()57}58}5960func drawDot() {61// Accuracy62let accuracy = accuracyInPoints()63let center = bounds.size.width / 2.0 - accuracy / 2.064let accuracyPath = UIBezierPath(ovalIn: CGRect(x: center, y: center, width: accuracy, height: accuracy))65let accuracyColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.4)66accuracyColor.setFill()67accuracyPath.fill()6869// Dot70let dotCenter = bounds.size.width / 2.0 - CustomUserLocationDotSize / 2.071let dotPath = UIBezierPath(ovalIn: CGRect(x: dotCenter, y: dotCenter, width: CustomUserLocationDotSize, height: CustomUserLocationDotSize))72UIColor.green.setFill()73dotPath.fill()7475UIColor.black.setStroke()76dotPath.lineWidth = 177dotPath.stroke()7879// Accuracy text80let font = UIFont.systemFont(ofSize: 11)81let attributes: [NSAttributedString.Key: Any] = [82.font: font,83.backgroundColor: UIColor(white: 0, alpha: 0.5),84.foregroundColor: UIColor.white85]86let accuracyText = NSString(format: "%.0f", accuracy)87accuracyText.draw(at: CGPoint.zero, withAttributes: attributes)88}8990func drawCar() {91let fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)92let strokeColor = UIColor(red: 0.592, green: 0.592, blue: 0.592, alpha: 1)93let fillColor2 = UIColor(red: 1, green: 1, blue: 1, alpha: 1)9495let bezier2Path = UIBezierPath()96bezier2Path.move(to: CGPoint(x: 30, y: 7.86))97bezier2Path.addLine(to: CGPoint(x: 30, y: 52.66))98bezier2Path.addCurve(to: CGPoint(x: 0, y: 52.66), controlPoint1: CGPoint(x: 30, y: 62.05), controlPoint2: CGPoint(x: 0, y: 62.84))99bezier2Path.addCurve(to: CGPoint(x: 0, y: 7.86), controlPoint1: CGPoint(x: 0, y: 42.48), controlPoint2: CGPoint(x: 0, y: 17.89))100bezier2Path.addCurve(to: CGPoint(x: 30, y: 7.86), controlPoint1: CGPoint(x: -0, y: -2.17), controlPoint2: CGPoint(x: 30, y: -3.05))101bezier2Path.close()102bezier2Path.usesEvenOddFillRule = true103104fillColor.setFill()105bezier2Path.fill()106107let bezier3Path = UIBezierPath()108bezier3Path.move(to: CGPoint(x: 30, y: 7.86))109bezier3Path.addLine(to: CGPoint(x: 30, y: 52.66))110bezier3Path.addCurve(to: CGPoint(x: 0, y: 52.66), controlPoint1: CGPoint(x: 30, y: 62.05), controlPoint2: CGPoint(x: 0, y: 62.84))111bezier3Path.addCurve(to: CGPoint(x: 0, y: 7.86), controlPoint1: CGPoint(x: 0, y: 42.48), controlPoint2: CGPoint(x: 0, y: 17.89))112bezier3Path.addCurve(to: CGPoint(x: 30, y: 7.86), controlPoint1: CGPoint(x: 0, y: -2.17), controlPoint2: CGPoint(x: 30, y: -3.05))113bezier3Path.close()114strokeColor.setStroke()115bezier3Path.lineWidth = 1116bezier3Path.stroke()117118let bezier4Path = UIBezierPath()119bezier4Path.move(to: CGPoint(x: 15.56, y: 4.26))120bezier4Path.addCurve(to: CGPoint(x: 26, y: 6), controlPoint1: CGPoint(x: 21, y: 4.26), controlPoint2: CGPoint(x: 26, y: 6))121bezier4Path.addCurve(to: CGPoint(x: 23, y: 21), controlPoint1: CGPoint(x: 26, y: 6), controlPoint2: CGPoint(x: 29, y: 17))122bezier4Path.addCurve(to: CGPoint(x: 16, y: 21), controlPoint1: CGPoint(x: 20.03, y: 22.98), controlPoint2: CGPoint(x: 16, y: 21))123bezier4Path.addCurve(to: CGPoint(x: 7, y: 21), controlPoint1: CGPoint(x: 16, y: 21), controlPoint2: CGPoint(x: 9.02, y: 23.53))124bezier4Path.addCurve(to: CGPoint(x: 4, y: 6), controlPoint1: CGPoint(x: 3, y: 16), controlPoint2: CGPoint(x: 4, y: 6))125bezier4Path.addCurve(to: CGPoint(x: 15.56, y: 4.26), controlPoint1: CGPoint(x: 4, y: 6), controlPoint2: CGPoint(x: 10.12, y: 4.26))126bezier4Path.close()127bezier4Path.usesEvenOddFillRule = true128129fillColor2.setFill()130bezier4Path.fill()131132let rectanglePath = UIBezierPath()133rectanglePath.move(to: CGPoint(x: 25, y: 46))134rectanglePath.addCurve(to: CGPoint(x: 21, y: 55), controlPoint1: CGPoint(x: 31, y: 46), controlPoint2: CGPoint(x: 28.5, y: 55))135rectanglePath.addCurve(to: CGPoint(x: 9, y: 55), controlPoint1: CGPoint(x: 13.5, y: 55), controlPoint2: CGPoint(x: 14, y: 55))136rectanglePath.addCurve(to: CGPoint(x: 5, y: 46), controlPoint1: CGPoint(x: 4, y: 55), controlPoint2: CGPoint(x: 0, y: 46))137rectanglePath.addCurve(to: CGPoint(x: 25, y: 46), controlPoint1: CGPoint(x: 10, y: 46), controlPoint2: CGPoint(x: 19, y: 46))138rectanglePath.close()139UIColor.white.setFill()140rectanglePath.fill()141142let bezierPath = UIBezierPath()143UIColor.white.setFill()144bezierPath.fill()145146let rectangle2Path = UIBezierPath()147rectangle2Path.move(to: CGPoint(x: 2, y: 35))148rectangle2Path.addCurve(to: CGPoint(x: 4.36, y: 35), controlPoint1: CGPoint(x: 2, y: 39), controlPoint2: CGPoint(x: 4.36, y: 35))149rectangle2Path.addCurve(to: CGPoint(x: 4.36, y: 22), controlPoint1: CGPoint(x: 4.36, y: 35), controlPoint2: CGPoint(x: 5.55, y: 26))150rectangle2Path.addCurve(to: CGPoint(x: 2, y: 22), controlPoint1: CGPoint(x: 3.18, y: 18), controlPoint2: CGPoint(x: 2, y: 22))151rectangle2Path.addCurve(to: CGPoint(x: 2, y: 35), controlPoint1: CGPoint(x: 2, y: 22), controlPoint2: CGPoint(x: 2, y: 31))152rectangle2Path.close()153UIColor.white.setFill()154rectangle2Path.fill()155156let rectangle3Path = UIBezierPath()157rectangle3Path.move(to: CGPoint(x: 28, y: 35))158rectangle3Path.addCurve(to: CGPoint(x: 25.64, y: 35), controlPoint1: CGPoint(x: 28, y: 39), controlPoint2: CGPoint(x: 25.64, y: 35))159rectangle3Path.addCurve(to: CGPoint(x: 25.64, y: 22), controlPoint1: CGPoint(x: 25.64, y: 35), controlPoint2: CGPoint(x: 24.45, y:26))160rectangle3Path.addCurve(to: CGPoint(x: 28, y: 22), controlPoint1: CGPoint(x: 25.82, y: 18), controlPoint2: CGPoint(x: 28, y:22))161rectangle3Path.addCurve(to: CGPoint(x: 28, y: 35), controlPoint1: CGPoint(x: 28, y: 22), controlPoint2: CGPoint(x: 28, y:31))162rectangle3Path.close()163UIColor.white.setFill()164rectangle3Path.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:
-
The nbMapView property is initialized as an instance of NGLMapView with the frame matching the bounds of the view controller's view.
-
The autoresizingMask is set to allow the map view to resize automatically with the view controller's view.
-
The delegate property is set to self to receive map view delegate callbacks.
-
The userTrackingMode is set to .follow to enable user location tracking and center the map on the user's location.
Custom User Location Style:
-
The mapView(styleForDefaultUserLocationAnnotationView:) method is implemented to provide custom styling options for the default user location annotation view.
-
An instance of NGLUserLocationAnnotationViewStyle is created and configured with various properties to customize the appearance of the user location puck, arrow, and halo.
-
The method returns the configured NGLUserLocationAnnotationViewStyle object.
Custom user location view:
-
The mapView(_:viewFor:) method is implemented to provide a custom annotation view for the user location.
-
An instance of NGLUserLocationAnnotationView is created and its frame is set based on the intrinsic content size.
-
The method returns the CustomUserLocationAnnotationView object as the view for the user location annotation.