Search code examples
swiftswiftuigoogle-maps-sdk-iosgmsmapview

Google Maps iOS SDK animate map padding SwiftUI


I'm looking to animate my Google Map's padding, as when a bottom sheet appears, the map padding resizes dynamically.

The GMSMapView.padding documentation says it can be accomplished with a UIView block based animation, but I'm not sure how that would tie in to a SwiftUI application?

The bottom sheet works fine, it shows when expected and passes the correct padding value through to the MapView. It's just the animation I can't figure out. (The bottom sheet uses this component if you're curious https://github.com/LucasMucGH/BottomSheet)

Any help would be greatly appreciated, thanks.

Current relevant code:

struct MapView: UIViewRepresentable {
  
  var bottomPadding: CGFloat
  private let mapView = GMSMapView(frame: .zero)
  
  func makeUIView(context: Context) -> GMSMapView {
    return mapView
  }

  func updateUIView(_ mapView: GMSMapView, context: Context) {
    mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: bottomPadding, right: 0)
  }
}

struct ContentView: View {
  
  @State var showBottomSheet: ShowBottomSheet = .hidden
  
  var bottomSheetHeight: CGFloat {
    if showBottomSheet == .visible {
      return showBottomSheet.rawValue
    } else {
      return 0
    }
  }
  
  var body: some View {
        
    GeometryReader { geometry in
      ZStack {
        
        // Map
        MapView(bottomPadding: geometry.size.height * bottomSheetHeight)
        .edgesIgnoringSafeArea(.all)
        .bottomSheet(
          bottomSheetPosition: $showBottomSheet,
          options: [
            .allowContentDrag,
            .swipeToDismiss,
            .noBottomPosition,
            .shadow()
          ],
          content: {}
        )
      }
    }
  }
}

enum ShowBottomSheet: CGFloat, CaseIterable {
    case hidden = 0, visible = 0.5
  }

Solution

  • If anyone's interested I worked out how to do it. The solution was simpler than I expected, it's literally just wrapping the padding parameter in a UIView.animate closure, then calling it in the updateUIViewController method:

    private func setMapPadding(mapView: GMSMapView) {
        UIView.animate(withDuration: 0.5, delay: 0.18, usingSpringWithDamping: 0.75, initialSpringVelocity: 0) {
          mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: bottomPadding, right: 0)
        }
      }
    
    func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
        animateCameraToMarker(mapView: uiViewController.mapView)
      }