Swiftui didSelect上的MapKit/MKMapViewDelegate:底页不';如果启用了用户位置,则无法打开
我正在尝试在iOS上重建Apple Maps的注释选择行为。如果我点击地图上的图钉,它会打开一张底部的纸,上面有关于这个地方的更多信息 虽然已经对所有基本组件进行了编码,但如果在地图上启用了用户位置,则我的底部工作表不会打开 如果启用了用户位置,为什么底部工作表不打开?我真的很感谢你的意见。谢谢 如何复制它:Swiftui didSelect上的MapKit/MKMapViewDelegate:底页不';如果启用了用户位置,则无法打开,swiftui,mapkit,core-location,mapkitannotation,mkmapviewdelegate,Swiftui,Mapkit,Core Location,Mapkitannotation,Mkmapviewdelegate,我正在尝试在iOS上重建Apple Maps的注释选择行为。如果我点击地图上的图钉,它会打开一张底部的纸,上面有关于这个地方的更多信息 虽然已经对所有基本组件进行了编码,但如果在地图上启用了用户位置,则我的底部工作表不会打开 如果启用了用户位置,为什么底部工作表不打开?我真的很感谢你的意见。谢谢 如何复制它: 根据要测试的内容,在MapMainView.swift中更改变量showCurrentLocation 请不要忘记添加Info.plist条目Privacy-Location In U
MapMainView.swift
中更改变量showCurrentLocation
Info.plist
条目Privacy-Location In Use Description
和Privacy-Location Always Usage Description
,以访问设备的本机位置import SwiftUI
struct ContentView: View {
var body: some View {
MapMainView()
}
}
import Foundation
import SwiftUI
import MapKit
struct MapMainView: View {
let showCurrentLocation = false
let locationFetcher = LocationFetcher()
@State var selectedPin: MapPin? = nil
@State var isBottomSheetOpen: Bool = false
@State var examplePins = [MapPin]()
var body: some View {
GeometryReader { geometry in
ZStack() {
VStack() {
Spacer()
BottomSheetView(isOpen: self.$isBottomSheetOpen, maxHeight: geometry.size.height * 0.3) {
Text(String(self.selectedPin?.title ?? "no title")).foregroundColor(Color.black)
}
}
.edgesIgnoringSafeArea(.all)
.zIndex(1)
MapView(locationFetcher: self.locationFetcher, showCurrentLocation: self.showCurrentLocation, displayedPins: self.$examplePins, selectedPin: self.$selectedPin, isBottomSheetOpen: self.$isBottomSheetOpen)
.edgesIgnoringSafeArea(.all)
.onAppear{
var currentLat: Double
var currentLng: Double
if self.showCurrentLocation {
currentLat = self.locationFetcher.getCurrentCoordinates()?.latitude ?? 46.9457590197085
currentLng = self.locationFetcher.getCurrentCoordinates()?.longitude ?? 8.007923669708498
} else {
currentLat = 46.9457590197085
currentLng = 8.007923669708498
}
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.004, longitude: currentLng - 0.002), title: "First Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat + 0.002, longitude: currentLng + 0.002), title: "Second Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.002, longitude: currentLng + 0.004), title: "Third Pin"))
}
}
}
}
}
class MapPin: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(coordinate: CLLocationCoordinate2D, title: String? = nil) {
self.coordinate = coordinate
self.title = title
}
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
let locationFetcher: LocationFetcher
let showCurrentLocation: Bool
@Binding var displayedPins: [MapPin]
@Binding var selectedPin: MapPin?
@Binding var isBottomSheetOpen: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
if showCurrentLocation {
mapView.showsUserLocation = true
self.locationFetcher.attemptLocationAccess()
centerLocation(mapView: mapView, locationCoordinate: locationFetcher.getCurrentCoordinates())
} else {
centerLocation(mapView: mapView, locationCoordinate: CLLocationCoordinate2D(latitude: 46.9457590197085, longitude: 8.007923669708498))
}
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
if self.displayedPins.count != mapView.annotations.count {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(self.displayedPins)
}
}
func centerLocation(mapView: MKMapView, locationCoordinate: CLLocationCoordinate2D?) {
if locationCoordinate != nil {
let kilometerRadius = 1.5;
let scalingFactor = abs((cos(2 * Double.pi * locationCoordinate!.latitude / 360.0)));
let span = MKCoordinateSpan(latitudeDelta: kilometerRadius/111, longitudeDelta: kilometerRadius/(scalingFactor * 111))
let region = MKCoordinateRegion(center: locationCoordinate!, span: span)
mapView.setRegion(region, animated: true)
}
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let pin = view.annotation as? MapPin else {
return
}
mapView.setCenter(pin.coordinate, animated: true)
DispatchQueue.main.async {
self.parent.selectedPin = pin
self.parent.isBottomSheetOpen = true
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard (view.annotation as? MapPin) != nil else {
return
}
DispatchQueue.main.async {
self.parent.selectedPin = nil
self.parent.isBottomSheetOpen = false
}
}
}
import Foundation
import SwiftUI
struct BottomSheetView<Content: View>: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: Constants.RADIUS)
.fill(Color.black)
.frame(
width: Constants.INDICATOR_WIDTH,
height: Constants.INDICATOR_HEIGHT
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * Constants.MIN_HEIGHT_RATIO
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
.cornerRadius(Constants.RADIUS)
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset + self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * Constants.SNAP_RATIO
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
}
}
enum Constants {
static let RADIUS: CGFloat = 16
static let INDICATOR_HEIGHT: CGFloat = 6
static let INDICATOR_WIDTH: CGFloat = 60
static let SNAP_RATIO: CGFloat = 0.25
static let MIN_HEIGHT_RATIO: CGFloat = 0
}
MapMainView.swift
import SwiftUI
struct ContentView: View {
var body: some View {
MapMainView()
}
}
import Foundation
import SwiftUI
import MapKit
struct MapMainView: View {
let showCurrentLocation = false
let locationFetcher = LocationFetcher()
@State var selectedPin: MapPin? = nil
@State var isBottomSheetOpen: Bool = false
@State var examplePins = [MapPin]()
var body: some View {
GeometryReader { geometry in
ZStack() {
VStack() {
Spacer()
BottomSheetView(isOpen: self.$isBottomSheetOpen, maxHeight: geometry.size.height * 0.3) {
Text(String(self.selectedPin?.title ?? "no title")).foregroundColor(Color.black)
}
}
.edgesIgnoringSafeArea(.all)
.zIndex(1)
MapView(locationFetcher: self.locationFetcher, showCurrentLocation: self.showCurrentLocation, displayedPins: self.$examplePins, selectedPin: self.$selectedPin, isBottomSheetOpen: self.$isBottomSheetOpen)
.edgesIgnoringSafeArea(.all)
.onAppear{
var currentLat: Double
var currentLng: Double
if self.showCurrentLocation {
currentLat = self.locationFetcher.getCurrentCoordinates()?.latitude ?? 46.9457590197085
currentLng = self.locationFetcher.getCurrentCoordinates()?.longitude ?? 8.007923669708498
} else {
currentLat = 46.9457590197085
currentLng = 8.007923669708498
}
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.004, longitude: currentLng - 0.002), title: "First Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat + 0.002, longitude: currentLng + 0.002), title: "Second Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.002, longitude: currentLng + 0.004), title: "Third Pin"))
}
}
}
}
}
class MapPin: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(coordinate: CLLocationCoordinate2D, title: String? = nil) {
self.coordinate = coordinate
self.title = title
}
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
let locationFetcher: LocationFetcher
let showCurrentLocation: Bool
@Binding var displayedPins: [MapPin]
@Binding var selectedPin: MapPin?
@Binding var isBottomSheetOpen: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
if showCurrentLocation {
mapView.showsUserLocation = true
self.locationFetcher.attemptLocationAccess()
centerLocation(mapView: mapView, locationCoordinate: locationFetcher.getCurrentCoordinates())
} else {
centerLocation(mapView: mapView, locationCoordinate: CLLocationCoordinate2D(latitude: 46.9457590197085, longitude: 8.007923669708498))
}
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
if self.displayedPins.count != mapView.annotations.count {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(self.displayedPins)
}
}
func centerLocation(mapView: MKMapView, locationCoordinate: CLLocationCoordinate2D?) {
if locationCoordinate != nil {
let kilometerRadius = 1.5;
let scalingFactor = abs((cos(2 * Double.pi * locationCoordinate!.latitude / 360.0)));
let span = MKCoordinateSpan(latitudeDelta: kilometerRadius/111, longitudeDelta: kilometerRadius/(scalingFactor * 111))
let region = MKCoordinateRegion(center: locationCoordinate!, span: span)
mapView.setRegion(region, animated: true)
}
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let pin = view.annotation as? MapPin else {
return
}
mapView.setCenter(pin.coordinate, animated: true)
DispatchQueue.main.async {
self.parent.selectedPin = pin
self.parent.isBottomSheetOpen = true
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard (view.annotation as? MapPin) != nil else {
return
}
DispatchQueue.main.async {
self.parent.selectedPin = nil
self.parent.isBottomSheetOpen = false
}
}
}
import Foundation
import SwiftUI
struct BottomSheetView<Content: View>: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: Constants.RADIUS)
.fill(Color.black)
.frame(
width: Constants.INDICATOR_WIDTH,
height: Constants.INDICATOR_HEIGHT
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * Constants.MIN_HEIGHT_RATIO
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
.cornerRadius(Constants.RADIUS)
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset + self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * Constants.SNAP_RATIO
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
}
}
enum Constants {
static let RADIUS: CGFloat = 16
static let INDICATOR_HEIGHT: CGFloat = 6
static let INDICATOR_WIDTH: CGFloat = 60
static let SNAP_RATIO: CGFloat = 0.25
static let MIN_HEIGHT_RATIO: CGFloat = 0
}
斯威夫特地图
import SwiftUI
struct ContentView: View {
var body: some View {
MapMainView()
}
}
import Foundation
import SwiftUI
import MapKit
struct MapMainView: View {
let showCurrentLocation = false
let locationFetcher = LocationFetcher()
@State var selectedPin: MapPin? = nil
@State var isBottomSheetOpen: Bool = false
@State var examplePins = [MapPin]()
var body: some View {
GeometryReader { geometry in
ZStack() {
VStack() {
Spacer()
BottomSheetView(isOpen: self.$isBottomSheetOpen, maxHeight: geometry.size.height * 0.3) {
Text(String(self.selectedPin?.title ?? "no title")).foregroundColor(Color.black)
}
}
.edgesIgnoringSafeArea(.all)
.zIndex(1)
MapView(locationFetcher: self.locationFetcher, showCurrentLocation: self.showCurrentLocation, displayedPins: self.$examplePins, selectedPin: self.$selectedPin, isBottomSheetOpen: self.$isBottomSheetOpen)
.edgesIgnoringSafeArea(.all)
.onAppear{
var currentLat: Double
var currentLng: Double
if self.showCurrentLocation {
currentLat = self.locationFetcher.getCurrentCoordinates()?.latitude ?? 46.9457590197085
currentLng = self.locationFetcher.getCurrentCoordinates()?.longitude ?? 8.007923669708498
} else {
currentLat = 46.9457590197085
currentLng = 8.007923669708498
}
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.004, longitude: currentLng - 0.002), title: "First Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat + 0.002, longitude: currentLng + 0.002), title: "Second Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.002, longitude: currentLng + 0.004), title: "Third Pin"))
}
}
}
}
}
class MapPin: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(coordinate: CLLocationCoordinate2D, title: String? = nil) {
self.coordinate = coordinate
self.title = title
}
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
let locationFetcher: LocationFetcher
let showCurrentLocation: Bool
@Binding var displayedPins: [MapPin]
@Binding var selectedPin: MapPin?
@Binding var isBottomSheetOpen: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
if showCurrentLocation {
mapView.showsUserLocation = true
self.locationFetcher.attemptLocationAccess()
centerLocation(mapView: mapView, locationCoordinate: locationFetcher.getCurrentCoordinates())
} else {
centerLocation(mapView: mapView, locationCoordinate: CLLocationCoordinate2D(latitude: 46.9457590197085, longitude: 8.007923669708498))
}
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
if self.displayedPins.count != mapView.annotations.count {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(self.displayedPins)
}
}
func centerLocation(mapView: MKMapView, locationCoordinate: CLLocationCoordinate2D?) {
if locationCoordinate != nil {
let kilometerRadius = 1.5;
let scalingFactor = abs((cos(2 * Double.pi * locationCoordinate!.latitude / 360.0)));
let span = MKCoordinateSpan(latitudeDelta: kilometerRadius/111, longitudeDelta: kilometerRadius/(scalingFactor * 111))
let region = MKCoordinateRegion(center: locationCoordinate!, span: span)
mapView.setRegion(region, animated: true)
}
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let pin = view.annotation as? MapPin else {
return
}
mapView.setCenter(pin.coordinate, animated: true)
DispatchQueue.main.async {
self.parent.selectedPin = pin
self.parent.isBottomSheetOpen = true
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard (view.annotation as? MapPin) != nil else {
return
}
DispatchQueue.main.async {
self.parent.selectedPin = nil
self.parent.isBottomSheetOpen = false
}
}
}
import Foundation
import SwiftUI
struct BottomSheetView<Content: View>: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: Constants.RADIUS)
.fill(Color.black)
.frame(
width: Constants.INDICATOR_WIDTH,
height: Constants.INDICATOR_HEIGHT
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * Constants.MIN_HEIGHT_RATIO
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
.cornerRadius(Constants.RADIUS)
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset + self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * Constants.SNAP_RATIO
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
}
}
enum Constants {
static let RADIUS: CGFloat = 16
static let INDICATOR_HEIGHT: CGFloat = 6
static let INDICATOR_WIDTH: CGFloat = 60
static let SNAP_RATIO: CGFloat = 0.25
static let MIN_HEIGHT_RATIO: CGFloat = 0
}
斯威夫特
import SwiftUI
struct ContentView: View {
var body: some View {
MapMainView()
}
}
import Foundation
import SwiftUI
import MapKit
struct MapMainView: View {
let showCurrentLocation = false
let locationFetcher = LocationFetcher()
@State var selectedPin: MapPin? = nil
@State var isBottomSheetOpen: Bool = false
@State var examplePins = [MapPin]()
var body: some View {
GeometryReader { geometry in
ZStack() {
VStack() {
Spacer()
BottomSheetView(isOpen: self.$isBottomSheetOpen, maxHeight: geometry.size.height * 0.3) {
Text(String(self.selectedPin?.title ?? "no title")).foregroundColor(Color.black)
}
}
.edgesIgnoringSafeArea(.all)
.zIndex(1)
MapView(locationFetcher: self.locationFetcher, showCurrentLocation: self.showCurrentLocation, displayedPins: self.$examplePins, selectedPin: self.$selectedPin, isBottomSheetOpen: self.$isBottomSheetOpen)
.edgesIgnoringSafeArea(.all)
.onAppear{
var currentLat: Double
var currentLng: Double
if self.showCurrentLocation {
currentLat = self.locationFetcher.getCurrentCoordinates()?.latitude ?? 46.9457590197085
currentLng = self.locationFetcher.getCurrentCoordinates()?.longitude ?? 8.007923669708498
} else {
currentLat = 46.9457590197085
currentLng = 8.007923669708498
}
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.004, longitude: currentLng - 0.002), title: "First Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat + 0.002, longitude: currentLng + 0.002), title: "Second Pin"))
self.examplePins.append(MapPin(coordinate: CLLocationCoordinate2D(latitude: currentLat - 0.002, longitude: currentLng + 0.004), title: "Third Pin"))
}
}
}
}
}
class MapPin: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(coordinate: CLLocationCoordinate2D, title: String? = nil) {
self.coordinate = coordinate
self.title = title
}
}
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
let locationFetcher: LocationFetcher
let showCurrentLocation: Bool
@Binding var displayedPins: [MapPin]
@Binding var selectedPin: MapPin?
@Binding var isBottomSheetOpen: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
if showCurrentLocation {
mapView.showsUserLocation = true
self.locationFetcher.attemptLocationAccess()
centerLocation(mapView: mapView, locationCoordinate: locationFetcher.getCurrentCoordinates())
} else {
centerLocation(mapView: mapView, locationCoordinate: CLLocationCoordinate2D(latitude: 46.9457590197085, longitude: 8.007923669708498))
}
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {
if self.displayedPins.count != mapView.annotations.count {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(self.displayedPins)
}
}
func centerLocation(mapView: MKMapView, locationCoordinate: CLLocationCoordinate2D?) {
if locationCoordinate != nil {
let kilometerRadius = 1.5;
let scalingFactor = abs((cos(2 * Double.pi * locationCoordinate!.latitude / 360.0)));
let span = MKCoordinateSpan(latitudeDelta: kilometerRadius/111, longitudeDelta: kilometerRadius/(scalingFactor * 111))
let region = MKCoordinateRegion(center: locationCoordinate!, span: span)
mapView.setRegion(region, animated: true)
}
}
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let pin = view.annotation as? MapPin else {
return
}
mapView.setCenter(pin.coordinate, animated: true)
DispatchQueue.main.async {
self.parent.selectedPin = pin
self.parent.isBottomSheetOpen = true
}
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
guard (view.annotation as? MapPin) != nil else {
return
}
DispatchQueue.main.async {
self.parent.selectedPin = nil
self.parent.isBottomSheetOpen = false
}
}
}
import Foundation
import SwiftUI
struct BottomSheetView<Content: View>: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Binding var isOpen: Bool
let maxHeight: CGFloat
let minHeight: CGFloat
let content: Content
@GestureState private var translation: CGFloat = 0
private var offset: CGFloat {
isOpen ? 0 : maxHeight - minHeight
}
private var indicator: some View {
RoundedRectangle(cornerRadius: Constants.RADIUS)
.fill(Color.black)
.frame(
width: Constants.INDICATOR_WIDTH,
height: Constants.INDICATOR_HEIGHT
).onTapGesture {
self.isOpen.toggle()
}
}
init(isOpen: Binding<Bool>, maxHeight: CGFloat, @ViewBuilder content: () -> Content) {
self.minHeight = maxHeight * Constants.MIN_HEIGHT_RATIO
self.maxHeight = maxHeight
self.content = content()
self._isOpen = isOpen
}
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.indicator.padding()
self.content
}
.frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
.background(Color.white)
.cornerRadius(Constants.RADIUS)
.frame(height: geometry.size.height, alignment: .bottom)
.offset(y: max(self.offset + self.translation, 0))
.animation(.interactiveSpring())
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
state = value.translation.height
}.onEnded { value in
let snapDistance = self.maxHeight * Constants.SNAP_RATIO
guard abs(value.translation.height) > snapDistance else {
return
}
self.isOpen = value.translation.height < 0
}
)
}
}
}
enum Constants {
static let RADIUS: CGFloat = 16
static let INDICATOR_HEIGHT: CGFloat = 6
static let INDICATOR_WIDTH: CGFloat = 60
static let SNAP_RATIO: CGFloat = 0.25
static let MIN_HEIGHT_RATIO: CGFloat = 0
}
或者你可以在这里下载所有文件我自己发现了问题。不仅我的MapPins,蓝色的位置圆也算作mapView注释。因此,如果启用了位置服务,my UpdateUI()将删除并添加每个didSelect上的所有注释,因为self.displayedPins.count!=mapView.annotations.count始终为false,这会导致底部图纸突然消失 在过滤注释后,它最终起作用:
func updateUIView(_ mapView: MKMapView, context: Context) {
let displayedMapPins = mapView.annotations.filter { annotation in
return annotation is MapPin
}
if self.mapPinsToDisplay.count != displayedMapPins.count {
mapView.removeAnnotations(displayedMapPins)
mapView.addAnnotations(self.mapPinsToDisplay)
}
}