Can we change icon of marker when click on AGSPictureMarkerSymbol

1336
3
09-26-2018 04:23 AM
chiragshah4
New Contributor II

I have requirement to change AGSPictureMarkerSymbol picture when user clicked on marker? is this possible?

Tags (1)
0 Kudos
3 Replies
Nicholas-Furness
Esri Regular Contributor

We don't have an alternate image property that you can just switch to when you click the marker.

You would have to manually switch the symbol, and how you do that would depend on whether the marker comes from a feature service or is a graphic in a graphics overlay. There are a couple of possible approaches.

  • You could manipulate the renderer on the layer to display that particular marker differently. How complex this approach is will depend on the renderer defined on the layer. In the simplest case, you would replace the layer's AGSSimpleRenderer with an AGSUniqueValueRenderer that has the default symbol as the old AGSSimpleRenderer's symbol, and a single AGSUniqueValue keyed off the feature's ObjectID to display only that feature differently. Add and remove AGSUniqueValues for each feature that is "toggled on".
  • You could hide that marker (using setFeature:visible()) and use an AGSGraphicsOverlay to display the replacement marker.
  • Depending on your requirements, you could use the Selection logic to highlight the marker (but it sounds like that won't suffice for you).
  • If the marker is in an AGSGraphicsOverlay, then you could simply switch the symbol on the graphic. You could alternatively use a renderer and make sure each graphic in the overlay has an attribute that suitably drives the renderer, a bit like the first option above but with total control over the field used to drive things.

There may be other approaches I haven't thought of. I've used the first approach myself and it works well, but only if you understand the original renderer.

JakeShapley
New Contributor III

I've been working on a similar requirement. My observation is the one problem with reassigning the renderer, even with a uniqueValueRenderer, is that all the markers flash off and on. This isn't the best experience. We are starting with a AGSGraphicOverlay, not a feature layer, but solution should be similar. We also have a requirement to animate the marker. I'll share when I'm done, but here are the general steps:

1) Query the graphic overlay (or feature layer) with identify task. 

2) Set the visibility of the selected graphic (or feature) to false.

3) Add a custom UIImageView as a sublayer to the mapView, using the mapView.location(toScreen: AGSPoint) to construct a CGRect for the image view frame

4) Animate the image view, in my case, translating to grow, but maintain tip of marker on location

5) Add marker with same final image, size and yOffset to a second graphic overlay

6) Upon new tap, remove the second graphic overlay, optionally re-add and animate down an image view, remove it, and set all graphics in the first graphic overlay to isVisible = true

Hope this helps.

Cheers,

Jake

0 Kudos
JakeShapley
New Contributor III

This example utilizes 2 separate AGSGraphicOverlay layers, but you could similarly accomplish with a feature layer and a graphic overlay layer. It also uses a custom UIImageView to transition between removing the selected graphic from the one layer, and adding it to the second (selected graphic overlay). It's not quite perfect, but appears to work as desired. Alternatively, you can clone the project (uses CocoaPods and ArcGIS Runtime 100.2.1):

//

//  ViewController.swift

//  ESRI Scratch

//

//  Created by Jake Shapley on 9/30/18.

//  Licensed under MIT

//

import UIKit

import ArcGIS

class ViewController: UIViewController, AGSCalloutDelegate, AGSGeoViewTouchDelegate {

    

    // MARK: Properties

    

    @IBOutlet weak var mapView: AGSMapView!

    

    var waypointGraphic = AGSGraphic()

    var selectedWayPointImageView = UIImageView()

    var wayPointLayer = AGSGraphicsOverlay()

    var selectedPointLayer = AGSGraphicsOverlay()

    var lastQuery: AGSCancelable!

    

    // MARK: Load

    override func viewDidLoad() {

        super.viewDidLoad()

        self.initMap()

    }

    // MARK: Initializers

    

    fileprivate func initMap() {

        let map = AGSMap(basemap: AGSBasemap.lightGrayCanvasVector())

        self.mapView.map = map

        self.mapView.touchDelegate = self

        self.mapView.setViewpoint(AGSViewpoint(center: AGSPoint(x: -13454510, y: 6071864, spatialReference: AGSSpatialReference.webMercator()), scale: 8000000), completion: nil)

        self.updateWaypointGeometry()

    }

    

    // MARK: Waypoints

    

