Skip to content

Instantly share code, notes, and snippets.

@nixta
Last active March 23, 2021 13:17
Show Gist options
  • Save nixta/ed31857dc1cd8abaebd6e7eacaa8b26f to your computer and use it in GitHub Desktop.
Save nixta/ed31857dc1cd8abaebd6e7eacaa8b26f to your computer and use it in GitHub Desktop.
A Google-Maps like single-finger zoom gesture for the ArcGIS Runtime SDK for iOS. Awesome idea, Google Maps team! πŸ‘πŸ‘πŸ‘
//
// AGSSingleFingerZoomGestureRecognizer.swift
//
// Created by Nicholas Furness on 6/17/16.
// Copyright Β© 2016 Esri. All rights reserved.
//
import Foundation
import ArcGIS
import UIKit.UIGestureRecognizerSubclass
enum AGSSingleFingerZoomMode {
case Google
case UpOutDownIn(centeredOnTap:Bool)
case UpInDownOut(centeredOnTap:Bool)
func translateScaleFactor(factor:Double) -> Double {
switch self {
case .Google, .UpOutDownIn:
return -factor
case .UpInDownOut:
return factor
}
}
}
class AGSSingleFingerZoomGestureRecognizer : UIGestureRecognizer {
var scalePower:Double = 5.0
var zoomMode:AGSSingleFingerZoomMode = .UpOutDownIn(centeredOnTap: true)
private var anchorPoint:CGPoint?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
guard let mapView = self.view as? AGSMapView else {
print("You should only apply \(self.dynamicType) to an AGSMapView!")
self.state = .Failed
return
}
guard touches.count == 1 else {
// Single finger only
self.state = .Cancelled
return
}
if let tap = touches.first {
guard tap.tapCount <= 2 else {
// Second tap becomes the drag, so no more than 2 taps allowed
self.state = .Cancelled
return
}
if tap.tapCount == 2 {
// We're doing the drag, so remember where we tapped
self.anchorPoint = self.locationInView(mapView)
self.state = .Began
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
guard self.state != .Failed else {
// Apple recommend you check this.
return
}
guard [.Began, .Changed].contains(self.state) else {
// Only continue if we've determined the gesture is happening, continue.
return
}
guard let mapView = self.view as? AGSMapView else {
print("You should only apply \(self.dynamicType) to an AGSMapView!")
self.state = .Failed
return
}
guard touches.count == 1 else {
// Only 1 finger
self.state = .Cancelled
return
}
if let touch = touches.first {
let prevLoc = touch.previousLocationInView(mapView)
let thisLoc = touch.locationInView(mapView)
if CGPointEqualToPoint(prevLoc, thisLoc) {
return
}
let diff = Double(thisLoc.y - prevLoc.y)
// Scale ratio is determined by taking a proportion of the screen height we've dragged, and raising by a power.
let scaleRatio = pow(1 + (self.zoomMode.translateScaleFactor(diff) / Double(mapView.frame.height)), self.scalePower)
switch self.zoomMode {
case .UpInDownOut(true), .UpOutDownIn(true):
// Zoom in/out around the tap point
mapView.zoomWithFactor(scaleRatio, atAnchorPoint: self.anchorPoint!, animated: false)
default:
// Zoom in/out around the center of the map view
let newScale = mapView.mapScale * scaleRatio
mapView.zoomToScale(newScale, animated: false)
}
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
self.state = .Ended
}
override func reset() {
self.anchorPoint = nil
}
}
//
// ViewController.swift
// DoubleTapZoom
//
// Created by Nicholas Furness on 6/17/16.
// Copyright Β© 2016 Esri. All rights reserved.
//
import UIKit
import ArcGIS
class ViewController: UIViewController {
@IBOutlet weak var mapView: AGSMapView!
let singleFingerZoomGesture = AGSSingleFingerZoomGestureRecognizer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let basemap = AGSTiledMapServiceLayer(URL: NSURL(string:"http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"))
self.mapView.addMapLayer(basemap)
self.mapView.addGestureRecognizer(self.singleFingerZoomGesture)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment