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");
}
}
Created
April 27, 2019 00:35
-
-
Save CreatureSurvive/ac1bc57be6a7a07682abc25f5cd300a3 to your computer and use it in GitHub Desktop.
A UIGestureRecognizer subclass that handles Force Touch events.
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
// | |
// 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 |
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
// | |
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment