pauljohanneskraft / Map

MKMapView wrapper for SwiftUI as drop-in to MapKit's SwiftUI view. Easily extensible annotations and overlays, iOS 13 support and backwards compatible with MKAnnotation and MKOverlay!
MIT License
181 stars 48 forks source link

onTapGesture not working #39

Open IraklisElef opened 1 year ago

IraklisElef commented 1 year ago

The onTapGesture is not working inside the ViewMapAnnotation, here is my code:

 `Map(
        coordinateRegion: $region,
        type: .standard,
        pointOfInterestFilter: .includingAll,
        interactionModes: [.all],
        annotationItems: evChargerManager.evChargers,
        annotationContent: { charger in

            ViewMapAnnotation(coordinate: (charger.addressInfo?.coordinates)!) {

                Image(K.Images.SupplementarilyIcons.pinpoint)
                    .resizable()
                    .frame(width: 40, height: 40, alignment: .center)
                    .onTapGesture {
                        showingSheet.toggle()
                    }
                    .sheet(isPresented: $showingSheet) {
                        EVChargerView(evCharger: charger)
                    } //: SHEET

            } //: VIEW MAP ANNOTATION

        } //: ANNOTATION CONTENT

    ) //: MAP`
pauljohanneskraft commented 1 year ago

You are right - this is one of the issues that #38 is trying to fix - could you please try whether your gesture works when targeting that branch?

JohannesWi commented 1 year ago

Got the same problem. Using the Branch https://github.com/iakov-kaiumov/Map didn't solve the problem for me.

Hope this get fixed soon as the library is amazing and I would love to use it!

iakov-kaiumov commented 1 year ago

@JohannesWi Hi! Could you provide an example of code that is not working for you, please? It will help me to solve the issue. In my project everything works fine.

JohannesWi commented 1 year ago

Thanks for your response @iakov-kaiumov ! Im using the same example as @IraklisElef is doing. My code looks like this:

Map(
                         coordinateRegion: $region,
                          type: .standard,
                          pointOfInterestFilter: .excludingAll,
                          informationVisibility: .default.union(.userLocation),
                          interactionModes: [.all],
                          annotationItems: markers,
                          annotationContent: { location in
                              ViewMapAnnotation(coordinate: location.coordinate) {
                                  Color.red
                                    .frame(width: 24, height: 24)
                                    .clipShape(Circle())
                                    .onTapGesture {
                                      print("test")
                                    }
                              }
                          }
                        )
                .frame(height: 350)
                .cornerRadius(8)
iakov-kaiumov commented 1 year ago

@JohannesWi This is really strange, because this code works for me. Attaching screen recording and code fragment.

struct Annotation: Identifiable {

    let id = UUID()
    let coordinate: CLLocationCoordinate2D
    let clusteringIdentifier: String

}

struct ContentView: View {

    @State private var region = MKCoordinateRegion(
        center: .init(latitude: 50, longitude: 10),
        latitudinalMeters: 100_000,
        longitudinalMeters: 100_000
    )

    @State private var annotations = [
        Annotation(coordinate: .init(latitude: 49.5, longitude: 09.5), clusteringIdentifier: "a"),
        Annotation(coordinate: .init(latitude: 49.5, longitude: 10.0), clusteringIdentifier: "a"),
        Annotation(coordinate: .init(latitude: 49.5, longitude: 10.5), clusteringIdentifier: "a"),
        Annotation(coordinate: .init(latitude: 50.0, longitude: 09.5), clusteringIdentifier: "b"),
        Annotation(coordinate: .init(latitude: 50.0, longitude: 10.0), clusteringIdentifier: "b"),
        Annotation(coordinate: .init(latitude: 50.0, longitude: 10.5), clusteringIdentifier: "b"),
        Annotation(coordinate: .init(latitude: 50.5, longitude: 09.5), clusteringIdentifier: "b"),
        Annotation(coordinate: .init(latitude: 50.5, longitude: 10.0), clusteringIdentifier: "b"),
        Annotation(coordinate: .init(latitude: 50.5, longitude: 10.5), clusteringIdentifier: "b"),
    ]

    @State private var isPresented: Bool = false

    var body: some View {
        Map(
            coordinateRegion: $region,
            type: .standard,
            pointOfInterestFilter: .excludingAll,
            informationVisibility: .default.union(.userLocation),
            interactionModes: [.all],
            annotationItems: annotations,
            annotationContent: { location in
                ViewMapAnnotation(coordinate: location.coordinate) {
                    Color.red
                    .frame(width: 24, height: 24)
                    .clipShape(Circle())
                    .onTapGesture {
                        isPresented.toggle()
                    }
                }
            }
        )
        .cornerRadius(8)
        .alert(isPresented: $isPresented) { Alert(title: Text("Tapped!")) }
    }
}

https://user-images.githubusercontent.com/29120192/224317131-ef486954-0ca2-4c4d-8159-0d2117f26e7a.mov

IraklisElef commented 1 year ago

The branch of @iakov-kaiumov did it for me! It also fixed the weird bug with the annotation where it misplaces itself when applying the .ignoresSafeArea() modifier. However, there is another weird bug. When I press the annotation, instead of showing one sheet, it kind of shows 2 sheets one on top of the other, here is a screen recording:

https://user-images.githubusercontent.com/112685491/224356734-0e97cb31-f731-400f-afa8-2e2870f2cb9e.mov

JohannesWi commented 1 year ago

@iakov-kaiumov thank you very much for your reply! I have my map view inside a ZStack and added a custom color above which didn't work with the touch-event.

It would be great if this branch could be merged soon

iakov-kaiumov commented 1 year ago

The branch of @iakov-kaiumov did it for me! It also fixed the weird bug with the annotation where it misplaces itself when applying the .ignoresSafeArea() modifier. However, there is another weird bug. When I press the annotation, instead of showing one sheet, it kind of shows 2 sheets one on top of the other, here is a screen recording:

Could you please tell a little bit more about how you present the sheet? Such behaviour cannot occur when using one .sheet modifier even if the touch event trigger twice. Moreover, in my case, touch event fires only once, so, I guess that the problem is connected with your sheets implementation.

IraklisElef commented 1 year ago

Ok so, I have an array of annotations. Each annotation has coordinates and some other data. Whenever I am presenting this annotation, I want to be able to click it and then show a sheet. This sheet needs the annotation's data. However, I noticed that the sheet does not take the correct annotation. It takes data from another annotation. Moreover, if I use instead a NavigationLink instead of a sheet, it shows the correct data every time. Do you have any clue of what is going on?

IraklisElef commented 1 year ago

I figured it out. Here is my code:

                `Map(
                    coordinateRegion: $region,
                    type: .standard,
                    pointOfInterestFilter: .includingAll,
                    interactionModes: [.all],
                    annotationItems: mapInformation.evChargers,
                    annotationContent: { charger in

                        ViewMapAnnotation(coordinate: (charger.addressInfo?.coordinates)!) {

                            Image(K.Images.SupplementarilyIcons.pinpoint)
                                .resizable()
                                .frame(width: 40, height: 40, alignment: .center)
                                .onTapGesture {
                                    showingSheet.toggle()
                                }
                                .sheet(isPresented: $showingSheet) {
                                    EVChargerView(evCharger: charger)
                                } //: SHEET

                        } //: VIEW MAP ANNOTATION

                    } //: ANNOTATION CONTENT

                ) //: MAP`

As you can see, whenever the user taps the image. the showingSheet variable toggles to true, which ends up activating the sheet. Inside that sheet I have a view which takes as a parameter the annotation. However, because, as I understand, the Map struct keeps updating, it keeps passing every time a new annotation to the sheet view, which ends up creating not two sheets, but as many as there are annotations. Now that I know the issue, I am not sure how to work around it so that it passes only one annotation. I tried saving to a separate variable the annotation that the user taps on, and then passing that variable to the sheet view, but for some reason it does not save it. Any ideas?

iakov-kaiumov commented 1 year ago

@IraklisElef As I thought, the problem is not connected with the Map library. To show the sheet correctly, try the following code:

struct YourView: View {
  ...

  @State private var selectedAnnotation: Annotation? = nil

