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

# Add a view annotation attached to a symbol layer

![Add view annotations anchored to a symbol layer feature.](https://docs.mapbox.com/ios/ja/assets/ideal-img/maps-examples-view-annotations-advanced.dd44d56.480.png)

This example demonstrates the integration of view annotations with a map created using the **Mapbox Maps SDK for iOS** using the [`MapView`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapview/) class in conjunction with [`GeoJSONSource`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/geojsonsource/), [`RasterDemSource`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/rasterdemsource/), and [`SymbolLayer`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/symbollayer/) from the Mapbox Maps SDK. The example sets up a map view and allows users to add markers to a custom GeoJSON source and view annotations at specified locations. Annotations can be toggled and customized with title and anchor configurations. The style of the map is changed using the [`styleURI`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapinitoptions/styleuri/) property of the map. Users can change the map style between two of the classic [`Mapbox Styles`](https://docs.mapbox.com/api/maps/styles/#classic-mapbox-styles), [`Streets`](https://www.mapbox.com/maps/streets) and [`Satellite Streets`](https://www.mapbox.com/maps/satellite) by tapping the "Change style" button.

> **Note (legacy): Classic styles are no longer maintained**
> 
> This example uses classic Mapbox styles (for example: `MAPBOX_STREETS`,`SATELLITE`, `OUTDOORS`, etc). These styles are no longer maintained and may not include the latest features or updates. Developers are encouraged to use the [Mapbox Standard](https://docs.mapbox.com/map-styles/standard/guides) or Mapbox Standard Satellite styles]([https://docs.mapbox.com/map-styles/standard/guides#mapbox-standard-satellite](https://docs.mapbox.com/map-styles/standard/guides#mapbox-standard-satellite)) or to build a custom style using [Mapbox Studio](https://docs.mapbox.com/help/tutorials/aa-standard-in-studio/).

The example utilizes various functionalities including handling marker taps, managing map style preparation, adding markers with view annotations, and responding to user interactions.

> **Note (warning): Add AnnotationView class to your project**
> 
> This example uses [`AnnotationView`](https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/Examples/All%20Examples/Annotations/AnnotationView.swift/). To use this code snippet, you must also add the [AnnotationView class](https://github.com/mapbox/mapbox-maps-ios/blob/main/Sources/Examples/All%20Examples/Annotations/AnnotationView.swift/), a custom class defined in the Maps SDK for the [iOS Examples App](https://github.com/mapbox/mapbox-maps-ios/tree/main/Sources/Examples/).

> **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: `ViewAnnotationMarkerExample.swift`

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

```swift
import UIKit
import MapboxMaps
import CoreLocation

final class ViewController: UIViewController {
    private var mapView: MapView!
    private var pointList: [Feature] = []
    private var markerId = 0
    private var annotations = [String: ViewAnnotation]()
    private var _topPriority = 0
    private var topPriority: Int {
        _topPriority += 1
        return _topPriority
    }

    private let image = UIImage(named: "intermediate-pin")!
    private lazy var markerHeight: CGFloat = image.size.height
    private var cancelables = Set<AnyCancelable>()

    lazy var styleChangeButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = .systemTeal
        button.layer.cornerRadius = 8
        button.clipsToBounds = true
        button.setTitle("Change style", for: .normal)
        button.addTarget(self, action: #selector(styleChangePressed(sender:)), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let centerCoordinate = CLLocationCoordinate2D(latitude: 39.7128, longitude: -75.0060)
        let options = MapInitOptions(cameraOptions: CameraOptions(center: centerCoordinate, zoom: 7))

        mapView = MapView(frame: view.bounds, mapInitOptions: options)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(mapView)

        mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
            guard let self = self else { return }
            
        }.store(in: &cancelables)

        mapView.mapboxMap.onStyleLoaded.observe { [weak self, weak mapView] _ in
            guard let self, let mapView else { return }
            self.prepareStyle()
            self.addMarker(at: mapView.mapboxMap.coordinate(for: mapView.center), viewAnnotation: true)
        }.store(in: &cancelables)

        mapView.mapboxMap.addInteraction(LongPressInteraction { [weak self] context in
            self?.addMarker(at: context.coordinate)
            return false
        })

        mapView.mapboxMap.addInteraction(TapInteraction(.layer(Constants.LAYER_ID)) { [weak self] feature, _ in
            self?.handleMarkerTap(feature) ?? false
        })

        view.addSubview(styleChangeButton)

        NSLayoutConstraint.activate([
            styleChangeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -32),
            styleChangeButton.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor, constant: -16),
            styleChangeButton.widthAnchor.constraint(equalToConstant: 128)
        ])
    }

    private func handleMarkerTap(_ feature: FeaturesetFeature) -> Bool {
        guard let id = feature.id?.id else { return false }

        if let annotation = annotations[id] {
            annotation.priority = topPriority
            return true
        }
        return addViewAnnotation(id: id, geometry: feature.geometry)
    }

    @objc private func styleChangePressed(sender: UIButton) {
        mapView.mapboxMap.mapStyle = mapView.mapboxMap.mapStyle == .standard ? .standardSatellite : .standard
    }

    // MARK: - Style management

    private func prepareStyle() {
        try? mapView.mapboxMap.addImage(image, id: Constants.BLUE_ICON_ID)

        var source = GeoJSONSource(id: Constants.SOURCE_ID)
        source.data = .featureCollection(FeatureCollection(features: pointList))
        try? mapView.mapboxMap.addSource(source)

        if mapView.mapboxMap.mapStyle == .standardSatellite {
            var demSource = RasterDemSource(id: "terrain-source")
            demSource.url = Constants.TERRAIN_URL_TILE_RESOURCE
            try? mapView.mapboxMap.addSource(demSource)
            let terrain = Terrain(sourceId: demSource.id)
            try? mapView.mapboxMap.setTerrain(terrain)
        }

        var layer = SymbolLayer(id: Constants.LAYER_ID, source: Constants.SOURCE_ID)
        layer.iconImage = .constant(.name(Constants.BLUE_ICON_ID))
        layer.iconAnchor = .constant(.bottom)
        layer.iconOffset = .constant([0, 12])
        layer.iconAllowOverlap = .constant(true)
        try? mapView.mapboxMap.addLayer(layer)
    }

    // MARK: - Annotation management

    // Add a marker to a custom GeoJSON source:
    // This is an optional step to demonstrate the automatic alignment of view annotations
    // with features in a data source
    private func addMarker(at coordinate: CLLocationCoordinate2D, viewAnnotation: Bool = false) {
        let currentId = "\(Constants.MARKER_ID_PREFIX)\(markerId)"
        markerId += 1
        var feature = Feature(geometry: Point(coordinate))
        feature.identifier = .string(currentId)
        pointList.append(feature)
        if (try? mapView.mapboxMap.source(withId: Constants.SOURCE_ID)) != nil {
            mapView.mapboxMap.updateGeoJSONSource(withId: Constants.SOURCE_ID, geoJSON: .featureCollection(FeatureCollection(features: pointList)))
        }

        if viewAnnotation {
            addViewAnnotation(id: currentId, geometry: .point(Point(coordinate)))
        }
    }

    // Add a view annotation at a specified location and optionally bind it to an ID of a marker
    @discardableResult
    private func addViewAnnotation(id: String, geometry: Geometry) -> Bool {
        guard case let .point(point) = geometry else { return false }
        let annotationView = AnnotationView(frame: .zero)
        annotationView.title = String(format: "lat=%.2f\nlon=%.2f", point.coordinates.latitude, point.coordinates.longitude)

        let annotation = ViewAnnotation(
            annotatedFeature: .layerFeature(layerId: Constants.LAYER_ID, featureId: id),
            view: annotationView)
        annotation.variableAnchors = [ViewAnnotationAnchorConfig(anchor: .bottom, offsetY: markerHeight - 12)]
        mapView.viewAnnotations.add(annotation)

        annotationView.onClose = { [weak annotation, weak self] in
            annotation?.remove()
            self?.annotations.removeValue(forKey: id)
        }
        annotationView.onSelect = { [weak annotation, weak self] _ in
            guard let self else { return }
            annotation?.priority = self.topPriority
            annotation?.setNeedsUpdateSize()
        }

        annotations[id] = annotation
        return true
    }
}

extension ViewController {
    private enum Constants {
        static let BLUE_ICON_ID = "blue"
        static let SOURCE_ID = "source_id"
        static let LAYER_ID = "layer_id"
        static let TERRAIN_URL_TILE_RESOURCE = "mapbox://mapbox.mapbox-terrain-dem-v1"
        static let MARKER_ID_PREFIX = "view_annotation_"
        static let SELECTED_ADD_COEF_PX: CGFloat = 50
    }
}
```