Как сделать так, чтобы лист формы отображался после касания окна информации о маркере Google Maps в iOS?

В настоящее время я создаю приложение с использованием SwiftUI и Google Maps. Я пытаюсь отобразить лист формы после нажатия на информационное окно маркера Google Maps, но у меня возникают проблемы с его работой.

В других частях своего приложения я отображаю листы, используя этот метод: Пример метода здесь

Я попытался использовать тот же метод, описанный выше, для отображения листа после нажатия на информационное окно маркера, но у меня возникли проблемы с выполнением этого из функции. Мои фрагменты кода ниже содержат более подробную информацию.

-

Ниже представлена ​​урезанная версия моего файла GMView.swift, который управляет моим экземпляром Google Maps. (Мой файл отличается от типичной интеграции карт Swift + Google, потому что я использую SwiftUI). Вы заметите три основные части файла: 1. представление, 2. класс GMController и 3. структуру GMControllerRepresentable:

import SwiftUI
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Foundation



struct GoogMapView: View {
    var body: some View {
        GoogMapControllerRepresentable()
    }
}


class GoogMapController: UIViewController, CLLocationManagerDelegate, GMSMapViewDelegate {
    var locationManager = CLLocationManager()
    var mapView: GMSMapView!
    let defaultLocation = CLLocation(latitude: 42.361145, longitude: -71.057083)
    var zoomLevel: Float = 15.0
    let marker : GMSMarker = GMSMarker()


    override func viewDidLoad() {
        super.viewDidLoad()

//        Control location data
        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.distanceFilter = 50
            locationManager.startUpdatingLocation()
        }


        let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude, longitude: defaultLocation.coordinate.longitude, zoom: zoomLevel)
        mapView = GMSMapView.map(withFrame: view.bounds, camera: camera)
        mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        mapView.isMyLocationEnabled = true
        mapView.setMinZoom(14, maxZoom: 20)
        mapView.settings.compassButton = true
        mapView.isMyLocationEnabled = true
        mapView.settings.myLocationButton = true
        mapView.settings.scrollGestures = true
        mapView.settings.zoomGestures = true
        mapView.settings.rotateGestures = true
        mapView.settings.tiltGestures = true
        mapView.isIndoorEnabled = false


        marker.position = CLLocationCoordinate2D(latitude: 42.361145, longitude: -71.057083)
        marker.title = "Boston"
        marker.snippet = "USA"
        marker.map = mapView


//        view.addSubview(mapView)
        mapView.delegate = self
        self.view = mapView

    }

    // Handle incoming location events.
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
      let location: CLLocation = locations.last!
      print("Location: \(location)")
    }

    // Handle authorization for the location manager.
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
      switch status {
      case .restricted:
        print("Location access was restricted.")
      case .denied:
        print("User denied access to location.")
        // Display the map using the default location.
        mapView.isHidden = false
      case .notDetermined:
        print("Location status not determined.")
      case .authorizedAlways: fallthrough
      case .authorizedWhenInUse:
        print("Location status is OK.")
      }
    }

    // Handle location manager errors.
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
      locationManager.stopUpdatingLocation()
      print("Error: \(error)")
    }

}


struct GoogMapControllerRepresentable: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
        return GMController()
    }

    func updateUIViewController(_ uiViewController: GMController, context: UIViewControllerRepresentableContext<GMControllerRepresentable>) {

    }
}

Это функция, которую я добавил в класс GMController в моем вышеупомянутом файле GMView.swift, который в документации Google сказано использовать для обработки при касании информационного окна маркера:

// Function to handle when a marker's infowindow is tapped
    func mapView(_ mapView: GMSMapView, didTapInfoWindowOf didTapInfoWindowOfMarker: GMSMarker) {
        print("You tapped a marker's infowindow!")
//        This is where i need to get the view to appear as a modal, and my attempt below
        let venueD2 = UIHostingController(rootView: VenueDetail2())
        venueD2.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height - 48)
        self.view.addSubview(venueD2.view)
        return
    }

Моя функция выше в настоящее время отображает представление при нажатии на информационное окно, но оно просто появляется поверх моего представления карт Google, поэтому я не получаю анимацию и не могу отклонить представление, как типичный лист формы iOS.

Кто-нибудь знает, как я могу отобразить лист после касания информационного окна маркера карт Google в SwiftUI вместо того, чтобы просто добавить его в качестве подпредставления?


person zlyt    schedule 26.11.2019    source источник


Ответы (1)


Привет, чтобы взаимодействовать с UIViewController из структуры View, вам нужно привязать переменную .. сначала мы объявляем @Binding var isClicked : Bool, и если вам нужно передать больше параметров в структуру, вам нужно объявить ее с объявлением @Binding. в любом случае в UIViewController ошибка покажет, что isClicked Свойство 'self.isClicked' не инициализировано, чтобы исправить это, мы объявляем:

@Binding var isClicked
init(isClicked: Binding<Bool>) {
        _isClicked = isClicked
        super.init(nibName: nil, bundle: nil) 
    }

также Назначенный инициализатор для UIViewController - это initWithNibName: bundle :. Вместо этого ты должен позвонить так. Если у вас нет пера, передайте nil для nibName (bundle также необязателен). Теперь у нас есть все настройки для UIViewController, и мы переходим к UIViewControllerRepresentable: так же, как мы делали сначала, нам нужно объявить @Binding var isClicked, потому что viewController будет запрашивать новый параметр при инициализации, поэтому у нас будет что-то вроде этого:

@Binding var isClicked: Bool
func makeUIViewController(context: UIViewControllerRepresentableContext<GMControllerRepresentable>) -> GMController {
        return GMController(isClicked: $isClicked)
    }

в представлении структуры:

@State var isClicked: Bool = false
var body: some View {
        GoogMapControllerRepresentable(isClicked: $isClicked)
.sheet(isPresented: $isShown) { () -> View in
            <#code#>
        }
    }

и еще кое-что, что нам просто нужно переключить эту переменную при щелчке маркера следующим образом:

func mapView(_ mapView: GMSMapView, didTapInfoWindowOf didTapInfoWindowOfMarker: GMSMarker) {
        print("You tapped a marker's infowindow!")
//        This is where i need to get the view to appear as a modal, and my attempt below
       self.isClicked.toggle()
// if you want to pass more parameters you can set them from here like self.info = //mapView.coordinate <- Example
        return
    }
person fakiho    schedule 02.12.2019
comment
ты отличный человек. Это сработало, и ваш ответ помог мне лучше понять мою настройку. Спасибо! - person zlyt; 03.12.2019