How to detect "Drag Enter" on UIScrollView - ios

Imagine a UIButton and a user who taps somewhere OUTSIDE the button and then slides onto it. If I want the button to detect such touches I can register it for any "Touch Drag Enter" events.
My question: is there some way to achieve the same for a UIScrollView?
I.e., I tap somewhere outside the scrollview, drag my finger onto the scrollview and as soon as I enter the scrollviews frame it starts panning? (Because by default it doesn't, I have to start my touch INSIDE the scrollview in order to pan)

If you want to do this, you will have to do a custom implementation using -touchesBegan, -touchesMoved, and -touchesEnded
The documentation for the UIResponder class (which all ViewControllers inherit from) that allows you to do this is here.
Hopefully you can figure out what to do from here. :)
As an extra hint, you will also most likely need to use this function
bool contains = CGRectContainsPoint(CGRect rect, CGPoint point);

Imagine a UIButton and a user who taps somewhere OUTSIDE the button and then slides onto it. If I want the button to detect such touches I can register it for any "Touch Drag Enter" events
Actually, no you can't. A UIControl will not get any of the control events unless the touch started in the control.
And that's for the same reason that you are seeing a similar effect with the scroll view. This is the entire basis of touch delivery on iOS. A touch "belongs" to the view that was initially touched.
After all, the runtime cannot possibly know that you are going to drag into the scroll view...

Related

UIView: Inheriting Touch

Example 1:
When invoking 3D Touch on app icon, you are able to make selections without lifting the finger up.
Example 2
Long pressing on a keyboard key allowing you to drag in to different selections without lifting finger up.
If the app icon is the first view and the pop up is the second view, how can I transfer touch down from first to second view?
Normally, a view loses control of the touches when the fingers leave its area. But if you set isMultipleTouchEnabled to true, it will keep control over touches if the finger leave its area. If you use a button or another UIControl you can assign actions to touchDragExit, touchUpOutside or touchDragOutside etc. to handle events outside of the control.

Proper UIGestureRecognizer and Delegate design

This is a pretty hypothetical question just to understand proper design but lets say I have two custom UIViews.
One of them is essentially a container that I'll call a drawer. Its purpose is to hide and show content. It's a lot like the notification center on iOS where you swipe to pull it open and flick it back up to close it. It's a generic container than can contain any other UIView. It has a UIPanGestureRecognizer to track the finger that's pulling it open/closed. It might also have a UISwipeGestureRecognizer to detect a "flick".
The other view is a custom map widget that has UIPan/Rotation/Pinch GestureRecognizers.
I think the drawer view should be the UIGestureRecognizerDelegate for the Pan/Swipe GestureRecognizers so that it can prevent touches from being delivered unless the user is grabbing "the handle".
My first instinct is for the map to be the UIGestureRecognizerDelegate of the pan/rotation/pinch gestures so that it can allow them to all run simultaneously.
The problem I'm having is that, I really don't want the map to receive any touches or begin recognizing gestures until the drawer is completely open. I'd like to be able to enforce this behavior automatically in the drawer itself so that it works for all subviews right out of the box.
The only way that I can think to do this is to wire all of the gestures handlers to the ViewController and let it do everything, but to me that breaks encapsulation as now it has to know that the map gestures need to run simultaneously, that the drawer should only get touches on it's handle and that the map should only get touches when it's open.
What are some ways of doing this where the logic can stay in the Views where I think it belongs?
I would do something like this to make the subviews of the drawer disabled while panning. Essentially loop through the drawer's subviews and disbale interaction on them.
[self.subviews enumerateObjectsUsingBlock:^(UIView *subview, NSUInteger idx, BOOL *stop){
subview.userInteractionEnabled = NO;
}];
And something similar again for when you want to re-enable user interaction on the subviews.
This should already Just Work™. A gesture recogniser is attached to a view; when a continuous gesture is recognised, all subsequent touches associated with that gesture are associated with that view.
So in your case, when the drawer pan is recognised, no touches associated with that pan should ever cause behaviour in your map view's pan/pinch/rotation gestures (unless you explicitly specify that they should using the appropriate delegate methods).
Or do you mean that you want to prevent the user from, halfway through opening the drawer, using another finger (i.e. another gesture) to start scrolling the (half-visible) map? If so, you should just set userInteractionEnabled on the drawer's contentView (or equivalent) to NO at UIGestureRecognizerStateBegan/Changed and YES again at UIGestureRecognizerStateEnded/Cancelled.

iOS multiple pangestureRecognizer

I have a View as the baseView and I add a panRegconizer named : basePan;
then add a tableview on baseView, when scroll it to a point (assume the bottom)
I disable the scroll and userInteractionEnabled = NO; I hope the basePan could continue handle the touch events and move the tableview by pan point. but seems when disable the scroll the whole touch event is cancel by system.
any method to pass the touch to base down view or panRegconizer,
I tried to make another view(tView) on base view and add second panRegconizer on tView;
when pan on it, basePan doesn't do the job,
how could I transit the touch event to multiple regconizers, I know the touchbegin touchmove touchEnd could be passed to next responder. but nowadays , panregonizer seems easier to use and recommended by apple.

How to make UIView stop receiving touch events?

I'm working on an app where the user is expected to rapidly touch and swipe across multiple UIViews, each of which is supposed to do an action once the user's finger has reached it. I've got a lot of views and so the typical thing to do, where I'd iterate over each view to see if a touch is inside of its bounds, is a no-go - there's just too much lag. Is there any other way to get touch events from one view to another (that is beside the first one)? I thought maybe there is some way to cancel the touch event, but I've searched and so far have come up empty.
One of the big problems I have is that if I implement my touch handling in my view controller, touchesBegan only fires for the first touch - if the user touches something and then, without moving the first finger, taps on something else, that tap is not recorded in either touchesBegan or touchesMoved. But if I implement my touch handling in the UIViews themselves, once a view registers a touch, if the user does not lift their finger up and moves it, the views around the first view do not register the touch. Only if the user lifts his finger and then puts it back down will the surrounding views register the touch.
So my question is, lets say I have two views side by side, my touch handling code is implemented in the views, and I put my finger down on view 1. I then slide my finger over to view 2 - what do I need to do to make view 2 register that touch, which started in view 1 and never "ended"?
Set userInteractionEnabled property of UIView to NO.
view.userInteractionEnabled = NO;
UIView has the following property:
#property(nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled
Ok, I figured out what was going on. Thing is, I have my views as subviews of a scrollview, which is itself a subview of my main view. With scrollEnabled = NO, I could touch my subviews - but apparently the scrollview was only forwarding me the initial touch event, and all subsequent touches were part of that initial event. Because of that, I had many weird problems such as touching two views one after the other, both would select and highlight, but if I took the first finger off the screen both views would de-select. This was not the desired behavior.
So what I did is I subclassed the scrollview and overrode the touch handling methods to send the events to its first responder, which is its superview, which is the view where I'm doing my touch handling. Now it works!

iPhone: Multiple touches on a UIButton are not recognized

I have a UIButton (a subclass of one, actually) that interacts with the user via the touchesbegan: and touchesmoved: functions.
What I would like is for the user to be able to press down the button, drag their finger away, and have a second finger touch the button (all while the first finger has never left the screen).
Problem is, the second touch event never calls touchesbegan: unless the first finger has been released.
Is there some way to override this, or am I trying to do the impossible?
Have you tried setting multipleTouchesEnabled to YES?
If the interactions are using touchesbegan: and touchesmoved: then use a UIView instead of a UIButton. A button is a UIControl, and the way to interact with UIControls is
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents.
I'm not sure this two ways of getting events mix well.

Resources