在 iOS 中点击 Google 地图标记信息窗口后,如何使表单 sheet 出现?
How to make form sheet appear after Google Maps marker infoWindow is tapped in iOS?
我目前正在使用 SwiftUI 和 Google 地图构建应用程序。我试图让表单 sheet 在点击 Google 地图标记的信息窗口后显示,但我无法让它工作。
在我应用程序的其他部分,我使用此方法显示 sheets:
Example method here
我尝试使用与上面相同的方法在点击标记的信息窗口后显示 sheet,但是在函数内执行此操作时遇到问题。我的以下代码片段提供了更多详细信息。
-
下面是我的 GMView.swift 文件的精简版本,它控制我的 Google 地图实例。 (我的文件看起来与典型的 Swift + Google 地图集成不同,因为我使用 SwiftUI)。
您会注意到该文件的 3 个主要部分:1. 视图,2. GMController class 和 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>) {
}
}
这是我在 GMView.swift 文件中添加到 GMController class 的功能,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形式sheet.
有谁知道如何在 SwiftUI 中点击 Google 地图标记信息窗口后显示 sheet 而不是仅仅添加它作为一个子视图?
你好,要从结构视图与 UIViewController 交互,你需要绑定一个变量。首先我们声明 @Binding var isClicked : Bool
如果你需要将更多参数传递给结构,你需要使用声明 @ 声明它捆绑。在 UIViewController 中的任何方式错误将显示 isClicked
属性 'self.isClicked' 未初始化以解决此问题我们声明:
@Binding var isClicked
init(isClicked: Binding<Bool>) {
_isClicked = isClicked
super.init(nibName: nil, bundle: nil)
}
还有 UIViewController 的指定初始化程序是 initWithNibName:bundle:。您应该改为调用它。如果您没有 nib,请为 nibName 传入 nil(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
}
我目前正在使用 SwiftUI 和 Google 地图构建应用程序。我试图让表单 sheet 在点击 Google 地图标记的信息窗口后显示,但我无法让它工作。
在我应用程序的其他部分,我使用此方法显示 sheets: Example method here
我尝试使用与上面相同的方法在点击标记的信息窗口后显示 sheet,但是在函数内执行此操作时遇到问题。我的以下代码片段提供了更多详细信息。
-
下面是我的 GMView.swift 文件的精简版本,它控制我的 Google 地图实例。 (我的文件看起来与典型的 Swift + Google 地图集成不同,因为我使用 SwiftUI)。 您会注意到该文件的 3 个主要部分:1. 视图,2. GMController class 和 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>) {
}
}
这是我在 GMView.swift 文件中添加到 GMController class 的功能,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形式sheet.
有谁知道如何在 SwiftUI 中点击 Google 地图标记信息窗口后显示 sheet 而不是仅仅添加它作为一个子视图?
你好,要从结构视图与 UIViewController 交互,你需要绑定一个变量。首先我们声明 @Binding var isClicked : Bool
如果你需要将更多参数传递给结构,你需要使用声明 @ 声明它捆绑。在 UIViewController 中的任何方式错误将显示 isClicked
属性 'self.isClicked' 未初始化以解决此问题我们声明:
@Binding var isClicked
init(isClicked: Binding<Bool>) {
_isClicked = isClicked
super.init(nibName: nil, bundle: nil)
}
还有 UIViewController 的指定初始化程序是 initWithNibName:bundle:。您应该改为调用它。如果您没有 nib,请为 nibName 传入 nil(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
}