I'm getting exactly the behavior I want in a UIScrollView: when I return NO for touchesShouldBegin, the scrolling behavior happens. Otherwise, the content views get the event.
However, I'd like to show something on touch down and touch up when the scrolling behavior is occurring. Unfortunately, returning NO for touchesShouldBegin blocks the touchesBegan and touchesEnded methods.
The delegate method:
-(void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {
doesn't work because sometimes the user touches but doesn't drag. How can I register touchUp and touchDown events yet preserve scrolling behavior?
You cannot. While a UIScrollview is scrolling, the subview(s) (and the view itself) are not updated or redrawn. It only scrolls. It is just how the UIResponder chain works in this scenario.
Related
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.
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.
I have a UITapGestureRecognizer on a UIViewController, which has a UIScrollView and UIWebView inside. It recognizes the tap gesture only after I scroll the UIWebView. How could I prevent this ?. Basically I want the tap gesture to be detected, when I am not scrolling the web view. I looked around and the closest I found is this:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
return YES;
}
but not sure how can I use this to disable the tap while scrolling. Any idea?
Another thing I want to do is to disable the UITapGestureRecognizer, when a link on the UIWebView is clicked (shouldStartLoadWebRequest is called). I checked that the tap gesture recognizer is called, before the shouldStartLoadWebRequest is called. Basically when clicking on a link on a UIWebView, it shouldn't trigger the action invoked by the UITapGestureRecongnizer. Any idea on how to do this?
So Apple's documentation strongly recommends you don't nest a UIWebView inside a scroll view:
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
It is possible on iOS 5 and above to get direct access to the underlying scroll view on a UIWebView (using the scrollView) property - playing around with this might help you.
In an iPad app, wherever a user is touching the screen I want to display an image, highlighting the points they are touching. The app contains a number of nested views, all of which should receive touches and behave normally.
Seems simple, but I've failed to find a good way to do it. Using the touches began: with event and related functions on the root view controller does not work because if a subview is touched the events are not fired. I've also created a 'dummy' gesture recognizer which just passes touch events to another class which draws images. That works great-ish and buttons work, but breaks UIScrollViews and I'm guessing other subviews with gesture reconizers.
Is there nowhere you can just access all touch events without affecting where those touches are headed?
thanks.
Your dummy gesture recognizer should be ok. Just watch out for setting states. possible -> began -> ...
Basically your gesture recognizer is forwarding all touches so it can be in began or possible state all the time while any touch exists.
To get rid of problems with other gesture recognizers return YES in this delegate method.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Other option is to subclass main UIWindow in your app and override this method
- (void)sendEvent:(UIEvent *)event
Here you should have access to all events. It's quite easy to filter them.
You can apply a UITapGestureRecognizer to the entire view and set the cancelsTouchesInView property to NO. This will allow you to be notified of all taps on the view and its subviews without intercepting all of the touch events.
Additionally, you can implement the -gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: delegate method to keep this gesture recognizer from stomping on the ones used by views like UIScrollView.
you can try overriding hitTest:withEvent:
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
This maybe what you are looking for.
I am trying to detect touches, but the touchesBegan method is not being called.
In my ViewController, I have added the touchesBegan method. My Nib files owner is set to the correct V.C. The Nib itself consists of the view, with a scroll view and a tab bar. Nested in the scroll view is an image view, which has user interaction enabled. What is precluding touches from being registered, or preventing my implementation of touchesBegan from being called?
I've scoured the Internet and Apple docs, and I can't see what I am doing wrong. Also, I'm not really sure what code I can post here to help with my query. Thanks.
Okay, after a lot more reading, I've now got a scrollview and a imageview, both of which are created programatically. The imageview is a sub view of the scrollview, and scrollview has been subclassed so that the touches ended method can decide whether it was a single touch, in which case call the touches ended method from the view controller, otherwise call its supers method. This works just fine, however, why is it that this cannot be done without subclassing scrollview? Is it my lack of understanding of how scrollview works, or is it just a limitation of it?