touchesEnded:withEvent: from UIScrollView First Responder - cocoa-touch

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...

Related

scrollview, buttons events exchange

I have a scrollview where I added several buttons (dynamically, programmatically). Hence my view is totally covered with buttons. Also there are some labels
However, I observed that the uiscrollview is not scrolling when the drag starts on the button. All labels work fine. But I want this scroll to happen i.e. when drag event occurs in the uibutton, I want it to send this event to its superview (scrollview).
Please note, according to my search, subclassing the scrollview and overrriding the content touches event, or add touch began actions to uibuttons are not helpful.
How do one in general work with this event passing things when objects are added dynamically?
You have to subclass the UIScrollView, there's no other proper way, but you do not need to mess with the touch events. All you have to do is override this method
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
return YES;
}
And set the canCancelContentTouches property of the UIScrollView to YES.

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.

Handle tap event by subview of UIScrollView while scrolling

I have custom UIScrollView subclass with some content views inside. In some of them I have UITapGestureRecogniser. All works fine when scroll view is not scrolling. But when it scrolling content views does not receive tap action. What is the simplest solution to handle tap action by subview while scroll view is scrolling?
Details:
MyScrollView scrolls horizontally. It contains a lot of content views (e.g. MyContentView). Each MyContentView has width about one third of MyScrollView width. So there are about 3-4 visible MyContentView elements at a moment. The main behavior of MyScrollView is to 1)make sure that after scrolling one of MyContentView elements will be at center of screen and 2)to scroll to center of MyContentView if user taps on it. So the main answer I hope to get is how to "properly" implement handling of tap action in MyContentView while MyScrollView is decelerating.
I found some same questions and answers but none of them satisfied me. The best was to implement gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: of UITapGestureRecogniser delegate. But in this case I sometimes (when I tap, make smaaaal drag and release finger so tap is steel recognizable(lets called it quasi tap)) have both tap and scroll events and it leads to bugs for me even if scroll view is not scrolling when I begin tap. When user make quasi tap my application tries to scroll to tapped MyContentView element and than immediately handle normal scrolling. It seems even more terrible, due to some other functionality start to perform after handling tap (it must not perform when normal scrolling).
I need solution where scroll view wait enough to decide it is not tap event and only then make scroll. Otherwise if tap event had recognized scroll must not happen.
You can go with the custom delegates methods as well, using #protocol. Implement those delegate methods in view controller where your UIScrollView has been added.
like in MyContentView:
In touchesBegan method,
[self.delegate contentViewTapped:self];
Now in ContainerView class where scroll view is added, implement that method:
- (void)contentViewTapped:(MyContentView *)myContentView {
NSLog (#"ContentView no: %d", myContentView.tag); // if tag has been set while adding this view to scrollview.
}
Go through the examples for #protocol.
Hope this is what you required.
Enjoy Coding :)
This is built into UIScrollView - take a look at the delaysContentTouches and canCancelContentTouches properties. This should alleviate the problem when dragging a small bit after a tap.
This is all system built-in behaviour. I would suggest sticking with what Apple has provided for the feel of your interface (how it reacts to small drags, for instance) so that your app doesn't feel out of place on a user's phone.
EDIT:
Alternatively, you could disable scrolling of your scroll view in you gesture recognizer and re-enable it once it's ended/cancelled.
Further Edit:
I don't understand - I've created a sample project that illustrates how to intercept touches in a subview of a scroll view using gesture recognizer delegate methods. Play close attention to the "Cancellable Content Touches" and "Delays Content Touches" properties of the scroll view. They're both YES for very important reasons.
You scroll view should be delaying content touches until it has determined if the user is attempting a tap, pseudo-tap (as you put it), or a pan for the scroll view. Apple has already written the functionality you're trying to build; UIScrollView will already do what you want.
The problem is that the system doesn't want a scroll view's subviews intercepting tap events while the scroll view is scrolling. To this end, it cancels touch events if it determines that the user is actually trying to pan. Setting "Delays Content Touches" enables this behaviour. Ensure it's turned on and you should be fine.

UIPopoverController takes control and won't let go

I have a few Popovers that I present from UIBarButtonItems.
The popovers are Property settings for an Object, Color, Size, Positioning, name, line thickness, etc.
As the settings in the popover change, the Object they are properties for also changes in realtime.
Most of the time a tap away from the popover clears it. Though sometimes something happens to the environment where the responder chain seems to be broken and a tap away from the popover no longer dismisses the popover. The Property controls in the popover still work, though there is no way to dismiss the popover.
On Popovers with TextFields, I can alter the text, dismiss the Keyboard and then I can Dismiss the popover normally.
Any Tips on where to start Logging and see who's on top of the responder chain and who's able to receive gestures/touches.
Thanks!
When I was debugging recognizers and such (similar), I put a UITapGestureRecognizer on the main background view, calling a method viewTapped:. In viewTapped: you can dismiss any outstanding popovers (handy if they are properties of the VC). You can also send resignFirstResponder if you worry about any textFields that might not be cooperating.
Are you setting any passthrough views? You might examine those. Normally you don't have any ability to tap outside the popover without dismissing unless you specifically set the passthroughViews.
Good luck,
Damien

UIView Event Forwarding Infinite Loop

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).