Skip to content

Commit

Permalink
fix: Updates to support changes to GMSMapViewOptions during view cycle
Browse files Browse the repository at this point in the history
  • Loading branch information
waynewbishop committed Dec 7, 2024
1 parent 1951ed8 commit 7655772
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MapExamplesViewModel: ObservableObject {
@Published var examples: [MapExample] = [
MapExample(
title: "Basic map",
description: "A simple map. Shows how to configure GMSMapViewOptions as a map binding.",
description: "A simple map. Shows how to configure GMSMapViewOptions as a view modifier.",
destination: AnyView(BasicMap())
),
MapExample(
Expand Down Expand Up @@ -48,8 +48,8 @@ class MapExamplesViewModel: ObservableObject {
destination: AnyView(MapWithContainer())
),
MapExample(
title: "Map with Delegate",
description: "A GoogleMapView configured with a handler for map tap events.",
title: "Map with Delegates",
description: "A GoogleMapView configured with handlers for map and marker tap events.",
destination: AnyView(MapWithDelegate())
)
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ struct GoogleMapView: UIViewRepresentable {
private static let mapDelegate = GoogleMapViewDelegate()


init(options: Binding<GMSMapViewOptions>,
markers: [GMSMarker] = [],
mapType: GMSMapViewType = .normal) {
self._options = options
self.markers = markers
self.mapType = mapType
}
init(options: Binding<GMSMapViewOptions> = .constant(GMSMapViewOptions()),
markers: [GMSMarker] = [],
mapType: GMSMapViewType = .normal) {
self._options = options
self.markers = markers
self.mapType = mapType
}

/// Creates the underlying UIKit map view
func makeUIView(context: Context) -> GMSMapView {
Expand All @@ -46,29 +46,50 @@ struct GoogleMapView: UIViewRepresentable {

// Set shared delegate to handle interactions
mapView.delegate = Self.mapDelegate

// Add any markers to the map
markers.forEach { marker in
marker.map = mapView
}

return mapView
}

/// Updates the map view when SwiftUI state changes
func updateUIView(_ uiView: GMSMapView, context: Context) {
uiView.mapType = mapType // Update map type if changed

// Update camera if it exists and has changed
if let newCamera = options.camera, uiView.camera != newCamera {
uiView.camera = newCamera
}

// Update background color if it exists and has changed
if let newBackgroundColor = options.backgroundColor, uiView.backgroundColor != newBackgroundColor {
uiView.backgroundColor = newBackgroundColor
}

// refresh markers to the map
markers.forEach { marker in
marker.map = uiView
}

uiView.mapType = mapType // Update map type if changed
}
}

// MARK: - viewModifiers and callbacks

extension GoogleMapView {
/// Updates map options
/// - Parameter options: New GMSMapViewOptions to apply
/// - Returns: New GoogleMapView instance with updated options
func mapOptions(_ options: GMSMapViewOptions) -> GoogleMapView {
GoogleMapView(options: .constant(options), markers: markers, mapType: mapType)
}


/// Adds markers to the map
/// - Parameter markers: Array of GMSMarker objects to display
/// - Returns: New GoogleMapView instance with updated markers
func mapMarkers(_ markers: [GMSMarker]) -> GoogleMapView {
GoogleMapView(options: _options, markers: markers, mapType: mapType)
}


/// Changes the map display type
/// - Parameter type: GMSMapViewType to use (.normal, .satellite, etc)
Expand Down
39 changes: 22 additions & 17 deletions GoogleMaps-SwiftUI/GoogleMaps-SwiftUI/Samples/BasicMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,31 @@ import SwiftUI
import GoogleMaps

struct BasicMap: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered at Google HQ
options.camera = .camera(.googleplex)

// Or with custom zoom level for closer view
// options.camera = .camera(.sanFrancisco, zoom: 15)
return options
}()

var body: some View {
/*
The $ prefix creates a two-way binding to mapOptions. This means:
1. GoogleMapView can read the current mapOptions
2. GoogleMapView can update mapOptions if the map state changes
3. Changes to mapOptions in BasicMap will update the map
4. Changes to the map will update mapOptions in BasicMap
*/
GoogleMapView(options: $mapOptions)
.ignoresSafeAreaExceptTop() //optional property for samples display
}
var body: some View {
/*
GoogleMapView can be configured using view modifier:
GoogleMapView()
.mapOptions(newOptions)
- Preferred method for updating map options at initialization or during the view lifecycle
- Can be chained with other modifiers like mapMarkers() and mapType()
*/
VStack {
GoogleMapView()
.mapOptions(defaultOptions)
.ignoresSafeAreaExceptTop()

Button("Fly to New York") {
let newOptions = GMSMapViewOptions()
newOptions.camera = .camera(.newYork)
defaultOptions = newOptions
}
.padding()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import GoogleMaps

struct MapWithContainer: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .camera(.seattle)
Expand All @@ -28,7 +28,8 @@ struct MapWithContainer: View {

var body: some View {
VStack(spacing: 16) {
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
.ignoresSafeAreaExceptTop() //optional property for samples display
.frame(maxWidth: .infinity, minHeight: 325)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import GoogleMaps
struct MapWithDelegate: View {

@State var response: String = ""
@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .camera(.sanFrancisco)
Expand All @@ -38,7 +38,8 @@ struct MapWithDelegate: View {
var body: some View {

VStack(spacing: 16) {
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
.mapMarkers(multipleMarkers)
.onMarkerTapped { marker in
response = "Marker tapped at: \(marker.position)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import GoogleMaps

struct MapWithMarker: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .camera(.sanFrancisco)
Expand All @@ -32,7 +32,8 @@ struct MapWithMarker: View {
]

var body: some View {
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
//Adds one or more markers to be displayed on the map
.mapMarkers(singleMarker)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import GoogleMaps

struct MapWithMarkers: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .camera(.sanFrancisco)
Expand All @@ -35,7 +35,8 @@ struct MapWithMarkers: View {
]

var body: some View {
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
//Adds one or more markers to be displayed on the map
.mapMarkers(multipleMarkers)
.ignoresSafeAreaExceptTop() //optional property for samples display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import GoogleMaps

struct MapWithStreetLevel: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .streetLevel(.sanFrancisco, bearing: 45)
Expand All @@ -35,7 +35,8 @@ struct MapWithStreetLevel: View {
var body: some View {

//Map camera position set to street-level 3D perspective.
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
.ignoresSafeAreaExceptTop() //optional property for samples display
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import GoogleMaps

struct MapWithTypes: View {

@State private var mapOptions: GMSMapViewOptions = {
@State private var defaultOptions: GMSMapViewOptions = {
var options = GMSMapViewOptions()
// Initialize map centered on San Francisco
options.camera = .camera(.sanFrancisco)
Expand All @@ -33,7 +33,8 @@ struct MapWithTypes: View {
// .satellite - Satellite imagery without street labels or overlays
// .terrain - Topographic data showing elevation, vegetation, and natural features
// .hybrid - Satellite imagery combined with road overlays and place labels
GoogleMapView(options: $mapOptions)
GoogleMapView()
.mapOptions(defaultOptions)
.mapType(.satellite)
.ignoresSafeAreaExceptTop() //optional property for samples display
}
Expand Down

0 comments on commit 7655772

Please sign in to comment.