  var body: some View {
      Map(
          coordinateRegion: $region,
          annotationItems: annotations,
          annotationContent: { annotation in
              ViewMapAnnotation(coordinate: annotation.coordinate) {
                 Image(K.Images.SupplementarilyIcons.pinpoint)
                  .resizable()
                  .frame(width: 40, height: 40, alignment: .center)
                  .onTapGesture {
                      selectedAnnotation = annotation
                  }
              }
          }
      )
      .sheet(item: $selectedAnnotation) { annotation in
          List {
              Text("Sheet! \(annotation.id.uuidString)")
          }
      }
   }
}
IraklisElef commented 1 year ago

Game changing solution! Thank you! Could I ask you one more question? I am trying to use the clusterAnnotation property of the Map struct. However, I am not really sure how to implement it. Any suggestions?

iakov-kaiumov commented 1 year ago

You may take a look at the examples in the readme. Also, if you are using my branch, update it to the latest version, please.

IraklisElef commented 1 year ago

I have taken a look. However, I have not seen any mentions on how to use the clusterAnnotation property, nor have I seen any examples of it.

pauljohanneskraft commented 1 year ago

For the clusterAnnotation, first make sure to set the clusteringIdentifier parameter when initializing the ViewMapAnnotation. Annotations with the same clusteringIdentifier will be clustered, so make sure to not make that identifier fully unique, but according to some categorization or use the same for all annotations in use (depending on how you would like to cluster them), then the clusterAnnotation parameter in the Map-initializer will be called whenever the MKMapView decides to create clusters.

IraklisElef commented 1 year ago

I want the clustering to work whenever the user zooms out too much and the annotations are on top of each other. Does that mean that I will need to have the same clusteringIdentifier for all my annotations? Also, when I am trying to use clusterAnnotation, it asks me for 2 parameters. One is MKClusterAnnotation and the other the annotation items that I passed earlier on the annotationItems parameter. Could you give me an example of what to pass on MKClusterAnnotation? One last thing. I see that you've included an anchor parameter in the ViewMapAnnotation. What values would I need to pass on my CGPoint to anchor my annotation (basically in the middle bottom part of my image) on this spot:

IMG_5202

iakov-kaiumov commented 1 year ago
IraklisElef commented 1 year ago

I've managed to integrate a clusterAnnotation. One last question if you know the answer. Is it possible to cluster more annotations? What I am trying to say is, let's say that the user is currently zoomed out 25% and the number of annotations that are clustered is 20. I want to be able to cluster, for example 30 annotations, if the user is zoomed out by the same 25%. So basically make the area that is needed to cluster smaller. Is that possible or Swift has a default value for it?

pauljohanneskraft commented 1 year ago

I'm not sure whether this is even possible in MKMapView. @IraklisElef do you have a working example on how this would be implemented using MKMapView? If it is not possible there, it will not be possible here, since this is just a wrapper around the UIKit view.

IraklisElef commented 1 year ago

No I do not have a working example using MKMapView. I reckoned it would not be possible; nevertheless, I asked you in case you knew something I did not know. One question @pauljohanneskraft. On the example you provided on clustering annotations, here, I see that you've include one line of code that I do not understand its purpose, this one: let _ = assert(Set(members.map(\.clusteringIdentifier)).count == 1). Could you explain what it does? I removed this line of code and the clustering still worked.

pauljohanneskraft commented 1 year ago

You can remove it.

It has been part of the example to show that only annotations with the same clusteringIdentifier exist in a cluster (i.e. that the set of clusteringIdentifiers has a count of 1) and asserts this to be true (i.e. it would fail, if that weren't the case).

iakov-kaiumov commented 1 year ago

I've managed to integrate a clusterAnnotation. One last question if you know the answer. Is it possible to cluster more annotations? What I am trying to say is, let's say that the user is currently zoomed out 25% and the number of annotations that are clustered is 20. I want to be able to cluster, for example 30 annotations, if the user is zoomed out by the same 25%. So basically make the area that is needed to cluster smaller. Is that possible or Swift has a default value for it?

If I understand you correctly, your problem is that your annotations are not collapsing even if they visually intersect. This is an unsolved issue of my implementation (MKViewAnnotation frame is smaller than it should be). To solve the issue, try this branch #40

I've tested it and it works as expected. I hope soon it will be merged.

IraklisElef commented 1 year ago

Is it safe to use or are there any bugs?