googlemaps / google-maps-ios-utils

Google Maps SDK for iOS Utility Library
https://developers.google.com/maps/documentation/ios-sdk/utility/
Apache License 2.0
716 stars 408 forks source link

Marker disappeared on cluster zoom  #321

Open hiteshborse12 opened 4 years ago

hiteshborse12 commented 4 years ago

I am working on a cluster. Showing cluster using marker GMSMapView and GMUClusterManager. Markers clusters are showing properly but marker disappeared on map zoom.

Note: Assiging custom image to marker icon: let marker = GMSMarker(position: position) //custome marker image marker.icon = icon clusterManager.add(marker)

Code example

import UIKit
import GoogleMaps
import GoogleMapsUtils
class ViewController: UIViewController,GMSMapViewDelegate {
    @IBOutlet weak var mapview: GMSMapView!
    private var clusterManager: GMUClusterManager!

    override func viewDidLoad(){
        let iconGenerator = GMUDefaultClusterIconGenerator()
        let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
         let renderer = GMUDefaultClusterRenderer(mapView: mapview,
                                       clusterIconGenerator: iconGenerator)
        renderer.delegate = self
        self.clusterManager = GMUClusterManager(map: self.mapview ?? GMSMapView(), algorithm: algorithm, renderer: renderer)
        self.clusterManager.setDelegate(self, mapDelegate: self)

        // Add ClusterItems
        for i in 0..<10{
            let img = UIImage(named: "pin")!
            self.generateClusterItems(lat: "\(19+i)", lng:  "\(72+i)", name: "\(i)", icon: img)
        }
        self.clusterManager.cluster()
        self.mapview.animate(to: GMSCameraPosition(latitude: 19, longitude: 72, zoom: mapview.minZoom))

    }
    /// adds them to the cluster manager.
    private func generateClusterItems(lat: String, lng: String,name: String, icon: UIImage) {
        guard let lat = Double(lat) else { return  }
        guard let lng = Double(lng) else { return  }
        let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        let marker = GMSMarker(position: position)
        //custome marker image
        marker.icon = icon
        clusterManager.add(marker)
    }
}
//MARK:- GMUClusterManagerDelegate-
extension ViewController: GMUClusterManagerDelegate,GMUClusterRendererDelegate {
    func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool {
        print("didTap clusterItem")
        return true
    }
    func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
        print("didTap cluster")
        return true
    }
}

Project code and sample video: link

arriolac commented 4 years ago

I'm able to reproduce this. It appears that there is a bug when you try to add a GMSMarker directly. While I look into a fix, please create a class that conforms to GMUClusterItem as a workaround:

class ClusterItem: GMUClusterItem {
    var position: CLLocationCoordinate2D

    init(position: CLLocationCoordinate2D) {
        self.position= position
    }
}

class ViewController: UIViewController {

    /// adds them to the cluster manager.
    private func generateClusterItems(lat: String, lng: String,name: String, icon: UIImage) {
        guard let lat = Double(lat) else { return  }
        guard let lng = Double(lng) else { return  }
        let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        let item = ClusterIte(position: position)
        clusterManager.add(item)
    }
}

... and then attach a custom marker through the GMUClusterRendererDelegate method:

extension ViewController: GMUClusterManagerDelegate,GMUClusterRendererDelegate {
  func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
    marker.icon = UIImage(named: "pin")
  }
}
hiteshborse12 commented 4 years ago

I did the above changes.. and it fixed the marker disappear issue. But now I am not able to see the cluster (10+). Sending you code and screenshot

