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

# Frame a list of view annotations

![Animates to a camera which frames the list of selected view annotations.](https://docs.mapbox.com/ios/ja/assets/ideal-img/maps-examples-frame-list.f4c09a9.480.png)

This example demonstrates how to frame a [`ViewAnnotation`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/viewannotation/) dynamically on a map within the **Mapbox Maps SDK for iOS**. The example includes functionalities for animating the camera to focus on different annotations using the [`fly()`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/cameraanimationsmanager/fly(to:duration:curve:completion:)/), [`ease()`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/cameraanimationsmanager/ease(to:duration:curve:completion:)/), and [`viewport`](https://docs.mapbox.com/ios/maps/api/latest/documentation/mapboxmaps/mapview/viewport/) options. Users can interact with the buttons provided to trigger the corresponding camera animations or reset the camera to its initial position. Map annotations are added with customized views, each representing a location with its name and are positioned based on specified anchors.

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

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

```swift
import UIKit
import MapboxMaps

final class ViewController: UIViewController {
    private enum Animator {
        case flyTo, easeTo, viewport
    }

    private var cancelables = Set<AnyCancelable>()
    private var flyToButton: UIButton!
    private var easeToButton: UIButton!
    private var viewportButton: UIButton!
    private var resetButton: UIButton!
    private var annotations = [ViewAnnotation]()

    private var mapView: MapView!
    private let initialCamera = CameraOptions(
        center: .random,
        padding: UIEdgeInsets(top: .random(in: 0...20), left: .random(in: 0...20), bottom: .random(in: 0...20), right: .random(in: 0...20)),
        zoom: 0,
        bearing: 0,
        pitch: 0
    )

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white

        mapView = MapView(frame: view.bounds, mapInitOptions: MapInitOptions(cameraOptions: initialCamera))
        // Camera
        try! mapView.mapboxMap.setProjection(StyleProjection(name: .mercator))
        let buttonsView = makeButtonsView()

        view.addSubview(mapView)
        view.addSubview(buttonsView)

        mapView.translatesAutoresizingMaskIntoConstraints = false
        buttonsView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            buttonsView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            buttonsView.leadingAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.leadingAnchor),
            buttonsView.bottomAnchor.constraint(equalTo: mapView.ornaments.logoView.topAnchor, constant: -10),
        ])

        addAnnotations()

        mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
            
            
        }.store(in: &cancelables)
    }

    private func makeButtonsView() -> UIView {
        func makeButton(title: String, selector: Selector) -> UIButton {
            let button = UIButton()
            button.setTitle(title, for: .normal)
            button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
            button.backgroundColor = .black
            button.addTarget(self, action: selector, for: .touchUpInside)
            return button
        }

        flyToButton = makeButton(title: "FlyTo", selector: #selector(flyToButtonTapped(_:)))
        easeToButton = makeButton(title: "EaseTo", selector: #selector(easeToButtonTapped(_:)))
        viewportButton = makeButton(title: "Viewport", selector: #selector(viewportButtonTapped(_:)))
        resetButton = makeButton(title: "Reset camera", selector: #selector(resetButtonTapped(_:)))

        let buttonsView = UIStackView(arrangedSubviews: [flyToButton, easeToButton, viewportButton, resetButton])
        buttonsView.axis = .horizontal
        buttonsView.spacing = 10
        buttonsView.distribution = .fillEqually

        resetButton.isHidden = true

        return buttonsView
    }

    @objc private func flyToButtonTapped(_ sender: UIButton) {
        frameViewAnnotation(with: .flyTo, sender: sender)
    }

    @objc private func easeToButtonTapped(_ sender: UIButton) {
        frameViewAnnotation(with: .easeTo, sender: sender)
    }

    @objc private func viewportButtonTapped(_ sender: UIButton) {
        frameViewAnnotation(with: .viewport, sender: sender)
    }

    @objc private func resetButtonTapped(_ sender: UIButton) {
        mapView.viewport.idle()
        mapView.mapboxMap.setCamera(to: initialCamera)
        resetButton.isHidden = true
        flyToButton.isHidden = false
        easeToButton.isHidden = false
        viewportButton.isHidden = false
    }

    private func frameViewAnnotation(with animator: Animator, sender: UIButton) {
        flyToButton.isHidden = true
        easeToButton.isHidden = true
        viewportButton.isHidden = true
        resetButton.isHidden = false

        let camera = self.mapView.viewAnnotations.camera(
            forAnnotations: annotations,
            padding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10),
            bearing: nil,
            pitch: nil
        )!

        switch animator {
        case .flyTo:
            mapView.camera.fly(to: camera, duration: 1)
        case .easeTo:
            mapView.camera.ease(to: camera, duration: 1)
        case .viewport:
            let bounds = mapView.mapboxMap.coordinateBounds(for: camera)
            let overviewViewportStateOptions = OverviewViewportStateOptions(
                geometry: MultiPoint([bounds.northeast, bounds.southeast, bounds.southwest, bounds.northwest]),
                bearing: camera.bearing,
                pitch: camera.pitch,
                animationDuration: 1
            )
            let overviewViewportState = mapView.viewport.makeOverviewViewportState(options: overviewViewportStateOptions)
            mapView.viewport.transition(to: overviewViewportState)
        }
    }

    private func addAnnotations() {
        func makeView(text: String) -> UIView {
            let shadowRadius: CGFloat = 8
            let shadowOffset = CGSize(width: 0, height: 2)
            // Shadow blur extends beyond shadowRadius. Use ~2x multiplier for smooth fade.
            let shadowPadding = shadowRadius * 2 + max(abs(shadowOffset.width), abs(shadowOffset.height))
            let labelPadding: CGFloat = 4

            let container = UIView()

            let card = UIView()
            card.backgroundColor = .white
            card.layer.shadowOpacity = 0.25
            card.layer.shadowRadius = shadowRadius
            card.layer.shadowOffset = shadowOffset
            card.layer.cornerRadius = 8
            card.translatesAutoresizingMaskIntoConstraints = false

            let label = UILabel()
            label.text = text
            label.textColor = .black
            label.translatesAutoresizingMaskIntoConstraints = false

            card.addSubview(label)
            container.addSubview(card)

            NSLayoutConstraint.activate([
                label.leadingAnchor.constraint(equalTo: card.leadingAnchor, constant: labelPadding),
                label.trailingAnchor.constraint(equalTo: card.trailingAnchor, constant: -labelPadding),
                label.topAnchor.constraint(equalTo: card.topAnchor, constant: labelPadding),
                label.bottomAnchor.constraint(equalTo: card.bottomAnchor, constant: -labelPadding),

                card.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: shadowPadding),
                card.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -shadowPadding),
                card.topAnchor.constraint(equalTo: container.topAnchor, constant: shadowPadding),
                card.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -shadowPadding),
            ])

            return container
        }

        self.annotations = annotationData.map {
            let view = makeView(text: $0.name)
            let annotation = ViewAnnotation(coordinate: $0.coordinate, view: view)
            annotation.variableAnchors = [.init(anchor: $0.anchor)]
            self.mapView.viewAnnotations.add(annotation)
            return annotation
        }
    }

    private struct AnnotationInfo {
        var name: String
        var coordinate: CLLocationCoordinate2D
        var anchor: ViewAnnotationAnchor
    }

    private let annotationData: [AnnotationInfo] = [
        AnnotationInfo(name: "Saigon", coordinate: .init(latitude: 10.823099, longitude: 106.629662),
            anchor: .top),
        AnnotationInfo(name: "Hanoi", coordinate: .init(latitude: 21.027763, longitude: 105.834160),
            anchor: .bottomLeft),
        AnnotationInfo(name: "Tokyo", coordinate: .init(latitude: 35.689487, longitude: 139.691711),
            anchor: .right),
        AnnotationInfo(name: "Bangkok", coordinate: .init(latitude: 13.756331, longitude: 100.501762),
            anchor: .topRight),
        AnnotationInfo(name: "Jakarta", coordinate: .init(latitude: -6.175110, longitude: 106.865036),
            anchor: .topLeft)
    ]
}

private func annotation(geometry: GeometryConvertible, width: CGFloat, height: CGFloat, anchor: ViewAnnotationAnchor) -> ViewAnnotationOptions {
    .init(
        annotatedFeature: .geometry(geometry),
        width: width,
        height: height,
        allowOverlap: true,
        variableAnchors: [ViewAnnotationAnchorConfig(anchor: anchor)])
}
```