    fileprivate var waypoints: [(id: String, name: String, loc: CLLocationCoordinate2D)] {

        get {

            var points = [(id: String, name: String, loc: CLLocationCoordinate2D)]()

            points.append((id: "1", name: "Car", loc: CLLocationCoordinate2D(latitude: 46.323, longitude: -123.221)))

            points.append((id: "2", name: "Park", loc: CLLocationCoordinate2D(latitude: 46.446, longitude: -118.619)))

            points.append((id: "3", name: "Tree", loc: CLLocationCoordinate2D(latitude: 46.567, longitude: -122.521)))

            points.append((id: "4", name: "Old House", loc: CLLocationCoordinate2D(latitude: 47.237, longitude: -122.421)))

            points.append((id: "5", name: "Bird", loc: CLLocationCoordinate2D(latitude: 47.667, longitude: -122.123)))

            points.append((id: "6", name: "Truck", loc: CLLocationCoordinate2D(latitude: 46.667, longitude: -121.123)))

            points.append((id: "7", name: "School", loc: CLLocationCoordinate2D(latitude: 48.667, longitude: -119.123)))

            return points

        }

    }

    

    fileprivate func updateWaypointGeometry() {

        self.mapView.graphicsOverlays.remove(self.wayPointLayer)

        self.wayPointLayer.graphics.removeAllObjects()

        if waypoints.count > 0 {

            let wgs84 = AGSSpatialReference.wgs84()

            let symbol = AGSPictureMarkerSymbol(image: UIImage(named: "waypointMarker")!)

            symbol.height = 13

            symbol.width = 15

            symbol.offsetX = 0

            symbol.offsetY = 0

            for point in waypoints {

                let id = point.id

                let name = point.name

                let attributes = ["id": id, "name": name]

                let lat = point.loc.latitude

                let long = point.loc.longitude

                let loc = AGSPoint(x: long, y: lat, spatialReference: wgs84)

                let graphic = AGSGraphic(geometry: loc, symbol: symbol, attributes: attributes)

                self.wayPointLayer.graphics.add(graphic)

            }

            self.mapView.graphicsOverlays.add(self.wayPointLayer)

        }

    }

    

    fileprivate func updateSelectedWaypointGeometry(at point: AGSPoint, attributes: NSMutableDictionary?) -> Void {

        self.waypointGraphic.isVisible = false

        self.mapView.graphicsOverlays.remove(self.selectedPointLayer)

        self.selectedPointLayer.graphics.removeAllObjects()

        self.selectedWayPointImageView = UIImageView(image: UIImage(named: "waypointFilled"))

        let cgCenter = self.mapView.location(toScreen: point)

        let rect = CGRect(x: cgCenter.x - 10.0, y: cgCenter.y - 10.0, width: 20, height: 20)

        self.selectedWayPointImageView.frame = rect

        self.view.addSubview(self.selectedWayPointImageView)

        UIView.animate(withDuration: 0.3, delay: 0.0, options: [.curveEaseInOut], animations:

            {

                self.selectedWayPointImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.8).translatedBy(x: 0, y: -8)

        }

            , completion: { _ in

                let symbol = AGSPictureMarkerSymbol(image: UIImage(named: "waypointFilled")!)

                symbol.height = 35

                symbol.width = 25

                symbol.offsetX = 0

                symbol.offsetY = 18

                let graphic = AGSGraphic(geometry: point, symbol: symbol, attributes: attributes as! [String : Any])

                self.selectedPointLayer.graphics.add(graphic)

                self.mapView.graphicsOverlays.add(self.selectedPointLayer)

                //sleep(2)

                DispatchQueue.main.asyncAfter(deadline: (DispatchTime.now() + .milliseconds(200)), execute: {

                    self.selectedWayPointImageView.alpha = 0.0

                    self.selectedWayPointImageView.removeFromSuperview()

                })

        })

    }

    

    // MARK: GeoView Touch Delegate

    

    func geoView(_ geoView: AGSGeoView, didTapAtScreenPoint screenPoint: CGPoint, mapPoint: AGSPoint) {

        if let lastQuery = self.lastQuery {

            lastQuery.cancel()

        }

        if waypoints.count > 0 {

            self.waypointGraphic.isVisible = true

            self.lastQuery = self.mapView.identify(self.wayPointLayer, screenPoint: screenPoint, tolerance: 8.0, returnPopupsOnly: false, maximumResults: 1, completion: { [weak self] (identifyLayerResult: AGSIdentifyGraphicsOverlayResult?) -> Void in

                if let graphics = identifyLayerResult?.graphics, graphics.count > 0 {

                    let generator = UISelectionFeedbackGenerator()

                    generator.selectionChanged()

                    self?.waypointGraphic = graphics[0]

                    let attributes = self?.waypointGraphic.attributes

                    let mapPoint = self?.waypointGraphic.geometry?.extent.center

                    self?.updateSelectedWaypointGeometry(at: mapPoint!, attributes: attributes)

                } else {

                    self?.mapView.graphicsOverlays.remove(self?.selectedPointLayer)

                    self?.selectedPointLayer.graphics.removeAllObjects()

                }

            })

        }

    }

}

Cheers,

Jake