Resizable image
This example demonstrates how to add a resizable image to a style using the Mapbox Maps SDK for iOS and how the image can be stretched to encapsulate a text label.
The example adds a resizable UIImage
to the Map using the addImage
method. A SymbolLayer
is then created with an iconImage
property which references the id of the added image.
The symbol layer then sets textField
, iconTextFit
, iconTextFitPadding
properties to show the how the image is resized.
This example code is part of the Maps SDK for iOS Examples App, a working iOS project available on Github. 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 tutorial for step-by-step instructions.
import Foundation
import UIKit
import MapboxMaps
/// An example showcasing of adding a resizable image to the style
/// and demonstrating how the image is stretched
final class ViewController: UIViewController {
private static let center = CLLocationCoordinate2DMake(55.70651, 12.554729)
private static let layerId = "layer_id"
private static let textBase = "Hi! "
private lazy var mapView: MapView = {
let mapInitOptions = MapInitOptions(cameraOptions: CameraOptions(center: Self.center, zoom: 9))
let mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return mapView
}()
private var cancelables = Set<AnyCancelable>()
private var appendTextCounter = 1
private weak var timer: Timer? {
didSet { oldValue?.invalidate() }
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mapView)
mapView.mapboxMap.onMapLoaded.observeNext { [weak self] _ in
self?.setupExample()
self?.startUpdatingIconText()
}.store(in: &cancelables)
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
if parent == nil {
timer = nil
}
}
// MARK: - Private
private func setupExample() {
let geoJSONSourceId = "source_id"
// create an image of a circle and specify the corners that should remain unchanged
let image = UIImage(named: "circle")!
.resizableImage(withCapInsets: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12))
try? mapView.mapboxMap.addImage(image, id: "circle")
// add a GeoJSON source with a single point to the style
var source = GeoJSONSource(id: geoJSONSourceId)
source.data = .feature(Feature(geometry: Point(Self.center)))
try? mapView.mapboxMap.addSource(source)
// add a symbol layer with the resizable icon image
var symbolLayer = SymbolLayer(id: Self.layerId, source: geoJSONSourceId)
symbolLayer.iconImage = .constant(.name("circle"))
// make sure the icon image is stretched both vertically and horizontally
symbolLayer.iconTextFit = .constant(.both)
symbolLayer.iconTextFitPadding = .constant([10, 10, 10, 10])
symbolLayer.textField = .constant(Self.textBase)
try? mapView.mapboxMap.addLayer(symbolLayer, layerPosition: .default)
}
private func startUpdatingIconText() {
timer = Timer.scheduledTimer(withTimeInterval: 1.5, repeats: true) { [weak self] _ in
self?.updateIconText()
}
}
// Append some text to the layer's textField, stretching the icon image in both X and Y axes
private func updateIconText() {
guard mapView.mapboxMap.isStyleLoaded else {
return
}
let layer = try? mapView.mapboxMap.layer(withId: Self.layerId, type: SymbolLayer.self)
guard case .expression(let expression) = layer?.textField else {
return
}
appendTextCounter += 1
guard let textLabel = expression.arguments.first?.description
.appending(Self.textBase)
.appending(appendTextCounter % 3 == 0 ? "\n" : "") else {
return
}
try? mapView.mapboxMap.updateLayer(withId: Self.layerId, type: SymbolLayer.self) { layer in
layer.textField = .constant(textLabel)
}
}
}