UIScrollView on UIWebView and UITapGestureRecognizer conflicts - objective-c

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.

Related

Is there a way to make some parts of UITableView not scrollable?

I'm using a paper fold animation To show some content from the top of the screen, over the tableView.
Right now i have a button that triggers this folding action.
I would like instead to trigger this action by swiping from top to bottom. but my TableView is full screen, so i figured if there is a way to make on UITableViewCell, or a part of the table, not scrollable, for it to trigger the Fold swipe instead.
I tried using the UISwipeGestureRecognizer But it doesn't work so well...
There are several ways to stop UITableView from scrolling. UITableView is a subclass of UIScroll view so you can use the UIScroll view methods to stop the table view being scrollable. So in your tableViewController you can call [[self tableView] setScrollEnabled:NO]. You should also be able to stop any user interaction on the table view by calling [[self tableView] setUserInteractionEnabled:NO] (a method on UIView).
You could subclass the UITableView and override the setContentOffset: method (another scrollView method), and only call [super setContentOffset:] when you actually want the table view to scroll (it's possible that you may need to override other methods as well.
You can also get hold of the pan gesture recogniser that the scrollView is using, this allows you to see exactly what messages the scrollView is getting for scrolling and you may be able to adjust the scroll offset accordingly [[self tableView] panGestureRecognizer]. Once you have the panGestureRecognizer you should be able to set it to wait for your swipeGestureRecognizer to fail using [UIPanGestureRecognizer requireGestureRecognizerToFail:. Which will mean that the pan gesture recogniser the scroll view uses will only start working if the swipe fails, you may need to make it so the swipeGestureRecognizer fails if it doesn't start near the edge of the screen.

iOS - Double Tap Gesture does not work on top of a UIWebView

I have a viewController that has several items added programatically. A UILabel and a UIWebView including a scroll view.
Now I have added a swipe gesture onto the viewController using storyboard and when run all seems ok.
The issue comes when I try to swipe over the the UIWebview, it does not trigger the the Swipe gesture and looks as if its cancelling it out.
How can i enable it so the gesture works over the whole view but yet keeping the UIWebview functionality?
You can set
[UIWebView.scrollView setDelegate:myViewController]
and implement in your view controller
-(void)scrollViewDidScroll:(UIScrollView *)sender
But to really pass events, you will need more:
http://mithin.in/2009/08/26/detecting-taps-and-events-on-uiwebview-the-right-way/

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.

How to synchronize scrolling of a NSScrollView and a WebView?

My app has two main views: a NSTextView subclass within a NSScrollView and a WebView. What the WebView displays is dependent on what the user enters into the text view - so I would like when the user scrolls either the text view or the web view the other scrolls proportionately to it.
I found this article which mentions how to do it with 2 scroll views. My problem is that WebKit doesn't seem to use normal Scroll views anywhere.
How should I implement this? What am I missing?
This is not a trivial problem to solve perfectly, as it's difficult to know whether the amount of text being edited in your text view corresponds to a similar amount of scrolling in the web view.
However, to answer your question about scroll views in WebView, they are used but as far as I know not documented extensively. You have to take advantage of the fact that you can obtain the scrollview being used, using public API, by asking the appropriate WebView subview for its "enclosingScrollView". Something like this works for me in a WebView where I know that there is only one frame:
[[[[myWebView mainFrame] frameView] documentView] enclosingScrollView];
If they are scrolling proportionately, probably the simplest solution would be to override touches events on your UIScrollview and impliment stringByEvaluatingJavaScriptFromString: on the UIWebView with (js) window.scroll(x,y). However, scrolling from the webView to the textview will require more work. The webView eats touches events, so you would need to
create a top level UIView or UIScroll view which captures the touches and sends them on to both the scrollView and the webView (via javascript) for ALL touches events, or
use a gesture recognizer to do the same.

Changing the zoom out action for a UIWebView

I'm trying to change the way a UIWebView reacts when the user do a pinch out gesture (when he's doing that, I zoom out and show multiple webView).
I see 2 solutions but I can't make them work :
Setting scalesPageToFit to YES, and
then getting events that are sent by
the API to change the behavior.
Problem: I don't find how to catch those events
Setting
scalesPageToFit to NO, and adding a
PinchGestureRecognizer on the parent
of the UIWebView. Problem:
The gesture is not well recognize
since there are conflicts with the
scrolling. If the user starts moving
the first finger before putting down
the second finger, the scroll begin
and the pinchGesture is not
recognize.
Do you have an idea ?
Ok, the anwser is just to set the delegate of my UIPinchGestureRecognizer to self and to override
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*) otherRecognizer
{
return YES;
}
Now the pinch is recognized even if the scroll began in the UIWebView