Why is touchesCancelled being called after UITapGestureRecognizer? - swift

After finally finishing my coding within touchesBegan, touchesMoved, touchesEnded, touchesCancelled I started adding code for recognizing taps. I implemented it with the code below.
For some reason after every UITapGestureRecognizer, I get a touchesCancelled. Did I leave something out, is there a threshold I can modify, or do I somehow have to create my own code that says, I just did a tap, ignore the first cancel that comes along?
So essentially I get a touchesBegan, UITapGestureRecognizer, followed by a touchesCancelled.
class GameScene: SKScene, SKPhysicsContactDelegate{
let tapRec = UITapGestureRecognizer()
....
tapRec.addTarget(self, action:#selector(GameScene.tappedView(_:) ))
tapRec.numberOfTouchesRequired = 1
tapRec.numberOfTapsRequired = 1
self.view!.addGestureRecognizer(tapRec)
....
}
#objc func tappedView(_ sender:UITapGestureRecognizer) {
....
}

That's just what a gesture recognizer is / does. When a gesture recognizer recognizes its gesture, the touches for this gesture recognizer are sent to their hit-test views as a touchesCancelled(_:with:) message, and then no longer arrive at those views. The gesture recognizer takes over so it signs the view off from its touch sequence in good order.
If you're going to have both a gesture recognizer and manual touch events, you have to do more work to mediate between them. If you really don't want the standard behavior, you can set the gesture recognizer's cancelsTouchesInView property to false.

Related

Postponing gesture recognizer in swift (UISwipeGestureRecognizer)

I have a swipe gesture recognizer that I turn off after the user swipes. That’s when I have a series of animations. 1st, a view will move in the direction that the user swipes. Then based on the location that it stops, another animation occurs based on the location that it stopped in. I then turn on the gesture recognizer all the way at the end of the function that handles this swipe. The problem is that the recognizer is turning on too quickly and therefore if the user were to quickly swipe in another direction, the animation would only occur in the wrong place (due to the fact that the view is in a different place). My question is, how can I create a function that waits X amount of time and then turns back on the recognizer?
// 1
var toDoSmth: (()->void)? = nil
// 2
toDoSmth = {
// turns back on the recognizer
// and do any delayed task
}
// 3
let delayedTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: delayedTime) {
toDoSmth()
}
Here: 1, 2 - created variable/autoclosure;
3 - run your code after 1 second.

Pull to refresh in Apple Watch app in Swift

I see a pull to refresh in the Apple Watch mail app, but I don't see how to implement such a scenario. Kindly give some insight into how to achieve pull to refresh in Apple Watch app in Swift.
Thanks...
You should add a pan gesture recognizer to your Interface Controller and then you can write the code for refreshing your data.
You have to add the gesture recognizer to your InterfaceController in InterfaceBuilder, then connect an Action to it. Have a look at Apple's WKInterfaceCatalog example code, especially the GestureDetailController class and Gestures scene in Interface builder.
You can decide to use a Swipe gesture recognizer or Pan gesture recognizer.
The IBAction for Swipe gesture recognizer is:
#IBAction func swipeRecognized(_ sender: AnyObject) {
//update your UI
}
And for the Pan gesture recognizer:
#IBAction func panRecognized(_ sender: AnyObject) {
if let panGesture = sender as? WKPanGestureRecognizer {
//Update your UI
}
}

Pop view controller using Screen edge pan gesture recogniser not following the thumb

As soon as I've added custom bar navigation bar button item I've lost the ability to use the default function to go back. I'd like to have the ability to use "swipe from edge" to go back.
I've added the Edge Pan Gesture Recogniser and connected it to #IBAction, but the dismissing action happens completely as soon as the pan gesture is recognised.
Instead of slowly following my thumb (as seen in other apps), the current view moves out with predefined animation.
How to make the animation following my thumb using Edge Pan Gesture Recogniser?
#IBAction func edgeSwipe(sender: AnyObject) {
navigationController?.popViewControllerAnimated(true)
}
There's no need to add Edge Pan Gesture Recogniser. Following #beyowulf's suggestions I've been able to implement the swipe to go back feature that behaves the same way as the default system implementation does - the views edge follows my thumb as I swipe it to dismiss it.
So I've removed the ScreenEdge Pan Gesture Recogniser from the storyboard and also removed the related #IBAction.
I've made my first view controller to be the delegate for interactivePopGestureRecognizer. Here's the code:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.interactivePopGestureRecognizer?.delegate = self
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
return navigationController?.viewControllers.count > 1 ? true : false
}
}

Find out the view of the gesture has been set

In my SecondViewController I have a UITableView with a custom UITableViewCell where I have a UIPanGestureRecognizer and I want it to fail when otherGestureRecognizer is a UIPanGestureRecognizer from ViewController FirstViewController
The UIPanGestureRecognizer of the the cell is set to self and I tried using gestureRecognizer: shouldRequireFailureOfGestureRecognizer:
override func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
let view = otherGestureRecognizer.view
if let view as? FirstViewController.view { // Obviously doesn't work
return true
}
return false
}
The question is, how can I fail the UIPanGestureRecognizer of the UITableViewCell when a gesture has been recognized from FirstViewController?
This may seem silly, but it seems to me your problem is purely one of identification: Is this gesture recognizer, otherGestureRecognizer, the particular gesture recognizer I'm worried about and am supposed to yield to? What occurs to me immediately are two choices:
You have, as you rightly point out, its view. Are there no questions you can ask about this view that would help you identify it? Has it a distinguishing backgroundColor or any other feature that would help here? What about its class? Is it a plain vanilla UIView, or is it of some distinguishing class?
You also have the UIPanGestureRecognizer itself. So a drop-dead simple solution, which is what I'd probably use, is to subclass UIPanGestureRecognizer: let's call the subclass MySpecialPanGestureRecognizer. This subclass has no special functionality, and no purpose except to act as an identifier! When you give the view its gesture recognizer, make that gesture recognizer a MySpecialPanGestureRecognizer. Now you can ask whether otherGestureRecognizer is MySpecialPanGestureRecognizer.

When I call setLeftBarButtonItem,something like "Screen Edge Pan Gesutre Recognizer" just can not work

I know it is a feature of iOS7 added to UINavigationController to pop current ViewController by panning from the screen's left edge. And I found there is a "Screen Edge Pan Gesture Recognizer" in Object Library. But when I implement it by code, its behavior is slightly different from the previous one.
I want to know why this behavior just gone when I call setLeftBarButtonItem method. Hopes someone could help me.
Finally I found the solution to my problem.
class MyViewController : UIGestureRecognizerDelegate {
override func viewDidLoad() {
self.navigationItem.setLeftBarButtonItem(backButtonItem, animated: true) // disable the gesture recognizer
// the magic code
self.navigationController?.interactivePopGestureRecognizer.delegate = self
}
}