Indoor mapping
The Mapbox Maps SDK for iOS provides indoor mapping support, allowing your application to display detailed floor plans inside buildings and let users switch between floors. Indoor mapping is part of the Mapbox Standard style and is controlled via a style configuration property.
Indoor mapping is an experimental feature. APIs may change in future releases. You must import with @_spi(Experimental) import MapboxMaps.
Enable indoor mapping
Indoor mapping is included in the Mapbox Standard style, but the feature is disabled by default. To enable it, set the showIndoor configuration property on the basemap style import to true:
try? mapView.mapboxMap.setStyleImportConfigProperty(
for: "basemap",
config: "showIndoor",
value: true
)
or using predefined MapStyle type wrapper that allows to set configuration values directly:
private var mapStyle: MapStyle {
.standard(
lightPreset: lightPreset,
showPointOfInterestLabels: labelsSetting,
showIndoor: true
)
}
Once enabled, buildings that have indoor map data will render their indoor floor plans at appropriate zoom levels.
Indoor selector ornament
The indoor selector ornament provides a floor selection UI that lets users browse and switch between floors in an indoor-mapped building.
Setup
To show the indoor selector, set its visibility on the MapView ornaments:
@_spi(Experimental) import MapboxMaps
// Show the indoor floor selector
mapView.ornaments.options.indoorSelector.visibility = .visible
// Optionally adjust margins to avoid overlapping with other controls
mapView.ornaments.options.indoorSelector.margins = CGPoint(x: 8.0, y: 56.0)
When indoor data becomes available (e.g., the user pans to a building with indoor mapping), the selector automatically populates with available floors and lets the user tap to switch floors.
Observing indoor state
Use the indoor property on MapboxMap to subscribe to changes in the indoor state. The onIndoorUpdated signal fires whenever the set of available floors changes or when a floor is selected:
The programmatic indoor API is experimental and subject to change. In future releases, this API may be extended to allow full customization of the indoor control, including building custom floor selector UI.
@_spi(Experimental) import MapboxMaps
private var cancellables = Set<AnyCancelable>()
mapView.mapboxMap.indoor.onIndoorUpdated.sink { indoorState in
print("Floors: \(indoorState.floors.map { $0.id })")
print("Selected floor id: \(indoorState.selectedFloorId ?? "none")")
}.store(in: &cancellables)
This is useful for building custom floor selection user interfaces or responding to floor changes in your application logic.
Disable indoor mapping
To completely disable indoor mapping after it was enabled, you should both turn off the style configuration and hide the indoor selector ornament.
Disable style configuration
Set the showIndoor configuration property to false:
try? mapView.mapboxMap.setStyleImportConfigProperty(
for: "basemap",
config: "showIndoor",
value: false
)
Hide indoor selector ornament
Set the visibility of the indoor selector to .hidden:
mapView.ornaments.options.indoorSelector.visibility = .hidden
Complete example
Here's a complete example showing how to set up indoor mapping with the floor selector in a UIKit view controller:
import UIKit
@_spi(Experimental) import MapboxMaps
final class IndoorViewController: UIViewController {
private var mapView: MapView!
private var cancellables = Set<AnyCancelable>()
override func viewDidLoad() {
super.viewDidLoad()
let cameraOptions = CameraOptions(
center: CLLocationCoordinate2D(latitude: 35.5483, longitude: 139.7780),
zoom: 16,
bearing: 12,
pitch: 60
)
let options = MapInitOptions(cameraOptions: cameraOptions)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
// Enable indoor in the Standard style
try? mapView.mapboxMap.setStyleImportConfigProperty(
for: "basemap",
config: "showIndoor",
value: true
)
// Show the indoor floor selector ornament
mapView.ornaments.options.indoorSelector.visibility = .visible
mapView.ornaments.options.indoorSelector.margins = CGPoint(x: 8.0, y: 56.0)
// Show scale bar
mapView.ornaments.options.scaleBar.visibility = .visible
// Observe indoor state changes
mapView.mapboxMap.indoor.onIndoorUpdated.sink { indoorState in
print("Selected floor id: \(indoorState.selectedFloorId ?? "none")")
}.store(in: &cancellables)
}
}
SwiftUI
If you are using SwiftUI, you can configure indoor mapping using the MapReader and style configuration modifiers:
import SwiftUI
@_spi(Experimental) import MapboxMaps
struct IndoorMapView: View {
var body: some View {
Map(initialViewport: .camera(
center: CLLocationCoordinate2D(latitude: 35.5483, longitude: 139.7780),
zoom: 16,
bearing: 12,
pitch: 60
))
.mapStyle(.standard)
.ornamentOptions(OrnamentOptions(
indoorSelector: OrnamentVisibilityOptions(visibility: .visible)
))
.onStyleLoaded { _ in
// Enable indoor mapping via style config
}
}
}