UIView Event Forwarding Infinite Loop - cocoa-touch

I've created a UIView subclass in which I intercept all touches, check for certain conditions, and, if they aren't met, forward the touches to the appropriate subview. The problem is, if the subview doesn't intercept the touch, it's forwarded right back up the responder chain to my view, here I forward it back to the subview, and so on. How can I stop this from happening?

The responder chain is designed to pass events to superviews. I would make the subviews selectively handle events (check the opposite conditions) so events can be forwarded to the superview when it has to handle the event.
However, if only the superview can check the condition, the subviews will have to handle all events the superview sends (at least not to call UIView's event handling routine which forwards events to superviews).

Related

Prevent UITableView from scrolling when custom subview is handling touches

In my iOS app have a UITableView which contains a custom subview in one of it's cells. This cell is an interactive view that handles touch events (touchesBegan, touchesEnded, touchesMoved) to update itself.
The problem is that when the user 'drags' up or down, the tableView catches these touches (although I don't pass the touches up the responder chain), scrolls the table and prevents the subview from working correctly. I would like to prevent the table from scrolling as long as the user is touching that particular subview.
The subview has no reference at all to the tableView.
How can I prevent the scrolling behavior of the table?
Update
Despite accepting the answer below, I ended up handling my situation differently. I handle the touch events in my custom view now, pass them up the responder chain (to the table cell), the cell handles the touch events as well, using them to enable/disable scrolling on the superview (the table).
Turning off "Cancellable Content Touches" in the UITableView solved this for me (in the UITableView attributes inspector under Scroll View / Touch). I got this from this SO question: Scrolling a UITableView inside a UIScrollView
From the UIScrollView:canCancelContentTouches doc:
If the value of this property is NO, the scroll view does not scroll
regardless of finger movement once the content view starts tracking.
The most common method I use in such cases is to delegate information up the event chain. Set delegates in manner:
subview->cell->table
The second thing you can do is to send a notification via Notificaion Center. Table would listen to any events that forbid normal scrolling. It is overshoot but it will leave your code consistent.
I have no more ideas at the moment.

How to block NSView events under another NSView?

Here is the idea:
I have a NSWindow containing 2 NSView, let's call them ViewA and ViewB.
ViewA has a list of subview objects, each object has its own tracking area set and handles a mouseDown event. ViewB is a hidden view, which appears above ViewA.
The problem is when ViewB appears, ViewA still receives mouseDown events. So when I click on ViewB, the object behind the ViewB receives the mouseDown event. I would like to know if there's any way to block the events of ViewA while ViewB is over it.
I know I can remove the tracking area from every object, but it still responds to the mouseDown event.
You can override sendEvent: method on NSWindow and test 'firstResponder', if it is ViewA, than not call [super sendEvent:event] so ViewA will not receive any event.
If view B is a subview of A, the problem is that it's hidden. Don't hide it: just set its opacity to 0. That way you won't see it, but the responder chain will.
In case anyone still looking for answer for these kind of questions nowadays, I only managed to do this with a child window, solution described in this accepted answer. Also, if you want to make the window transparent (/clear colored), but still receive mouse events on it, put this line into action as well:
[overlayWindow setIgnoresMouseEvents:NO];
Sibling views block, descendant views dont as the child will propegate mouse events upstream to its parent. To block descendants propegating events to their parent you must overide the event in the child and not call super on the same event. Calling super will propegate the event to its parent. Here is a full explination on Events and hittesting sibling/descending views: (be warned its dense) http://stylekit.org/blog/2016/01/28/Hit-testing-sub-views/
you can also disable the touch events for ViewA by [ViewA setAcceptsTouchEvents:NO];
and can enable them again as per your requirement by setting YES again.

Set a subview as the responder to calls to a parent UIView subclass

I've got a UIView that I've subclassed to be the main view used throughout my app. In it, I have two subviews: banner and container. Banner is basically a place to put an ad or a disclaimer or whatever. Container is meant to act as the primary view, to which you can add, remove and whatever as if it were the only view.
Right now, I'm just overriding the methods of the parent view and sending the calls to the container view. I'm wondering if there is an easier way to do this, without having to write out stuff like this for every method:
- (void)addSubview:(UIView*)view {
[container addSubview:view];
}
Maybe something that lets you delegate all method calls to the view to a specific subview, rather than responding to the method calls itself.
Anyone know if this is possible?
I'm a little confused by the question.
The responder chain is present and passes ui events up through all visible views on screen, by hierarchy. It may be useful to read a little about the responder chain, because by design it passes events from the deepest view to the highest (root) in that order, which is the opposite of the direction you're seeking (if I'm reading this right).
If you need to forward events from a superview to a subview, to respect principles of encapsulation, you should define appropriate actions in your subview's subclass interface, and then then configure your superview to target the actions in that subview/class.

Receiving touch events in NSView when cursor is outside of the view

In my application I need to respond to trackpad touch events (touchesBeganWithEvent / touchesMovedWithEvent / touchesEndedWithEvent) in a custom NSView, I have the code working properly. But I also need to do this when the cursor is outside of the view when the touch starts and NSView doesn't receive these events in this case.
One direction I was exploring is subclassing NSApplication and overriding sendEvent. But I am not sure how to dispatch the proper messages to my NSView. Just forwarding these events to the NSWindow via sendEvent doesn't seem to work.
Or is there a better way? Any ideas?
Thanks.

touchesEnded:withEvent: from UIScrollView First Responder

I've made a UIScrollView the first responder. I need to maintain touch events to a -touchesEnded:withEvent: method on a view behind it. I've tried using the -nextResponder method and that failed. I've tried forwarding -touchesEnded:withEvent: to the view behind it and that fails.
How do I get this to work? The UIScrollView wont work unless it is the first responder or gets events some other way.
Thank you for any help. Shame Apple's documentation and APIs are terrible in areas.
UIScrollView handles touches in a slightly unusual way. From the class reference:
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement. If the timer fires without a significant change in position, the scroll view sends tracking events to the touched subview of the content view. If the user then drags their finger far enough before the timer elapses, the scroll view cancels any tracking in the subview and performs the scrolling itself...
A subview within the scroll view's content view will eventually receive non-scrolling touches to the scroll view.
A superview of a scroll view is unlikely to see touch events as its -hitTest:withEvent: will return the scroll view so touch events will be sent to the scroll view which is not required to forward them back up the responder chain to the parent view.
First responder does not influence the delivery of touch events, see http://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW5
The first responder is the responder object in an application (usually a UIView object) that is designated to be the first recipient of events other than touch events...