Skip to content

Instantly share code, notes, and snippets.

@CreatureSurvive
Created April 27, 2019 00:35
Show Gist options
  • Save CreatureSurvive/ac1bc57be6a7a07682abc25f5cd300a3 to your computer and use it in GitHub Desktop.
Save CreatureSurvive/ac1bc57be6a7a07682abc25f5cd300a3 to your computer and use it in GitHub Desktop.
A UIGestureRecognizer subclass that handles Force Touch events.
//
// ForceTouchGestureRecognizer.h
// TouchFlow
//
// Created by Dana Buehre on 4/26/19.
// Copyright © 2019 CreatureCoding. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface CSForceTouchGestureRecognizer : UIGestureRecognizer
@property(nonatomic, readonly, assign) CGFloat force;
@property(nonatomic, assign) CGFloat triggerForce;
@property(nonatomic, assign) BOOL triggerOncePerTouch;
@end
NS_ASSUME_NONNULL_END
//
// ForceTouchGestureRecognizer.m
// TouchFlow
//
// Created by Dana Buehre on 4/26/19.
// Copyright © 2019 CreatureCoding. All rights reserved.
//
#import "CSForceTouchGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <AudioToolbox/AudioToolbox.h>
@implementation CSForceTouchGestureRecognizer {
CGFloat _maximumForce;
BOOL _recognized;
}
#pragma mark Initialization
- (instancetype)init {
if ((self = [super initWithTarget:nil action:nil])) {
[self _applyDefaults];
}
return self;
}
- (instancetype)initWithTarget:(id)target action:(SEL)action {
if ((self = [super initWithTarget:target action:action])) {
[self _applyDefaults];
}
return self;
}
#pragma mark UIResponder overrides
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self triggerWithState:UIGestureRecognizerStateBegan touches:touches];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self triggerWithState:UIGestureRecognizerStateBegan touches:touches];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self triggerWithState:UIGestureRecognizerStateBegan touches:touches];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
[self triggerWithState:UIGestureRecognizerStateBegan touches:touches];
}
#pragma mark UIGestureRecogniser overrides
// prevent blocking other gestures
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer {
return NO;
}
// reset values when the gesture resets
- (void)reset {
[super reset];
_force = 0;
_recognized = NO;
}
#pragma mark Implementation
// determine the force of this touch and set the gesture state
- (void)triggerWithState:(UIGestureRecognizerState)state touches:(NSSet<UITouch*> *)touches {
if (!touches || !touches.count)
return;
UITouch *touch = touches.allObjects.firstObject;
// normalize the force to a usable value (0.0 - 1.0)
_force = touch.force / MIN(touch.maximumPossibleForce, 4.0f);
// set the state as Recognized when the force is greater than the triggerForce and conditions are appropriate
if (((_triggerOncePerTouch && !_recognized) || (!_triggerOncePerTouch && !_recognized)) && _force >= _triggerForce) {
[self _generateFeedback];
self.state = UIGestureRecognizerStateRecognized;
} else {
_recognized = NO;
self.state = state;
}
}
#pragma mark Internal
// apply default settings
- (void)_applyDefaults {
[self setCancelsTouchesInView:NO];
_triggerForce = 0.5f;
_triggerOncePerTouch = YES;
}
// play feedback
- (void)_generateFeedback {
if (@available(iOS 10.0, *)) {
UIImpactFeedbackGenerator *generator = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
[generator prepare];
[generator impactOccurred];
generator = nil;
} else {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
}
@end

usage

ForceTouchGestureRecognizer *forceGesture = [[ForceTouchGestureRecognizer alloc] initWithTarget:self action:@selector(handleForceTouchGesture:)];
forceGesture.triggerForce = 0.8f; // <optional> the force at which the gesture is recognized, defaults to 0.5f
forceGesture.triggerOncePerTouch = YES; // <optional> ensure the gesture is not triggered continuously while holding down the touch, defaults to YES
[self addGestureRecognizer:forceGesture];


- (void)handleForceTouchGesture:(ForceTouchGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateRecognized) {
        NSLog(@"Force Touch Recognized");
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment