Swipe gesture recognizer not working in vertical directions - iphone

guys, I'm totally new to Objective C and XCode, so don't be too harsh on me for such a question. In fact, this is my first question on StackOverflow ever. The problem is the following:
I need functionality for closing the keyboard when tapping or scrolling anywhere outside the textField that is currently first responder.
I managed to implement it for tap and horizontal swipes with the following code (right swipe example):
UISwipeGestureRecognizer *swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(closeKeyboard:)];
swipeGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.tableView addGestureRecognizer:swipeGestureRecognizer];
swipeGestureRecognizer.cancelsTouchesInView = NO;
And in fact, later I figured out how to do it using solely interface builder. But vertical swipes just would not work, neither when I do them programmatically, nor through the IB. I suspect that the problem is that my tableView scrolls vertically on the screen, thereby preventing the call of the swipe, but I still have no idea on how to overcome this issue.
I would greatly appreciate your help! Thanks.

I saw, that you are trying to add this vertical gesture to a tableView. So you don't need to add a vertical swipe gesture . You have to implement UIScrollViewDelegate protocol, and implement the -scrollViewDidScroll: or -scrollViewWillBeginDragging: methods. In these methods you can catch if your tableview scrolling, and if your tableView is scrolling, call your closeKeyboard method

the UITableView basically uses UIScrollView & when you implement the UITableView delegate, you also implement the UIScrollView delegate (because behind the scenes, the UITableViewDelegate has implemented the UIScrollViewDelegate)
You do not have to manually implement the UIScrollViewDelegate & you can simply start using any UIScrollView delegates you want such as the -(void)scrollViewDidScroll:
But if you want vertical swipes to work on the tableview then it can be done in the following dirty & totally buggy technique. (Basically this is how NOT to do it)
MAIN property is setDelaysContentTouches on your tableView object to NO.
setDirection on your UISwipeGestureRecognizer object to detect vertical up & vertical down swipes.
Set the swipe gesture onto the tableView object.
example:
[self.tableView setDelaysContentTouches:NO];
UISwipeGestureRecognizer *mySwipe = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(closeKeyboard:)];
[mySwipe setDirection: UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown];
[self.tableView addGestureRecognizer:mySwipe];
Finally, you'll get it to work but it works real bad.
Most of the times, the swipe is ignored since you need to swipe
really really fast and cover a decent distance within the swipe action.
When the swipe works, the table does not scroll.
So, as you see, it's really sad and dirty thing to do.
The reason is that the scrollView handles the content touches in a very specific way.
To decide if the touch is to be handled or to be forwarded, the
UIScrollView starts a timer when you first touch it:
If you haven't moved your finger significantly within 150ms, it passes the event on to the inner view.
If you have moved your finger significantly within 150ms, it starts scrolling (and never passes the event to the inner view). Note:
how when you touch a table (which is a subclass of scroll view) and
start scrolling immediately, the row that you touched is never
highlighted.
If you have not moved your finger significantly within 150ms and UIScrollView started passing the events to the inner view, but then
you have moved the finger far enough for the scrolling to begin,
UIScrollView calls touchesCancelled on the inner view and starts
scrolling. Note: how when you touch a table, hold your finger a
bit and then start scrolling, the row that you touched is highlighted
first, but de-highlighted afterwards.
These sequence of events can be altered by configuration of
UIScrollView:
If delaysContentTouches is NO, then no timer is used — the events immediately go to the inner control (but then are canceled if you move
your finger far enough)
If cancelsTouches is NO, then once the events are sent to a control, scrolling will never happen.
Note that it is UIScrollView that receives all touchesBegin,
touchesMoved, touchesEnded and touchesCanceled events from CocoaTouch
(because its hitTest tells it to do so). It then forwards them to the
inner view if it wants to, as long as it wants to.
source: https://stackoverflow.com/a/844627/2857130

Related

Two finger swipe on a UITableView

I am using a UITableView in my application to show a set of images under each section in a UITableView.I have a requirement which makes the UITableView to scroll to next section if the swipe is done with two fingers.
Can some one help me in making the UITableView respond to two finger swipe.I tried by assigning a swipe gesture with number of touches assigned to two but the table responds to normal traditional scroll.
Can someone suggest a way to implement this.If possible with pieces of code which is well appreciated.
TNQ
[swipeGesture setCancelsTouchesInView:TRUE];
This will make the gesture recognizer swallow the touches.
Hope this helps.
Cheers!

Using UISwipeGestureRecognizer and drawing code

I need a control that allows user to
1) draw on it
2) swipe to go to next screen (through an event or a delegate)
I've tried to add UISwipeGestureRecognizer to the view but it didn't work the way I wanted. My UI setup is like this:
Main Controller:
view (with UISwipeGestureRecognizer)
subview (owned by another controller that captures touch events and draws the graphics)
Whenever I try to draw a horizontal line on the canvas, the UISwipeGestureRecognizer takes over and fires the "go to next screen" event.
How can I prevent UISwipeGestureRecognizer from doing that? I am thinking about differentiating horizontal line vs swipe based on the duration/length but UISwipeGestureRecognizer does not support anything like that.
It sounds to me like a pretty confusing user experience, but if you're determined to do this, you'll probably need to subclass UIGestureRecognizer and tune it to recognize exactly the type of swipes you care about.

