I have 2 GesturerRecognizers, one is a custom 3DTouch and another one is the single UITapGestureRegognizer. I'd like to make that if you tap once it performs a segue and if you go trought the 3dtouch one you get another view. I have tried lot of things but it is not working (i have tried with timer even, but always is 0 at start, no matters if its 3dtouch or not.) That my 3dtouch custom implementation:
//Without this import line, you'll get compiler errors when implementing your touch methods since they aren't part of the UIGestureRecognizer superclass
import UIKit.UIGestureRecognizerSubclass
//Since 3D Touch isn't available before iOS 9, we can use the availability APIs to ensure no one uses this class for earlier versions of the OS.
@available(iOS 9.0, *)
public class ForceTouchGestureRecognizer: UIGestureRecognizer {
var timer = NSTimer()
var counter = Double()
//Since it also doesn't make sense to have our force variable as a settable property, I'm using a private instance variable to make our public force property read-only
private var _force: CGFloat = 0.0
//Because we don't know what the maximum force will always be for a UITouch, the force property here will be normalized to a value between 0.0 and 1.0.
public var force: CGFloat { get { return _force } }
public var maximumForce: CGFloat = 4.0
func timerAction() {
counter += 1
}
override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
counter = 0
timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: #selector(timerAction), userInfo: nil, repeats: true)
print("COUNTER: \(counter)")
normalizeForceAndFireEvent(.Began, touches: touches)
}
override public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
normalizeForceAndFireEvent(.Changed, touches: touches)
}
override public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
print("COUNTER: \(counter)")
normalizeForceAndFireEvent(.Ended, touches: touches)
timer.invalidate()
}
override public func touchesCancelled(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesCancelled(touches, withEvent: event)
normalizeForceAndFireEvent(.Cancelled, touches: touches)
}
func normalizeForceAndFireEvent(state: UIGestureRecognizerState, touches: Set<UITouch>) {
//Putting a guard statement here to make sure we don't fire off our target's selector event if a touch doesn't exist to begin with.
guard let firstTouch = touches.first else {
return
}
//Just in case the developer set a maximumForce that is higher than the touch's maximumPossibleForce, I'm setting the maximumForce to the lower of the two values.
maximumForce = min(firstTouch.maximumPossibleForce, maximumForce)
//Now that I have a proper maximumForce, I'm going to use that and normalize it so the developer can use a value between 0.0 and 1.0.
_force = firstTouch.force / maximumForce
//Our properties are now ready for inspection by the developer. By setting the UIGestureRecognizer's state property, the system will automatically send the target the selector message that this recognizer was initialized with.
self.state = state
}
//This function is called automatically by UIGestureRecognizer when our state is set to .Ended. We want to use this function to reset our internal state.
public override func reset() {
super.reset()
_force = 0.0
}
}
And that is what i do in the view:
self.forceTouchRecognizer = ForceTouchGestureRecognizer(target: self, action: #selector(PLView.handleForceTouchGesture(_:)))
self.addGestureRecognizer(forceTouchRecognizer)
self.nowTap = UITapGestureRecognizer(target: self, action: #selector(StoryTableViewController.didTapRightNow2(_:)))
self.addGestureRecognizer(nowTap)
If I understand you correctly you want to add 2 different GestureRecognizers to the same UIView. One that detects a normal tap and one that detects a tap with force.
For the normal tap you can use a UITapGestureRecognizer. For the force tap you have to create your own custom Gesture Recognizer that monitors the force of the touch decides if the force was high enough to qualify for a force tap gesture.
Here is the custom force tap gesture recognizer:
import UIKit.UIGestureRecognizerSubclass
@available(iOS 9.0, *)
public class ForceTapGestureRecognizer: UIGestureRecognizer {
private var forceTapRecognized = false
override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
forceTapRecognized = false
}
override public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
guard let touch = touches.first else { return }
if touch.force >= touch.maximumPossibleForce {
forceTapRecognized = true
}
}
override public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
state = forceTapRecognized ? .Ended : .Failed
}
}
Then you can add both recognizers to your view and add an action for each recognizer.
To make both recognizers work at the same time you have to tell the UITapGestureRecognizer
that it should only detect a tap, when the ForceTapGestureRecognizer
did not detect a force tap. You do this with requireGestureRecognizerToFail(_:)
. If you do not set this, only the normal taps will be recognized:
class ViewController: UIViewController {
let touchView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
let touchView = UIView()
view.addSubview(touchView)
let forceTapRecognizer = ForceTapGestureRecognizer(target: self, action: #selector(ViewController.didForceTap(_:)))
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.didTap(_:)))
tapRecognizer.requireGestureRecognizerToFail(forceTapRecognizer)
touchView.addGestureRecognizer(forceTapRecognizer)
touchView.addGestureRecognizer(tapRecognizer)
}
func didTap(recognizer: UITapGestureRecognizer) {
print("tap")
}
func didForceTap(recognizer: ForceTapGestureRecognizer) {
print("force tap")
}
}