使用SwiftUI和Mapbox在MGLMapView中的同一地址处处理多个注释

问题描述

问题是,我在此上找不到任何文档-有人知道是否有一种方法可以在同一位置整齐地处理批注(以便您可以单击批注或按钮来循环)通过该地点的注释或其他注释)?我只需要一种在特定位置循环浏览注释并分别访问它们的方法。任何帮助和/或建议将不胜感激。

func mapView(_ mapView: MGLMapView,didSelect annotation: MGLAnnotation) {
    //I tried to do a check here for if selectedAnnotation == annotation { somehow cycle to the next annotation in that location } but I guess when you click an already selectedAnnotation,the diddeselect function is run or something
    selectedAnnotation = annotation
    mapView.setCenter(annotation.coordinate,zoomLevel: 17,animated: true)
}

我的注释功能如下:

class AnnotationsVM: ObservableObject {
    @Published var annos = [MGLPointAnnotation]()
    @Observedobject var VModel: viewmodel //= viewmodel()

    init(VModel: viewmodel) {
        self.VModel = VModel
        let annotation = MGLPointAnnotation()
        annotation.title = "Shoe Store"
        annotation.coordinate = CLLocationCoordinate2D(latitude: 40.78,longitude: -73.98)
        annotation.subtitle = "10:00AM - 11:30AM"
        annos.append(annotation)
    }

    func addNextAnnotation(address: String) {
        let newAnnotation = MGLPointAnnotation()
            self.VModel.fetchCoords(address: address) { lat,lon in
            if (lat != 0.0 && lon != 0.0) {
                newAnnotation.coordinate = CLLocationCoordinate2D(latitude: lat,longitude: lon)
            }

            newAnnotation.title = address
            newAnnotation.subtitle = "9:00PM - 1:00AM"
        

                if (lat != 0 && lon != 0) {
                    self.annos.append(newAnnotation)
                }
        }
    }
}

在MapView(UIViewRepresentable)结构的updateUIView函数中,将annos数组添加到地图中。

解决方法

更新为上一个答案:

我(经常)误解了问题。 因此,这是执行以下操作的更新的存储库:

您可以根据需要向同一地点(地点A和地点B)添加尽可能多的位置。当您选择一个地点时,将打开一个自定义视图并显示更多信息。然后,您可以循环遍历同一位置的所有位置。这是通过比较初始选定地点的经纬度来完成的。

我将所有内容都推送到github

我尽量保持简短: 在模型中,我从同一位置获取所有位置,对其进行计数并为其设置视图。

import SwiftUI
import Combine
import Mapbox

struct AnnotationLocation{
  let latitude: Double
  let longitude: Double
  let title: String?
}

/// Source of Truth
class AnnotationModel: ObservableObject {
  var didChange = PassthroughSubject<Void,Never>()
  
  var annotationsForOperations: [AnnotationLocation] =  [AnnotationLocation]()
  @Published var locationsAtSameSpot: [AnnotationLocation] =  [AnnotationLocation]()
  @Published var showCustomCallout: Bool = false
  @Published var countSameSpots: Int = 0
  @Published var selectedAnnotation: AnnotationLocation = AnnotationLocation(latitude: 0,longitude: 0,title: nil)
  
  func addLocationInModel(annotation: MGLPointAnnotation) {
    
    let newSpot = AnnotationLocation(latitude: annotation.coordinate.latitude,longitude: annotation.coordinate.longitude,title: annotation.title ?? "No Title")
    annotationsForOperations.append(newSpot)
  }
  
  func getAllLocationsFormSameSpot() {
    locationsAtSameSpot = [AnnotationLocation]()
    
    for annotation in annotationsForOperations {
      if annotation.latitude == selectedAnnotation.latitude &&
        annotation.longitude == selectedAnnotation.longitude {
        locationsAtSameSpot.append(annotation)
      }
    }
  }
  
  func getNextAnnotation(index: Int) -> Bool {
    if locationsAtSameSpot.indices.contains(index + 1) {
      selectedAnnotation = locationsAtSameSpot[index + 1]
      return true
    } else {
      return false
    }
  }
}

MapView委托设置初始位置,并在模型中触发函数以从同一位置获取所有位置。

func mapView(_ mapView: MGLMapView,didSelect annotation: MGLAnnotation) {
      
      /// Create a customLoaction and assign it the model
      /// The values are needed to loop though the same annotations
      let customAnnotation = AnnotationLocation(latitude: annotation.coordinate.latitude,title: annotation.title ?? "No Tilte")
      
      /// assignselected annotion  @EnvironmentObject
      /// so it can be shown in the custom callout
      annotationModel.selectedAnnotation = customAnnotation
      
      /// show custom call out
      annotationModel.showCustomCallout = true
      
      /// count locations at same spot
      /// also pushes same locations into separte array to loop through
      annotationModel.getAllLocationsFormSameSpot()
      
      mapView.setCenter(annotation.coordinate,zoomLevel: 17,animated: true)
    }