UIScrollView with embedded UIWebView not scrolling after holding

I have a UIWebView which is embedded in a UIScrollView. The webView is resized so that the scroll view manages all the scrolling (I need control over the scrolling).
In the webView I have disabled userSelection via '-webkit-user-select: none;'
Everything is working fine except one annoying detail. When I hold down my finger on the content before starting to scroll for about a second the scrollView won't scroll. My best guess is, that it has something to do with userSelection. The time is about the same it usually takes for the copy/paste/magnifying-thing to appear which usually disables scrolling as well.
I am running out of ideas on how to solve this. Every help would be greatly appreciated!
Thanks!
EDIT: Another aspect of the problem is, that the non-scrolling actually triggers JS-Eventhandler (click, mousedown, mouseup) inside my webView which leads to surprising app behavior. The user puts her finger down, waits, scrolls, nothing happens, removes her finger and this is perceived as a click, which feels wrong from a users perspective.
I would guess what is happening is that after that short duration, the scrollview is no longer interpreting the touch as being on it's view and instead passes the touch down to it's content views.
Have you tried delaying the content touches for the scrollview? This will essentially tell the scrollview to delay taking action on the touch event and instead to briefly monitor the touch and if the touch moves then it recognizes it as a swipe gesture for scrolling. If it doesn't move, it will eventually pass the touch along to it's subviews.
scrollView.delaysContentTouches = YES;
I think even then, there is a standard delay time before the scrollview will pass the touch events along the responder chain. If you hold for too long, it's going to naturally perceive it as being a press down event rather than a scroll event.
This question is not relevant anymore. As of iOS 5.0 the UIWebView is based on a real UIScrollView and also exposes that UIScrollView via a property. Use that instead.
And don't mess with UIWebViews embedded in UIScrollViews anymore. The documentation explicitly advises against that.
Relevant Documentation

iPhone - Catch horizontal swipes -before- subview

I have a view, that is able to go back to the previous view.
Let's say this is a questionnaire. So my main view is the questionnaireView(Controller), and it has a subview which shows a question with 2 possible answers. When one answers the question, one presses next, and the questionnaireView(Controller) shows the next question in that particular subview. Simple, right?
Okay, now imagine having to accomodate up to 10 anwers for a particular question. This will require implementing a scrollview in the subview, to accomodate for the question + all answers.
Now my question: I want questionnaireView(Controller) to receive notice of horizontal swipes (next/previous question), but I want all other touches (taps, for the radiobuttons of the answers, and vertical swipes for the scrollview) to go through...
Any idea's how to go about this? I have tried about 5 different approaches and broke my head and motivation on each on of 'em :/
Last one (and most simple one), was simply adding another subview to questionnaireView(Controller), overlaying the question+answer view.
This kindly catches all the touchesBegan/Moved/Ended/Cancelled for me, but even if I just put a forward in -each- of thoses methods ([self nextResponder] ...) the underlying view (with the answers, and the scrollview) won't respond anymore...
I'm kinda lost on this, and am thinking of writing my own Scrollview someday, since UIScrollView is the most terrible monster faced by iPhone devvers :P
Mind you, I am supporting iPhone OS 3.0 and up, so the new gesture APIs are no-go.
I'm not sure what you mean by "a forward in -each- of thoses methods". I'm not sure that nextResponder is the correct thing to forward them to either.
The problem is that touches are supposed to be "owned" by a single view throughout their lifetime. I'm not sure how UIScrollView or gesture recognizers are implemented, but I'm pretty sure they do more than you're supposed to do on your own.
I'd override questionnaireView's hitTest:withEvent: to return self. In your touchesBegan:withEvent:, call [super hitTest:[touch locationInView:self] withEvent:event] and store the subview that "owns" it. In touchesMoved:withEvent:, forward the touch to the relevant subview, but if you detect a gesture, "forget" the subview that owns the touch and instead call touchesCancelled:withEvent:. In touchesEnded/Cancelled:withEvent:, forward it to the subview and then forget the owning subview.
It's icky, but it mostly works. Some things it gets wrong (from the perspective of subviews):
-[UIEvent touchesForView:] will return the QuestionnaireView.
UITouch.view will return the QuestionnaireView
UITouch.phase might not be UITouchPhaseCancelled in touchesCancelled:withEvent: (if you detect the gesture and cancel the touch).
*

Animating Reloading of UITableView

I am trying to animate table rows in a UITableView in an iPhone project as I swipe across the screen to reload the data.
When I disable animations and only call reloadData, table continues responding to swipe gestures.
When I add animations with the reloadSections:WithRowAnimation: method, table stops responding to swipes, and only the navigation bar at the top responds to swipes.
Another change is that, table starts responding to selection and I have to manually disable it again. I suspect these two issues might be related.
I am using the swipe detection code over here btw: 1
My code for managing the gesture was in a UIView which contained another UIView which in turn contains the UITableView. It turns out that the user interaction of the UIView which is the superview of the UITableView was Enabled. Setting it to disabled caused the gesture to move up in the responder chain, and solve the problem.