Disable touching view through subView - ios

I'm trying to make my first game using SpriteKit + Swift.
The problem I'm trying to solve right now is that if I add to my main SKView a SUBVIEW without any buttons just with a background color and size and touch it, my main view handles this touch like there is no subview at all.
So the subView of type UIView like doesn't exist for UITapGestureRecognizer of parent view. And the only way I found to solve it, is to put a subView-sized button on the subview without text and handler. But this way looks creepy...

It could be that your subView (or one of its parents views) has the userInteractionEnabled set to false. UIView has the userInteractionEnabled set to true by default, but for example UIImageView (which is a subclass of UIView) has the userInteractionEnabled set to false by default.
When set to NO, user events—such as touch and keyboard—intended for
the view are ignored and removed from the event queue. When set to
YES, events are delivered to the view normally. The default value of
this property is YES.
(...)
Note
Some UIKit subclasses override this property and return a different
default value. See the documentation for that class to determine if it
returns a different value.
See Apple docs for more info.
It could be not your subView by itself, but one of its parents views too, because this setting is propagated down.
If set to NO, this view (along with its subviews) is excluded from
receiving touches. Touches on this view or one of its subviews "fall
through" to a view behind it.
source: Programming iOS by Matt Neuburg
For more, check On iOS, if a superview's userInteractionEnabled is NO, then all subviews are disabled as well?

Related

Receiving touch events in hidden UIView?

Is it possible in some way to receive and respond to touch events from a UIView that is not visible. I tried -- as well as read from the Apple docs -- that simply setting 'hidden' will not work. I'm looking for an alternative to achieve the same effect.
A hidden view disappears from its window and does not receive input events. It remains in its superview’s list of subviews, however, and participates in autoresizing as usual. Hiding a view with subviews has the effect of hiding those subviews and any view descendants they might have. This effect is implicit and does not alter the hidden state of the receiver’s descendants.
if a view has set userInteractionEnabled to YES it will be touchable when hidden. Try to experiment with UIButton for example.
Make it transparent, not hidden (with alpha set to 0). If that doesn't work, make it nearly transparent (alpha set to 0.1).

How to handle tap out of custom view frame

I have a custom view with custom popup on it (added as subview).
When I'm opening this popup it's frame goes out of parent view frame.
And I can't handle user interaction on the outside popup view.
How can I fix it?
I thought about this plan:
1. Add custom view on superview;
2. Add custom popup on superview (right the position where it should be on custom view)
But i think it isn't right way.
Any suggestions?
To be honest I haven't tried it yet, but you might have luck with adding a custom view on the superview and overriding pointInside:withEvent:.
I'm thinking the superview is recognizing that the tap doesn't occur inside its own frame, so it won't even check its subviews. But if you override pointInside:withEvent: you can check that the tap location is inside the subviews frame regardless of whether it's inside the superview's frame.
If that doesn't work, you might have to override hitTest:withEvent: also (or instead of). I can try to work out an example if you need more direction than that.
This Technical Q&A might help you out https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
What you mention is actually the right approach: you should have a superview (it can be transparent (clear color)) that contains both the custom view and the popup inside of its frame.
On iOS, touch events are sent to a receiver at a low level in your visual hierarchy (I believe this is your Window to be specific). This receiver calls HitTest on its subviews to figure out which one to forward the event to. This happens recursively until the HitTest fails on all of a view's subviews. Then the parent of those subviews handles (or doesn't handle) the touch event.
But before the HitTest implementation even starts calling HitTest on the subviews, it calls PointInside on the superview. If PointInside returns false for the touch point, then HitTest returns null.
So overriding PointInside on the class that you call SuperView may solve the problem you're facing, but not necessarily. You may need to override PointInside on the superview of SuperView, and then on that view's superview, and so on. This is not a good solution, it is brittle and hacky. Overriding PointInside does have its uses, but this isn't one.
So try to keep all of your views within the bounds of their superview, even if you have to make a transparent superview that has no other reason to exist other than to contain other views. That's fine.

UIPicker within UIScrollView - Ignore drags/moves but capture touches

I have a rather large (in screen-estate) UIControl that I'm putting into a UIScrollView. Being a UIControl, it hijacks all touches and any drag that starts from within the control will not be reflected by the UIScrollView.
This control only needs UIControlEventTouchUpInside, so is there any way to "only" listen to that event?
My first instinct was to override hitTest:withEvent: and return the superview if it detected a drag, but that doesn't provide any information on the type of control event.
My last resort is to make it a generic UIView, but I'd rather not go down that road if possible.
EDIT:
Sorry, for clarity, I purposefully set delaysContentTouches to NO to support other features on the screen I am working on. The unintended side effects result in Joel H's second point.
Try setting both the canCancelContentTouches and delaysContentTouches properties of your UIScrollView to YES.
When the UIScrollView property canCancelContentTouches is set to YES, it should transfer any scrolling/drag touches to the scroll view and cancel whatever that touch would have been registered in the subview.
The scroll view's delaysContentTouches should be set to YES as well. This will prevent drags from being triggered as taps (because the handler fires off too soon).
UIScrollView class reference

Propagate dragging touch to UIScrollView superview

I have been looking to all the other similar topics here, using UIGestureRecognizers, using hitTest:withEvent, pointInside:withEvent: etc. but nothing seems to be ok for what I need to achieve.
Basically I have a main view (self.view of a common UIViewController) and a small rectangular UIScrollView attached onto it at the bottom: the scrollView is filled with some UIImageViews and the user can scroll it as usual.
But the user should also be able to drag one UIImageView (or a copy of it) from the UIScrollView to the main view, and, this is what I am finding really difficult, with the SAME dragging gesture, hence I need a way to:
1) Distinguish between normal horizontal scrolling gesture, which should be handled by the UIScrollView the usual way and a dragging gesture over the image view.
2) Once identified a dragging gesture, should propagate the touch to the superview, which will host a copy of the UIImageView and WITH the SAME dragging gesture continue the dragging over the main view even out of the bounds of the UIScrollView.
Please note that I know that if the UIScrollView has userInteractionEnabled = NO the touch is propagated to the subviews, but 1) I want to propagate it to the superview not the subviews, 2) the userInteractionEnabled property apparently becomes active only once the initial gesture is terminated, while I need to use a single dragging gesture.
Thank you very much for any help.
So, so far I have ended up implementing the touchesShouldBegin:withEvent:inContentView: method of my UIScrollView subclass but with delayContentTouches set to YES (default) instead of NO as #nhahtdh was suggesting.
Strangely enough even only implementing the method was sufficient for my subviews to intercept the dragging, and still my scrollview is scrolling properly, while with delayContentTouches set to NO I was not able to scroll it as all the subviews were starting to move around.
Really the credit for this is #nhahtdh, so man, if you post an answer I will accept it, thank you very much for your help.

Does UserInteractionEnabled work like Alpha for a UIView Hierarchy?

I know that if I set the Alpha for a parent UIView to to 0 that all its children are also 'invisible'. Is it also true that setting a parent UIView's UserInteractionEnabled to NO affect it's children in anyway? In other words will making a superView untouchable make its subViews untouchable?
Yes, but not in the same way. Basically, if you set a UIView.userInteractionEnable to NO, It will no longer process touch events, which means it will not be passing events to sub-views. You can, however, set userInteractionEnable to NO for sub-views with out impacting parent views or peer views.

Resources