class ViewController: UIViewController,GMSMapViewDelegate { @IBOutlet weak var mapview: GMSMapView! private var clusterManager: GMUClusterManager!

override func viewDidLoad(){
    let iconGenerator = GMUDefaultClusterIconGenerator()
    let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
    let renderer = GMUDefaultClusterRenderer(mapView: mapview,
                                             clusterIconGenerator: iconGenerator)
    renderer.delegate = self
    self.clusterManager = GMUClusterManager(map: self.mapview ?? GMSMapView(), algorithm: algorithm, renderer: renderer)
    self.clusterManager.setDelegate(self, mapDelegate: self)
    for i in 0..<10{
        let img = UIImage(named: "pin")!
        self.generateClusterItems(lat: "\(19+i)", lng:  "\(72+i)", name: "\(i)", icon: img)
    }
    self.clusterManager.cluster()
    self.mapview.animate(to: GMSCameraPosition(latitude: 19, longitude: 72, zoom: mapview.minZoom))
}

private func generateClusterItems(lat: String, lng: String,name: String, icon: UIImage) {
    guard let lat = Double(lat) else { return  }
    guard let lng = Double(lng) else { return  }
    let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
    **let item = ClusterItem(position: position)**
    clusterManager.add(item)
}

} extension ViewController: GMUClusterManagerDelegate,GMUClusterRendererDelegate { func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) { marker.icon = UIImage(named: "pin") }

} class ClusterItem:NSObject, GMUClusterItem { var position: CLLocationCoordinate2D init(position: CLLocationCoordinate2D) { self.position = position } }

Simulator Screen Shot - iPhone 11 Pro Max - 2020-07-30 at 22 13 23

ViewController.swift.zip

arriolac commented 4 years ago

Meant to say that you also need to check the type of marker.userData and only change the image if it is a GMUClusterItem:

    func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
      if marker.userData is GMUClusterItem {
        marker.icon = UIImage(named: "pin")
      }
    }
hiteshborse12 commented 4 years ago

Oh yes make sense. All issues fixed now. Thanks for the quick help.

arriolac commented 4 years ago

Reopening this issue because this is still a bug with the library. The code snippet you shared earlier should work.

carloshwa commented 4 years ago

Is there an ETA for this fix? The bug is reproducible in the project's sample app as well. Hope to avoid releasing an update to my app with a workaround if the library will be fixed soon. Well, hope to avoid releasing a workaround, period :)

MichaelNeas commented 4 years ago

Also experiencing this issue

googlemaps-bot commented 4 years ago

:tada: This issue has been resolved in version 3.3.1 :tada:

The release is available on GitHub release

Your semantic-release bot :package::rocket:

hiteshborse12 commented 4 years ago

Great. Thanks :-)

barnavarga commented 4 years ago

@arriolac the problem still exists. GMSMarker disappears by changing the zoom if its position value (latitude and longitude) changes in the background and of course marker is related with the GMUClusterManager.

arriolac commented 3 years ago

@barnavarga can you provide a code snippet to repro that?

SparklingAir commented 2 years ago

@arriolac Hello! I have:

pod 'GoogleMaps', '6.2.1'
pod 'GooglePlaces', '6.2.1'
pod 'Google-Maps-iOS-Utils', '~> 4.1.0'

and problem is still here. The workaround above didn't help. My markers have iconView as a horizontal rectangle to the right of the anchor, and disappear (during zoom) when the left side of the rectangle goes off the edge of the screen. My code:

final class MapViewController: UIViewController {
    ...
    private lazy var clusterManager: GMUClusterManager? = {
        let buckets: [NSNumber] = [10, 25, 50, 100, 250, 500, 1000]
        let images: [UIImage] = buckets.map { _ in
            UIImage(named: "cluster") ?? UIImage()
        }
        let iconGenerator = GMUDefaultClusterIconGenerator(buckets: buckets, backgroundImages: images)
        let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
        renderer.minimumClusterSize = 1
        renderer.maximumClusterZoom = 13

        guard let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm(clusterDistancePoints: 20) else { return nil }
        let manager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
        manager.setDelegate(self, mapDelegate: self)
        return manager
    }()
    ...
    func setupMarkers(_ events: [MapEventModel]) {
        clusterManager?.clearItems()
        for (i, event) in events.enumerated() {
            let customMarker = CustomMarkerView(tag: i, model: event)
            let marker = GMSMarker()
            marker.iconView = customMarker
            marker.position = event.coordinate
            marker.groundAnchor = CGPoint(x: 0.0, y: 1.0)
            marker.appearAnimation = .fadeIn
            marker.zIndex = Int32(i)
            clusterManager?.add(marker)
        }
        clusterManager?.cluster()
    }
    ...
}