最后,SwiftUI视图应该是自解释的...

import SwiftUI
import Mapbox

struct ContentView: View {
  @EnvironmentObject var annotationModel: AnnotationModel
  
  @State var annotations: [MGLPointAnnotation] = [MGLPointAnnotation]()
  @State private var showAnnotation: Bool = false
  @State private var nextAnnotation: Int = 0
  
  var body: some View {
    GeometryReader{ g in
      VStack{
        ZStack(alignment: .top){
          MapView(annotations: self.$annotations).centerCoordinate(.init(latitude: 37.791293,longitude: -122.396324)).zoomLevel(16).environmentObject(self.annotationModel)
          
          if self.annotationModel.showCustomCallout {
            VStack{
              
              HStack{
                Spacer()
                Button(action: {
                  self.annotationModel.showCustomCallout = false
                }) {
                  Image(systemName: "xmark")
                    .foregroundColor(Color.black)
                    .font(Font.system(size: 12,weight: .regular))
                }.offset(x: -5,y: 5)
                
              }
              
              HStack{
                Text("Custom Callout")
                  .font(Font.system(size: 12,weight: .regular))
                  .foregroundColor(Color.black)
              }
              
              Spacer()
              
              Text("Selected: \(self.annotationModel.selectedAnnotation.title ?? "No Tiltle")")
                .font(Font.system(size: 16,weight: .regular))
                .foregroundColor(Color.black)
                
              Text("Count same Spot: \(self.annotationModel.locationsAtSameSpot.count) ")
                .font(Font.system(size: 16,weight: .regular))
                .foregroundColor(Color.black)
              
               Spacer()
              
              Button(action: {
                let gotNextSpot = self.annotationModel.getNextAnnotation(index: self.nextAnnotation)
                if gotNextSpot {
                  self.nextAnnotation += 1
                } else {
                  self.nextAnnotation = -1 // a bit dirty...
                }
                
              }) {
                Text("Get Next Spot >")
              }
              
            }.background(Color.white)
              .frame(width: 200,height: 250,alignment: .center)
              .cornerRadius(10)
              .offset(x: 0,y: 0)
          }
        }
        
        VStack{
          HStack{
          Button(action: {
            self.addNextAnnotation(address: "Spot \(Int.random(in: 1..<1000))",isSpotA: true)
          }) {
            Text("Add to Spot A")
          }.frame(width: 200,height: 50)
          
         Button(action: {
          self.addNextAnnotation(address: "Spot \(Int.random(in: 1..<1000))",isSpotA: false)
         }) {
           Text("Add to Spot B")
         }.frame(width: 200,height: 50)
        }
           Spacer().frame(height: 50)
        }
      }
    }
  }
  
  /// add a random annotion to the map
  /// - Parameter address: address description
  func addNextAnnotation(address: String,isSpotA: Bool) {
    
    var newAnnotation = MGLPointAnnotation(title: address,coordinate: .init(latitude:  37.7912434,longitude: -122.396267))
    
    if !isSpotA {
      newAnnotation = MGLPointAnnotation(title: address,coordinate: .init(latitude:  37.7914434,longitude: -122.396467))
    }
    
    
    /// append to @State var which is used in teh mapview
    annotations.append(newAnnotation)
    
    /// also add location to model for calculations
    /// would need refactoring since this is redundant
    /// i leave it like that since it is more a prove of concept
    annotationModel.addLocationInModel(annotation: newAnnotation)
  
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView().environmentObject(AnnotationModel())
  }
}

enter image description here

边缘仍然很粗糙,但可能是一个起点。


上一个答案,也许对某些人还是有用的。 看看回购中的提交以获取代码。


这是MapBox视图的工作演示,可添加随机注释,当您选择其中一个注释时,它将循环遍历注释数组并在TextView中显示所选注释:

此演示基于出色的MapBox demo app

要添加所需的功能,我决定为该模型使用@EnvironmentObject。 UIViewRepresentable,Combine

我将所有内容都推送到github 我没有使用过您的模型,但我认为您可以将功能集成到AnnotationModel中,也可以在SwiftUI视图中进行逻辑处理。

