How to recognise gesture in superview? - iphone

I have a view (parent) and a subview (child). The child is a UIControl, responding to UIControlEventTouchDownInside, the parent has a swipe recogniser. I would like to catch swipes even if they start in the child.
Question: how can I recognise the swipe before the tap? Is there any way to tell iphone that gestures in the parent come before gestures in the child?
Thanks for your help!
Edit
I just changed the child to be a UIControl (instead of using a tap recogniser). I'm not sure this matters much to the answer to this question but I thought I'd mention it anyway.
Edit 2
In response to the two answers I have added the tap recogniser to the child again and tried to delay (and fail) the tap recognition so that I can swipe across the big view (parent) containing the child. No luck so far.
Edit 3
I would really like to keep the child a UIControl and use UITouchDownInside rather than a tap recogniser because I want to use the down event rather than the up event.
Edit 4
Now the swipe gets detected but in the child, none of the following gets detected:
UIControlEventTouchDragOutside, UIControlEventTouchDragExit , UIControlEventTouchCancel
and I need at least one of these to detect when the user doesn't actually mean to tap on the child : /

Check the - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer in UIGestureRecognizer. To tap, the swipe gesture has to fail.

You can use the [swipeGesture delaysTouchesBegan] message to delay the touch events being delivered to the child control.

Related

Swipe gesture recognizer not working in vertical directions

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

How to create iOS UiView that combines multiple buttons next to each other and enables swiping to selecting all of them

Right now i have a few buttons next to each other like this:
User has to press all of them to select the whole row (i have a few rows).
It would be natural to enable them to swipe over them to select them all.
I also need to get information about how many buttons were selected.
Could you please help me how to achieve this? Do i have to create my own view or implement some gesture listeners?
Depending on what you exactly trying to achieve there are 2 easy ways :
Simplest : add a UISwipeGestureRecognizer to your view (or to a view with only the 4 buttons on it) and implement delegate interface within your view controller.
When a swipe (in the good direction) is done, you receive an event and you perform your "unlock".
UISwipeGestureRecognizer extends UIGestureRecognizer (doc here) :
Clients of gesture recognizers can also ask for the location of a
gesture by calling locationInView: or locationOfTouch:inView:.
So with that + implementing UIGestureRecognizerDelegate (doc here) you should be able to check exactly what you want.
If you want to manually track the user finger you can just extend UIView and implement :
touchBegan
touchMoved
touchEnded
You can here have the full control (CGRectContainsPoint() will be useful).
Edit :
Something important you have to keep in mind to make your choice is speed. If the user can select the row even with a slow and non-linear gesture (put the finger on the first button then > slowly go to second > go to third > back to second > go to last for instance), you should extends UIView and on the touch began make the first image highlight and then follow the finger until the touchEnd on the last button. It should be more user friendly (depending on what you are trying to achieve of course).
Check out this open source project Swipe-to-Select Gridview

How to put UIGestureRecognizer in front if several views with gestures overlaps

I have two views that have tap gesture recognizers attached. The first view accepts touches while the second one is hidden. After some time, I'm making the second (with attached gesture) view visible. The problem is that, still, only the touches of the first view are handled, despite the second view is in front :(
Sorry, forgot to put "user interaction enabled". I'm drawing my views and other stuff with interface builder, therefore I have missed that :)

Can I cancel touch programmatically?

UIScrollView has a canceling mechanism, where it cancels the touch of subviews when it detects 'scrolling'.
I wonder if I can cancel a touch event (which already has begun) programmatically.
I have a draggable view inside a scroll view.
I can let the draggable view receive touches, but I'm wondering how to stop receiving touch events when I want and give touch events to the scroll view.
Have a look at cancelTrackingWithEvent:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIControl_Class/index.html#//apple_ref/occ/instm/UIControl/cancelTrackingWithEvent%3A
You might want to have a look at the UITapGestureRecogniser class's cancelsTouchesInView method
Well I have had a similar problem.
I have solved it by disabling the scroll view when you receive touch event on its subview.
And when you think your event is complete you re-enable the scroll view.
If you do not do enable-disable the scroll view cancels your drag events automatically!

I have a UI Scroll View with UIButtons on top. How do i make it scroll when they touch and drag the buttons?

I want something similar to how the iPhone homescreen works. I have my scrollview inside my main view, which scrolls to the right, but when you touch on the UIButtons, they become highlighted and dragging means they don't end up getting pressed but also the scrollview doesn't end up scrolling. It doesn't do this on the homescreen. How can i make it similar to this?
A touch-down-drag is a completely different event. Apple doesn't support this directly—you'd have to code it in—start tracking the touches using the gesture responders as soon as a touch-down-inside is detected, then scroll the same amount, and stop scrolling at a touch-up-outside (or inside). This would most likely fall under the category of unusual use of interface elements, and could very well get you rejected.
I think this is a better answer to the question:
UIScrollview with UIButtons - how to recreate springboard?