Skip to content

Instantly share code, notes, and snippets.

@128keaton
Created April 23, 2017 06:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 128keaton/346915c2211387f86be2d7813ece09a5 to your computer and use it in GitHub Desktop.
Save 128keaton/346915c2211387f86be2d7813ece09a5 to your computer and use it in GitHub Desktop.
//
// 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