Skip to main content

SwiftUI - Weather annotations

This example shows how to display weather data as annotations on a map using the Mapbox Maps SDK for iOS. The weather data includes point features with properties such as temperature, and icon name for each. The annotations are interactive, and upon selection, the view will adjust the viewport to focus on the selected annotation and animate the annotation to be larger and include additional information.

The example includes a SwiftUI view named WeatherIconView, which is a component to render individual weather icons. Users can tap on an icon to select it, updating the selection state and triggering an animation. The weather icons are displayed within circles, with varying sizes based on the selection status. This setup allows for an engaging visualization of weather data on the map.

iOS Examples App Available

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.

WeatherAnnotationExample.swift
import SwiftUI
import MapboxMaps

private struct WeatherData: Identifiable, Equatable {
let id = UUID().uuidString
let coordinate: CLLocationCoordinate2D
let temperature: Measurement<UnitTemperature>
let iconName: String
}

@available(iOS 14.0, *)
struct WeatherAnnotationExample: View {
@State private var viewport: Viewport = .camera(center: .berlin, zoom: 1.5)

@State private var selectedData: WeatherData?
private var weatherData: [WeatherData] = [
WeatherData(coordinate: .helsinki, temperature: Measurement(value: 25, unit: .celsius), iconName: "sun.min.fill"),
WeatherData(coordinate: .london, temperature: Measurement(value: 20, unit: .celsius), iconName: "cloud.drizzle.fill"),
WeatherData(coordinate: .berlin, temperature: Measurement(value: 30, unit: .celsius), iconName: "cloud.bolt.rain.fill")
]

var body: some View {
Map(viewport: $viewport) {
ForEvery(weatherData) { data in
MapViewAnnotation(coordinate: data.coordinate) {
WeatherIconView(data: data, selectedData: $selectedData)
}
}
}
.onChange(of: selectedData?.coordinate) { center in
guard let center else { return }

withViewportAnimation(.default(maxDuration: 0.5)) {
viewport = .camera(center: center, zoom: 2.5)
}
}
.ignoresSafeArea()
}
}

@available(iOS 14.0, *)
private struct WeatherIconView: View {
var data: WeatherData
@Binding var selectedData: WeatherData?
@State private var isSelected = false

private let formatter: MeasurementFormatter = {
let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit
return formatter
}()

var body: some View {
ZStack {
Circle()
.stroke(.white, lineWidth: 3)
.overlay(Circle().fill(.blue))
.frame(width: isSelected ? 50 : 35, height: isSelected ? 50 : 35)
VStack {
if isSelected {
Text(formatter.string(from: data.temperature))
.font(.caption2)
}
Image(systemName: data.iconName)
}
}
.foregroundColor(.white)
.onTapGesture {
selectedData = data
}
.onChange(of: selectedData) { selectedData in
withAnimation(.interactiveSpring()) {
isSelected = selectedData == data
}
}
}
}
@available(iOS 14.0, *)
struct WeatherAnnotationExample_Preview: PreviewProvider {

static var previews: some View {
WeatherAnnotationExample()
}
}
Was this example helpful?