> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/ios/maps/llms.txt)

# Customize the location puck

![Customized the location puck on the map.](https://docs.mapbox.com/ios/assets/ideal-img/maps-examples-customize-puck.c0a14e5.480.png)

This example demonstrates how to customize a 2D location puck with the **Mapbox Maps SDK for iOS**. In the code below, a puck is placed on the [`MapView`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapview/) over the user's location and a collapsible set of buttons is spawned that allows the user to customize the puck at runtime.

The list of adjustable features includes:

-   visibility
-   opacity

-   image
-   bearing

-   accuracy ring
-   map style

-   projection

The code uses the [Puck2DConfiguration](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/puck2dconfiguration/) structure to apply the request changes to the puck.

> **Note (warning): Troubleshooting with the iOS simulator**
> 
> If you cannot see the location puck while testing in the iOS simulator, try the following:
> 
> -   Simulate a location in Xcode by going to **Debug > Simulate Location** and select a location. Then, return to the Simulator and the location puck should appear over the selected location.
> -   If you already declined location permissions for your application you can update permission in Settings. Go to **Settings > Privacy & Security > Location Services**. Then find your application and update the location access to "Always" or "While Using the App".

> **Note: iOS Examples App Available**
> 
> This example code is part of the **Maps SDK for iOS Examples App**, a working iOS project [available on Github](https://github.com/mapbox/mapbox-maps-ios/tree/11.29.0/Sources/Examples). iOS developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.
> 
> See our [Run the Maps SDK for iOS Examples App](https://docs.mapbox.com/help/tutorials/maps-sdk-ios-examples-app/) tutorial for step-by-step instructions.

**Swift**

Title: `Custom2DPuckExample.swift`

[View on GitHub](https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/Examples/All%20Examples/Custom2DPuckExample.swift)

```swift
import UIKit
import MapboxMaps

final class ViewController: UIViewController {
    private var cancelables = Set<AnyCancelable>()
    private var mapView: MapView!
    private var puckConfiguration = Puck2DConfiguration.makeDefault(showBearing: true)

    private var showsPuck: PuckVisibility = .isVisible {
        didSet {
            updatePuckUI()
        }
    }

    private var puckImage: PuckImage = .blueDot {
        didSet {
            updatePuckUI()
        }
    }

    private var showsBearing: PuckBearingVisibility = .isVisible {
        didSet {
            updatePuckUI()
        }
    }

    private var showsAccuracyRing: PuckAccuracyRingVisibility = .isHidden {
        didSet {
            updatePuckUI()
        }
    }

    private var puckBearing: PuckBearing = .heading {
        didSet {
            mapView.location.options.puckBearing = puckBearing
        }
    }

    private var style: Style = .light {
        didSet {
            try? mapView.mapboxMap.setStyleImportConfigProperty(for: "basemap", config: "lightPreset", value: style.lightPreset)
        }
    }

    private var projection: StyleProjectionName = .mercator {
        didSet {
            updateProjection()
        }
    }

    private var puckOpacity: PuckOpaticy = .opaque {
        didSet {
            updatePuckUI()
        }
    }

    private enum PuckOpaticy: Double {
        case opaque = 1
        case semiTransparent = 0.5

        mutating func toggle() {
            self = self == .opaque ? .semiTransparent : .opaque
        }
    }

    private enum PuckVisibility {
        case isVisible
        case isHidden

        var isVisible: Bool {
            switch self {
            case .isVisible:
                return true
            case .isHidden:
                return false
            }
        }

        mutating func toggle() {
            self = self == .isVisible ? .isHidden : .isVisible
        }
    }

    private enum PuckImage: CaseIterable {
        case dash
        case jpegSquare
        case blueDot

        var image: UIImage? {
            switch self {
            case .dash:
                return UIImage(named: "dash-puck")
            case .jpegSquare:
                return UIImage(named: "jpeg-image")
            case .blueDot:
                return .none
            }
        }

        var usesDefaultShadowImage: Bool {
            self == .blueDot
        }

        mutating func toggle() {
            var idx = Self.allCases.firstIndex(of: self)! + 1
            if idx == Self.allCases.count {
                idx = 0
            }
            self = Self.allCases[idx]
        }
    }

    private enum PuckBearingVisibility {
        case isVisible
        case isHidden

        var isVisible: Bool {
            switch self {
            case .isVisible:
                return true
            case .isHidden:
                return false
            }
        }

        mutating func toggle() {
            self = self == .isVisible ? .isHidden : .isVisible
        }
    }

    private enum PuckAccuracyRingVisibility {
        case isVisible
        case isHidden

        var isVisible: Bool {
            switch self {
            case .isVisible:
                return true
            case .isHidden:
                return false
            }
        }

        mutating func toggle() {
            self = self == .isVisible ? .isHidden : .isVisible
        }
    }

    private enum Style {
        case light
        case dark

        var lightPreset: String {
            switch self {
            case .light:
                "day"
            case .dark:
                "night"
            }
        }

        mutating func toggle() {
            self = self == .light ? .dark : .light
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let cameraOptions = CameraOptions(center: CLLocationCoordinate2D(latitude: 37.26301831966747, longitude: -121.97647612483807), zoom: 6)
        let mapInitOptions = MapInitOptions(cameraOptions: cameraOptions)
        mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)
        mapView.mapboxMap.mapStyle = .standard(theme: .monochrome)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(mapView)

        addCustomizePuckButton()

        // Granularly configure the location puck with a `Puck2DConfiguration`
        puckConfiguration.layerPosition = .default
        mapView.location.options.puckType = .puck2D(puckConfiguration)
        mapView.location.options.puckBearing = .heading
        mapView.location.options.puckBearingEnabled = true

        // Center map over the user's current location
        mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
            guard let self = self else { return }

            if let currentLocation = self.mapView.location.latestLocation {
                let cameraOptions = CameraOptions(center: currentLocation.coordinate, zoom: 16.0)
                self.mapView.camera.ease(to: cameraOptions, duration: 2.0)
            }
        }.store(in: &cancelables)
    }

    

    private func addCustomizePuckButton() {
        // Set up button to change the puck options
        let button = UIButton(type: .system)
        button.setTitle("Customize puck", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = #colorLiteral(red: 0, green: 0.4784313725, blue: 0.9882352941, alpha: 1)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.layer.cornerRadius = 20
        button.addTarget(self, action: #selector(changePuckOptions(sender:)), for: .touchUpInside)
        view.addSubview(button)

        // Set button location
        NSLayoutConstraint.activate([
            button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -24),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.widthAnchor.constraint(equalToConstant: 200),
            button.heightAnchor.constraint(equalToConstant: 40)
        ])
    }

    @objc func changePuckOptions(sender: UIButton) {
        let alert = UIAlertController(title: "Toggle Puck Options",
                                      message: "Select an options to toggle.",
                                      preferredStyle: .actionSheet)
        alert.popoverPresentationController?.sourceView = sender

        alert.addAction(UIAlertAction(title: "Toggle Puck visibility", style: .default) { _ in
            self.showsPuck.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle Puck opacity", style: .default) { _ in
            self.puckOpacity.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle Puck image", style: .default) { _ in
            self.puckImage.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle bearing visibility", style: .default) { _ in
            self.showsBearing.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle accuracy ring", style: .default) { _ in
            self.showsAccuracyRing.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle bearing source", style: .default) { _ in
            self.puckBearing.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle Map Style", style: .default) { _ in
            self.style.toggle()
        })

        alert.addAction(UIAlertAction(title: "Toggle Projection", style: .default) { _ in
            self.projection.toggle()
        })

        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))

        present(alert, animated: true)
    }

    func updatePuckUI() {
        puckConfiguration = Puck2DConfiguration.makeDefault(showBearing: showsBearing.isVisible)
        puckConfiguration.showsAccuracyRing = showsAccuracyRing.isVisible
        puckConfiguration.topImage = puckImage.image
        puckConfiguration.layerPosition = .default
        if !puckImage.usesDefaultShadowImage {
            puckConfiguration.shadowImage = nil
        }
        puckConfiguration.opacity = puckOpacity.rawValue
        switch showsPuck {
        case .isVisible:
            mapView.location.options.puckType = .puck2D(puckConfiguration)
        default:
            mapView.location.options.puckType = .none
        }
    }

    func updateProjection() {
        do {
            try mapView.mapboxMap.setProjection(StyleProjection(name: projection))
        } catch {
            print(error)
        }
    }
}

extension PuckBearing {
    mutating func toggle() {
        self = self == .heading ? .course : .heading
    }
}

extension StyleProjectionName {
    mutating func toggle() {
        self = self == .mercator ? .globe : .mercator
    }
}
```