На моих картах я также показываю некоторые маркеры карты.
Это работает отлично, однако я хочу, чтобы карта игнорировала безопасные области устройства, чтобы она растягивалась на всю высоту (за любыми вырезами и нижними панелями вкладок).
Проблема, с которой я сталкиваюсь, заключается в том, что при применении .ignoresSafeArea() на карту, все мои маркеры на карте обрезаются, например:

Это происходит только тогда, когда я добавляю .ignoresSafeArea(). Без него маркеры отображаются нормально.
Я не уверен, что делаю неправильно.
Вот мой код SwiftUI, который показывает карту:
import GoogleMaps
struct MapView: View {
@EnvironmentObject var apiManager: ApiManager
private var mapOptions: GMSMapViewOptions {
var options = GMSMapViewOptions()
return options
}
var body: some View {
ZStack {
GoogleMapView(options: mapOptions)
.mapMarkers(MapHelper.convertToGoogleMarkers(markers: apiManager.markers))
.ignoresSafeArea()
}
.task {
getMapMarkers()
}
.navigationBarHidden(true)
}
func getMapMarkers() {
Task {
await apiManager.fetchAllMapMarkers()
}
}
}
А вот UIViewRepresentable для GoogleMapView:
import SwiftUI
import GoogleMaps
/// A SwiftUI wrapper for GMSMapView that displays a map with optional markers and configurable map type
struct GoogleMapView: UIViewRepresentable {
// Configuration properties - set at initialization
private let options: GMSMapViewOptions
/// Array of markers to display on the map
private var markers: [GMSMarker]
/// Type of map to display (normal, satellite, hybrid, terrain)
private let mapType: GMSMapViewType
// Runtime updatable properties
private var camera: GMSCameraPosition?
private var backgroundColor: UIColor?
/// Shared delegate instance to handle map interactions across all instances
/// Using static ensures callbacks work together when chaining modifiers
private static let mapDelegate = GoogleMapViewDelegate()
init(options: 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 {
// Initialize map with current options
let mapView = GMSMapView(options: options)
mapView.overrideUserInterfaceStyle = .unspecified
mapView.mapType = mapType
// Set shared delegate to handle interactions
mapView.delegate = Self.mapDelegate
return mapView
}
/// Updates the map view when SwiftUI state changes
func updateUIView(_ uiView: GMSMapView, context: Context) {
// Update runtime properties if set
if let camera = camera {
uiView.camera = camera
}
if let backgroundColor = backgroundColor {
uiView.backgroundColor = backgroundColor
}
//clears all markers and polylines
uiView.clear()
// Refresh markers on the map
markers.forEach { marker in
marker.map = uiView
}
uiView.mapType = mapType // Update map type if changed
}
}
class GoogleMapViewDelegate: NSObject, GMSMapViewDelegate {
var tapHandler: ((CLLocationCoordinate2D) -> Void)?
var markerTapHandler: ((GMSMarker) -> Bool)?
/// Called by GMSMapView when user taps the map at a specific coordinate
/// - Parameters:
/// - mapView: The GMSMapView that detected the tap
/// - coordinate: The geographic coordinate where the tap occurred
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
tapHandler?(coordinate) // Forward tap to handler if one is set
}
/// Called by GMSMapView when user taps a marker on the map
/// - Parameters:
/// - mapView: The GMSMapView that detected the tap
/// - marker: The GMSMarker that was tapped
/// - Returns: true if tap was handled by the app, false to allow default marker behavior
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
return markerTapHandler?(marker) ?? false // Forward to handler or use default behavior
}
}
// MARK: - viewModifiers and Markers
extension GoogleMapView {
/// Updates the camera position of the map view during runtime
/// - Parameter position: New camera position to apply
/// - Returns: Updated GoogleMapView instance
func camera(_ position: GMSCameraPosition?) -> GoogleMapView {
var view = self
if let position = position {
view.camera = position
}
return view
}
/// Updates the background color of the map view during runtime
/// - Parameter color: New background color to apply
/// - Returns: Updated GoogleMapView instance
func backgroundColor(_ color: UIColor) -> GoogleMapView {
var view = self
view.backgroundColor = color
return view
}
/// Changes the map display type
/// - Parameter type: GMSMapViewType to use (.normal, .satellite, etc)
/// - Returns: New GoogleMapView instance with updated map type
func mapType(_ type: GMSMapViewType) -> GoogleMapView {
GoogleMapView(options: options, markers: markers, mapType: type)
}
/// 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 {
var view = self
view.markers = markers
return view
}
}
// MARK: - View Callbacks
extension GoogleMapView {
/// Adds handler for map tap events
/// - Parameter handler: Closure called when map is tapped, providing tap coordinates
/// - Returns: Same GoogleMapView instance with updated tap handler
func onMapTapped(_ handler: @escaping (CLLocationCoordinate2D) -> Void) -> GoogleMapView {
Self.mapDelegate.tapHandler = handler
return self
}
/// Adds handler for marker tap events
/// - Parameter handler: Closure called when marker is tapped
/// - Returns: Same GoogleMapView instance with updated marker handler
/// Return true from handler to indicate tap was handled
func onMarkerTapped(_ handler: @escaping (GMSMarker) -> Bool) -> GoogleMapView {
Self.mapDelegate.markerTapHandler = handler
return self
}
}
extension View {
/// Configures the view to ignore safe areas except for the top
/// Useful for map views that should fill the screen below status bar
/// - Returns: Modified view that extends to screen edges except top
func ignoresSafeAreaExceptTop() -> some View {
ignoresSafeArea(.container, edges: [.bottom, .horizontal])
}
}
Наконец, вот моя функция ConvertToGoogleMarkers():
static func convertToGoogleMarkers(markers: Array, showOrder: Bool = false) -> Array {
return markers.enumerated().map { (index, mapMarker) in
let marker = GMSMarker(position: CLLocationCoordinate2D(
latitude: Double(mapMarker.latitude)!,
longitude: Double(mapMarker.longitude)!
))
// Set the Place as the userData
marker.userData = mapMarker
let markerView = MarkerUIView(marker: mapMarker, showOrder: showOrder)
let size = markerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
markerView.frame = CGRect(origin: .zero, size: size)
marker.iconView = markerView
if let order = mapMarker.order {
if (mapMarker.date != nil) {
marker.zIndex = Int32(10000 - order)
} else {
marker.zIndex = Int32(1000 - order)
}
}
return marker
}
}
Изменить:
Похоже, что если я закомментирую этот код из моей функции ConvertToGoogleMarkers(), маркеры карт Google по умолчанию будут отображаться нормально. В чем может быть проблема с моими собственными маркерами?
// let markerView = MarkerUIView(marker: mapMarker, showOrder: showOrder)
//
// let size = markerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
// markerView.frame = CGRect(origin: .zero, size: size)
//
// marker.iconView = markerView
Вот мой MapMarkerView:
import SwiftUI
import MapKit
struct MapMarkerView: View {
var marker: MapMarker2
var showOrder: Bool = false
let circleBorderWidth: CGFloat = 4
var body: some View {
VStack(spacing: 0) {
ZStack {
let color = marker.hexColor != nil ? Color(hex: marker.hexColor!) : Color.wireframe
if (showOrder && marker.date != nil) {
Circle()
.fill(color)
.frame(width: 40, height: 40)
.shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 4)
.overlay(
Circle()
.inset(by: circleBorderWidth / 2)
.stroke(Color.mapMarkerBackground, lineWidth: circleBorderWidth)
)
if let order = marker.order {
Text("\(order)")
.font(.custom("Inter-SemiBold", size: 18))
.foregroundColor(Color.white)
}
} else {
Circle()
.fill(Color.mapMarkerBackground)
.frame(width: 40, height: 40)
.shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 4)
.overlay(
ZStack {
Circle()
.fill(color)
.opacity(0.33)
Circle()
.inset(by: circleBorderWidth / 2)
.stroke(Color.mapMarkerBackground, lineWidth: circleBorderWidth)
}
)
AsyncImage(url: URL(string: marker.iconUrl ?? "")) { image in
image.resizable()
.frame(width: 24, height: 24)
} placeholder: {
Circle()
.fill(Color.mapMarkerBackground)
.frame(width: 40, height: 40)
}
}
}
Triangle()
.rotationEffect(.degrees(-180))
.foregroundColor(Color.mapMarkerBackground)
.frame(width: 12, height: 10)
.offset(y: -4)
}
}
}
class MarkerUIView: UIView {
private var hostingController: UIHostingController?
private var onTapAction: (() -> Void)?
init(marker: MapMarker2, showOrder: Bool = false) {
super.init(frame: .zero)
// Use AnyView so we can capture the binding
self.hostingController = UIHostingController(
rootView: AnyView(MapMarkerView(marker: marker, showOrder: showOrder))
)
guard let hostingView = hostingController?.view else { return }
// Make background transparent
hostingView.backgroundColor = .clear
// Add the SwiftUI view to the UIView hierarchy
addSubview(hostingView)
// Size the hosting controller's view
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.leadingAnchor.constraint(equalTo: leadingAnchor),
hostingView.trailingAnchor.constraint(equalTo: trailingAnchor),
hostingView.topAnchor.constraint(equalTo: topAnchor),
hostingView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
sizeToFit()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Forward tap to the SwiftUI view
override func touchesEnded(_ touches: Set, with event: UIEvent?) {
onTapAction?()
super.touchesEnded(touches, with: event)
}
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... google-map
Мобильная версия