UIScrollView pass event to child chain on WillEndDragging - ios

Edit: I am editing my initial question (see below for history) as I am getting new information.
I figured out that when the swipe motion starts from inside the button bounds, we never receive TouchesEnded or TouchesCancelled, only TouchesMoved. However, if I can react on WillEnddragging, it would be great. Is it possible to cancel a gesture on WillEndDragging and also pass this cancel down the children chain?
History:
I am using Xamarin Forms and I have the following issue
I have custom controls part of native scrolling views, like ScrollView or CollectionView, that remain in "clicked" state after the finger enters them but then initiates a scroll gesture.
I had a similar issue on UWP in the past and managed to solve it with the UIElement.PointerCaptureLost event.
Sorry if I am wasting your time on trivial stuff, but I am really stuck and I greatly appreciate your help.
I have tried different approaches suggested, including setting DelaysContentTouches to NO, and playing around with CanCancelContentTouches and overriding TouchesShouldCancelInContentView to always return NO, in a ScrollView custom renderer.
I have had a read of
Allow UIScrollView and its subviews to both respond to a touch
and
UIScrollView sending touches to subviews
Maybe the accepted answer here helps, but I am not sure how to get the tag of my custom view.
What I am expecting is my custom controls to receive the cancelled touch event (or something similar) as happens in both Android and Windows

This was easier than it looked. Solved by adding a UIGestureRecognizerDelegate to my UIGestureRecognizer class and in the delegate I overwrote ShouldRecognizeSimultaneously to return true.

Related

iOS - Superview gesture recognizer getting called when not wanted, can't cancel child view touch event

I have a super view that has a UITapGestureRecognizer on it. It allows touches within the view because there are clickable items within the view.
When these items are clicked on, I want to take a specific action, not the generic one that covers the entire superview. Unfortunately in my TouchDown event of my child control I don't know how to stop the event here. I know I could create a kludge flag, but this seems like the wrong way to go.
Any advice?
James
OK I got a solution. Totally my problem. I was playing around with trying to get all touches to work and at one point I had set cancelTouchesInView = true on the UITapGestureRecognizer superview. While this didn't stop the other touches from happening, for whatever reason the touches carried through to the superview as well. I understand that this explanation probably makes no sense, but that's what did it. Still trying to wrap my head around how iOS does touch.

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.

How to detect touch while presenting views with gesture recognizers?

I have been developing collage like application. In which user can add images, they can scale, move and rotate them. Whenever, I am trying to drag an image which is on top but added earlier than the one behind it, it is not receiving the gesture, rather than the one behind it which is added later gets detected and brought into front. How to resolve this issue?
I am Using this to remove the gesture in second view...
[imageView1 removeGestureRecognizer:GestureInView2];
This are the delegate method of UIGestureRecognizer you can use for your different requirement.
touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
I didn't understand your issue perfectly, But.
I think you have to refer for more detail according to your requirement - https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html.
I have done it. We need to change the index of subviews if you want to make them receive touches. Changing zposition alone will not help to make the view to receive touches.

Presentation overlay for iOS

When I demo my touch apps to remote teams the people on the other end dont know where I am touching. To remedy this, I have been working on an event intercepting view/window that can display touches over applications. No matter how may variations on nextResponder I call, I am unable to react to the touch and pass it along to the controllers underneath. Specifically scroll views dont react nor do buttons.
Is there a way to take an event, get its position, then pass it along to what ever component would have been responding to it initially (the controller underneath)?
Update:
I am making some progress with a UIView. The new view is always returning NO to pointInside.This works great for when the touch starts, but it doesnt track moves or releases. Is there a strategy to adding gesture recognizers to the touch in order to track its event lifecycle?
Joe
You could try creating your own subclass of UIApplication that overrides sendEvent:. Your implementation should call [super sendEvent:event] as well as process the event as needed.
Update your main.m and pass the name of you custom UIApplication class as the 3rd parameter to the call of UIApplicationMain.
After some more due diligence, I found my oversight. In the layer that was on top and displaying the touches, user interaction needed to be set to false. Once I set that to false, I was able to use that layer for display while catching events on the layers below. The project still isn't done but I am one step closer.
Take care,
Joe

a last touch event does not come if the view goes out of the screen programmatically

I have a situation where I apply an effect to a UIView when a touch begins and reverse that effect when that touch ends. So basically I am tracking touchesbegan, touchesEnded and touchesCancelled methods of UIView.
But the problem is that when the view goes out of the screen, i.e. when it or one of its parents gets removed from superview, it does not get any more touch events. Is there any way to give this "last" touchesended event to the view? Maybe if the UIView gets notified about being invisible, I can also use this event for that purpose.
Ok I am going to move the answers in comments to original question to make a good summary of important points.
The reason I am tracking touch events is that I want to apply some
nice effects such as glowing on touch start and remove those effects
on touch ending.
The reason why I can not simulate touchesEnded on removing those
views is that I do not directly remove them. Instead I remove one of
the ancestor views of them. I can not keep track of ancestor views
all the way to UIWindow, it is technically impossible I think.
Instead, framework should provide this to as an event I think.
I solved my problem by overriding -(void)willMoveToWindow:(UIWindow *)newWindow method and checking if newWindow is nil.

Resources