这就是我所做的:

  1. 定义模型:

    import SwiftUI
    import Combine
    
    /// Source of Truth
    class AnnotationModel: ObservableObject {
     var didChange = PassthroughSubject<Void,Never>()
     @Published var selectedAnnotaion: String = "none"
    }
    
  2. 将@EnvironmentObject添加到SceneDelegate

     let contentView = ContentView().environmentObject(AnnotationModel())
    
  3. 棘手的部分是使用MapBox UIViewRepresentable将其连接到@EnvironmentObject。从上方查看链接,了解如何通过协调器完成操作

    struct MapView: UIViewRepresentable {
        @Binding var annotations: [MGLPointAnnotation]
        @EnvironmentObject var annotationModel: AnnotationModel
    
        let mapView: MGLMapView = MGLMapView(frame: .zero,styleURL: MGLStyle.streetsStyleURL)
    
        // MARK: - Configuring UIViewRepresentable protocol
    
        func makeUIView(context: UIViewRepresentableContext<MapView>) -> MGLMapView {
          mapView.delegate = context.coordinator
          return mapView
        }
    
        func updateUIView(_ uiView: MGLMapView,context:      UIViewRepresentableContext<MapView>) {
          updateAnnotations()
        }
    
        func makeCoordinator() -> MapView.Coordinator {
          Coordinator(self,annotationModel: _annotationModel)
        }
    
  4. 在MapBox协调器中初始化@EnvironmentObject,然后可以使用该类,其中包含所有MapBox委托的类。在didSelect委托中,我可以遍历MapView中@Binding的注释,并通过SwiftUI中的@State var进行进一步设置。

    final class Coordinator: NSObject,MGLMapViewDelegate {
         var control: MapView
        @EnvironmentObject var annotationModel: AnnotationModel
    
    init(_ control: MapView,annotationModel: EnvironmentObject<AnnotationModel>) {
      self.control = control
      self._annotationModel = annotationModel
    }
    
    func mapView(_ mapView: MGLMapView,didSelect annotation: MGLAnnotation) {
     guard let annotationCollection = mapView.annotations else { return }
    
     /// cycle throu the annotations from the binding
     /// @Binding var annotations: [MGLPointAnnotation]
     for _annotation in annotationCollection {
       print("annotation",annotation)
    
    if annotation.coordinate.latitude == _annotation.coordinate.latitude {
      /// this is the same annotation
      print("*** Selected annoation")
      if let hastTitle = annotation.title {
        annotationModel.selectedAnnotaion = hastTitle ?? "no string in title"
      }
    } else {
      print("--- Not the selected annoation")
    }
    }
    
  5. 最后是SwiftUI视图

     import SwiftUI
     import Mapbox
    
     struct ContentView: View {
       @EnvironmentObject var annotationModel: AnnotationModel
    
       @State var annotations: [MGLPointAnnotation] = [
         MGLPointAnnotation(title: "Mapbox",coordinate: .init(latitude: 37.791434,longitude: -122.396267))
       ]
    
       @State private var selectedAnnotaion: String = ""
    
       var body: some View {
    VStack{
      MapView(annotations: $annotations).centerCoordinate(.init(latitude: 37.791293,longitude: -122.396324)).zoomLevel(16).environmentObject(annotationModel)
    
      VStack{
        Button(action: {
          self.addNextAnnotation(address: "Location: \(Int.random(in: 1..<1000))")
        }) {
          Text("Add Location")
        }.frame(width: 200,height: 50)
    
        Text("Selected: \(annotationModel.selectedAnnotaion)")
    
        Spacer().frame(height: 50)
      }
    }
       }
    
       /// add a random annotion to the map
       /// - Parameter address: address description
       func addNextAnnotation(address: String) {
         let randomLatitude = Double.random(in: 37.7912434..<37.7918434)
         let randomLongitude = Double.random(in: 122.396267..<122.396867) * -1
         let newAnnotation = MGLPointAnnotation(title: address,coordinate: .init(latitude: randomLatitude,longitude: randomLongitude))
    
         annotations.append(newAnnotation)
       }
     }
    
     struct ContentView_Previews: PreviewProvider {
       static var previews: some View {
         ContentView().environmentObject(AnnotationModel())
       }
     }
    

重要的是@State变量在MapBox中设置注释。 MapBox委托遍历此数组并在@EnvironmentObject中设置selectedText。这是MapBox委托与SwiftUI之间的连接。您还可以将批注放在@EnvironmentObject中,我没有这样做,因为已经在演示应用程序中定义了该批注...

让我知道这是否有帮助。很高兴检查出这个...

enter image description here

,

我最终完成了自己的实现-我制作了一个具有相同纬度和经度的注释数组,然后在自定义标注中添加了按钮,以循环浏览该数组,从而跟踪我正在查看的注释。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...