didSelect上的MapKit / MKMapViewDelegate:如果启用了用户位置,则底页不会打开

我正在尝试在iOS上重建Apple Maps的注释选择行为。如果单击地图图钉,则应打开底页,其中包含有关该地点的更多信息。

虽然已经对所有基本组件进行了编码,但是如果在地图上启用了用户位置,则底页不会打开。

User Location disabled: bottom sheet opens correctly

User Location enabled: bottom sheet doesn’t open,but WHY?

如果启用了用户位置,为什么我的底页无法打开?我非常感谢您的投入。谢谢!

如何复制它:

  1. 根据要测试的内容,在showCurrentLocation中更改变量MapMainView.swift
  2. 请不要忘记添加Info.plistPrivacy - Location When In Use Usage Description项,以访问设备的本机位置。

ContentView.swift

Privacy - Location Always Usage Description

MapMainView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        MapMainView()
    }
}

MapView.swift

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
    }
}

BottomSheetView.swift

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
        }
    }
}

LocationFetcher.swift

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
}

或者您可以在https://gofile.io/d/LSfli5

下载所有文件
iCMS 回答:didSelect上的MapKit / MKMapViewDelegate:如果启用了用户位置,则底页不会打开

我自己发现了问题。不仅我的MapPins,蓝色的位置圆圈也算作mapView批注。因此,如果启用了位置服务,我的updateUIView()会删除并在每个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)
    }
}
本文链接:https://www.f2er.com/1855342.html

大家都在问