Created
April 23, 2017 06:28
-
-
Save 128keaton/346915c2211387f86be2d7813ece09a5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DeepPressGestureRecognizer.swift | |
// DeepPressGestureRecognizer | |
// | |
// Created by SIMON_NON_ADMIN on 03/10/2015. | |
// Copyright © 2015 Simon Gladman. All rights reserved. | |
// | |
// Thanks to Alaric Cole - bridging header replaced by proper import :) | |
import AudioToolbox | |
import UIKit.UIGestureRecognizerSubclass | |
// MARK: GestureRecognizer | |
@available(iOS 9.0, *) | |
class DeepPressGestureRecognizer: UIGestureRecognizer | |
{ | |
var vibrateOnDeepPress = false | |
let threshold: CGFloat | |
private let pulse = PulseLayer() | |
private var deepPressed: Bool = false | |
required init(target: AnyObject?, action: Selector, threshold: CGFloat) | |
{ | |
self.threshold = threshold | |
super.init(target: target, action: action) | |
} | |
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) | |
{ | |
if let touch = touches.first | |
{ | |
handleTouch(touch: touch) | |
} | |
} | |
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) | |
{ | |
if let touch = touches.first | |
{ | |
handleTouch(touch: touch) | |
} | |
} | |
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) | |
{ | |
super.touchesEnded(touches, with: event) | |
state = deepPressed ? UIGestureRecognizerState.ended : UIGestureRecognizerState.failed | |
deepPressed = false | |
} | |
private func handleTouch(touch: UITouch) | |
{ | |
guard let view = view, touch.force != 0 && touch.maximumPossibleForce != 0 else | |
{ | |
return | |
} | |
if !deepPressed && (touch.force / touch.maximumPossibleForce) >= threshold | |
{ | |
view.layer.addSublayer(pulse) | |
pulse.pulse(frame: CGRect(origin: CGPoint.zero, size: view.frame.size)) | |
state = UIGestureRecognizerState.began | |
if vibrateOnDeepPress | |
{ | |
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) | |
} | |
deepPressed = true | |
} | |
else if deepPressed && (touch.force / touch.maximumPossibleForce) < threshold | |
{ | |
state = UIGestureRecognizerState.ended | |
deepPressed = false | |
} | |
} | |
} | |
// MARK: DeepPressable protocol extension | |
protocol DeepPressable | |
{ | |
var gestureRecognizers: [UIGestureRecognizer]? {get set} | |
func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer) | |
func removeGestureRecognizer(gestureRecognizer: UIGestureRecognizer) | |
func setDeepPressAction(target: AnyObject, action: Selector) | |
func removeDeepPressAction() | |
} | |
@available(iOS 9.0, *) | |
extension DeepPressable | |
{ | |
func setDeepPressAction(target: AnyObject, action: Selector) | |
{ | |
let deepPressGestureRecognizer = DeepPressGestureRecognizer(target: target, action: action, threshold: 0.75) | |
self.addGestureRecognizer(gestureRecognizer: deepPressGestureRecognizer) | |
} | |
func removeDeepPressAction() | |
{ | |
guard let gestureRecognizers = gestureRecognizers else | |
{ | |
return | |
} | |
for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer | |
{ | |
removeGestureRecognizer(gestureRecognizer: recogniser) | |
} | |
} | |
} | |
// MARK: PulseLayer | |
// Thanks to http://jamesonquave.com/blog/fun-with-cashapelayer/ | |
class PulseLayer: CAShapeLayer | |
{ | |
var pulseColor: CGColor = UIColor.red.cgColor | |
func pulse(frame: CGRect) | |
{ | |
strokeColor = pulseColor | |
fillColor = nil | |
let startPath = UIBezierPath(roundedRect: frame, cornerRadius: 5).cgPath | |
let endPath = UIBezierPath(roundedRect: frame.insetBy(dx: -50, dy: -50), cornerRadius: 5).cgPath | |
path = startPath | |
lineWidth = 1 | |
let pathAnimation = CABasicAnimation(keyPath: "path") | |
pathAnimation.toValue = endPath | |
let opacityAnimation = CABasicAnimation(keyPath: "opacity") | |
opacityAnimation.toValue = 0 | |
let lineWidthAnimation = CABasicAnimation(keyPath: "lineWidth") | |
lineWidthAnimation.toValue = 10 | |
CATransaction.begin() | |
CATransaction.setCompletionBlock | |
{ | |
self.removeFromSuperlayer() | |
} | |
for animation in [pathAnimation, opacityAnimation, lineWidthAnimation] | |
{ | |
animation.duration = 0.25 | |
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) | |
animation.isRemovedOnCompletion = false | |
animation.fillMode = kCAFillModeForwards | |
add(animation, forKey: animation.keyPath) | |
} | |
CATransaction.commit() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment