基于SwiftUI的MKAnnotationView在地图边缘渲染异常问题排查
问题:SwiftUI封装的MKAnnotationView在地图边缘显示错乱
我开发了基于SwiftUI的MKAnnotationView组件,在UIViewRepresentable封装的MKMapView中使用该标注视图。目前遇到异常:当标注视图渲染在地图边缘位置时,会出现显示错乱问题,靠近地图中心时显示恢复正常。推测问题可能出在MapPin视图或setupUI方法中的UIHostingController实现,但无法明确具体原因。将SwiftUI实现替换为纯UIKit自定义视图后,该Bug未再出现。
MapAnnotationView类实现
final class MapAnnotationView: MKAnnotationView { override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) frame = CGRect(x: 0, y: 0, width: 50, height: 50) centerOffset = CGPoint(x: 0, y: -frame.size.height / 2) } @available(*, unavailable) required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setupUI(with annotation: PlaceAnnotation) { backgroundColor = .clear let vc = UIHostingController(rootView: MapPin(color: Color(hex: annotation.color ?? 0), imageSystemName: annotation.icon ?? "")) vc.view.backgroundColor = .clear addSubview(vc.view) vc.view.frame = bounds } }
MapPin视图实现
struct MapPinShape: Shape { func path(in rect: CGRect) -> Path { let radius = rect.width / 2 var path = Path() path.addArc(center: CGPoint(x: rect.midX, y: rect.minY + radius), radius: radius, startAngle: .degrees(70), endAngle: .degrees(110), clockwise: true) path.addLine(to: CGPoint(x: rect.midX, y: radius * 2 + 0.2 * radius)) return path } } struct MapPin: View { var color: Color var imageSystemName: String var body: some View { ZStack { MapPinShape() .fill(color.darker()) GeometryReader { geo in Circle().fill(color) .frame(width: geo.size.width * 0.90) .frame(width: geo.size.width, height: geo.size.height, alignment: .center) Image(systemName: imageSystemName) .resizable() .scaledToFit() .foregroundColor(.white) .frame(width: geo.size.width / 3) .frame(width: geo.size.width, height: geo.size.height, alignment: .center) } } } }
UIViewRepresentable封装的MKMapView
struct MapView: UIViewRepresentable { var annotations: [PlaceAnnotation] var trackingMode: MKUserTrackingMode var onLongPress: (_ location: MapLocation) -> Void var onAnnotationTap: (_ annotation: PlaceAnnotation) -> Void let mapView = MKMapView() let locationManager = CLLocationManager() func makeUIView(context: Context) -> MKMapView { locationManager.delegate = context.coordinator mapView.delegate = context.coordinator mapView.showsUserLocation = true mapView.register(MapAnnotationView.self, forAnnotationViewWithReuseIdentifier: "AnnotationView") mapView.register(MapClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: "ClusterAnnotationView") let longPressRecognizer = UILongPressGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.longPressed(_:))) mapView.addGestureRecognizer(longPressRecognizer) configureLocationServices() return mapView } func updateUIView(_ mapView: MKMapView, context: Context) { let existing = mapView.annotations.compactMap { $0 as? EntMap.PlaceAnnotation } let diff = annotations.difference(from: existing) { $0 === $1 } for change in diff { switch change { case .insert(_, let element, _): mapView.addAnnotation(element) case .remove(_, let element, _): mapView.removeAnnotation(element) } } } func makeCoordinator() -> MapViewCoordinator { MapViewCoordinator(parent: self) } func centerMapOnUserLocation() { guard let coordinate = locationManager.location?.coordinate else {return} let coordinateRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000) mapView.setRegion(coordinateRegion, animated: true) } private func configureLocationServices() { if locationManager.authorizationStatus == .notDetermined { locationManager.requestAlwaysAuthorization() } else { return } } } class MapViewCoordinator: NSObject, MKMapViewDelegate, CLLocationManagerDelegate { let parent: MapView init(parent: MapView) { self.parent = parent } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if let annotation = annotation as? EntMap.PlaceAnnotation { if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView", for: annotation) as? MapAnnotationView { let mapAnnotation = annotation as EntMap.PlaceAnnotation annotationView.clusteringIdentifier = "ClusterAnnotationView" annotationView.setupUI(with: mapAnnotation) return annotationView } } if let annotation = annotation as? MKClusterAnnotation { if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "ClusterAnnotationView") as? MapClusterAnnotationView { annotationView.annotation = annotation let count = annotation.memberAnnotations.count < 100 ? "\(annotation.memberAnnotations.count)" : "99+" annotationView.setupUI(count: count) return annotationView } } return nil } func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) { for view in views { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(annotationTapped)) view.addGestureRecognizer(tapGesture) } } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { parent.centerMapOnUserLocation() } @objc func annotationTapped(_ gestureRecognizer: UITapGestureRecognizer) { if let view = gestureRecognizer.view as? MKAnnotationView { if let annotation = view.annotation as? MKClusterAnnotation { parent.mapView.showAnnotations(annotation.memberAnnotations, animated: true) } if let annotation = view.annotation as? EntMap.PlaceAnnotation { parent.onAnnotationTap(annotation) } } } @objc func longPressed(_ gestureRecognizer: UIGestureRecognizer) { if gestureRecognizer.state == .ended { if let map = gestureRecognizer.view as? MKMapView { let tapPoint = gestureRecognizer.location(in: map) let tapCoordinate = map.convert(tapPoint, toCoordinateFrom: map) parent.onLongPress(MapLocation(coordinates: tapCoordinate)) } } } }
显示效果对比
- 异常显示:

- 正常显示:

内容的提问来源于stack exchange,提问作者Bartson
相关产品推荐
相关产品推荐

