how to forward UIWebView scroll to UIScrollView ? (conditionally) - ios

I have a UIWebView inside UIScrollView.
This idea is to be able to create more reading space on screen when user scroll the webpage upwards - by scrolling the UIScrollView upwards till the toolbar is visible, and obviously when the toolbars is nomore visible actually scroll the webpage to show more content that's on the page.
IPhone Safari browser does exactly the same functionality.
see screenshot (first) for default behavior i am getting - i guess because : the scrolling message is consumed by the webview since the touch/scroll is happening directly on webview area.
what i would like to do here is programatiicaly decide when to forward the 'upward scroll' to the UIScrollivew and when not to.
Any tips on how to get around this will be so helpful. Thanks!!

The UIWebView class reference discourages embedding a UIWebView in a UIScrollView:
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.
However I suppose you still want to implement your feature ;-)
Idea 1: Don't embed the UIWebView in a UIScrollView, instead run javascript using UIWebView's stringByEvaluatingJavaScriptFromString: method, modifying the DOM to add a toolbar below. If required, you can callback objective-c code from any buttons pushed on the toolbar by registering some custom URL schemes.
Idea 2: Don't embed the UIWebView in a UIScrollView, but in a normal UIView. Building on Vignesh's suggestion, listen for your webView's inner scrollView's scrollViewDidScroll: callback via the delegate, checking the contentOffset vs. the contentSize's height each time the callback is called. Once they are equal, it means you got to the bottom. Once this happens you can animate your toolbar's frame to "enter" the containing UIView and "push" the webView's frame away.
Idea 3: Ignore Apple's recommendation, and embed the UIWebView in a UIScrollView. Building on Vignesh's suggestion, listen for your webView's inner scrollView's scrollViewDidScroll: callback via the delegate, checking the contentOffset vs. the contentSize's height each time the callback is called. Once they are equal, it means you got to the bottom. Once this happens set the userInteractionEnabled property of the webView to NO, and set it to YES on the scrollView which contains the webView and the toolbar. Hopefully the scroll will continue smoothly enough. Of course you have to listen to the containing scroll view in the same way to determine when to switch back the userInteractionEnabled.
A variation on this idea would be to just set userInteractionEnabled to NO for the webView, but set the webView's frame's height to match its contentSize, and also enlarge the contentSize of the containing scrollView accordingly.
Both variations have the drawback that in some cases you won't be able to do things such as click on links :-( But maybe that's good enough for your case. At least in the first variation it's not so bad.

You can add a searchbox and uiwebview in a UIscrollview one below another. To get the content offset when webview is scrolled you can use the following code snippet.
UIScrollView* currentScrollView;
for (UIView* subView in testWebView.subviews) {
if ([subView isKindOfClass:[UIScrollView class]]) {
currentScrollView = (UIScrollView*)subView;
currentScrollView.delegate = (id)self;
}
}
Now you can change your base scrollview offset's y value to the offset value you get from the scrollview didscroll delegate method.

Advanced ScrollView Techniques
https://developer.apple.com/videos/wwdc/2011/

Related

How to get rid of adjustedContentInset in UIWebview?

I have a UIWebView in which i load content from remote URL. Problem i am facing is, last part of the content of webview is not scrollable. When debugging i noticed that adjustedContentInset is set as {0, 0, 49, 0}. Perhaps that is the reason partial webview content(49 pts from end) is not scrollable. Is there any way i can get rid of adjustedContentInset so that the whole content becomes scrollable?
I tried setting the contentInsets of webview's scrollview to {0,0,0,0} but it didn't help.
UITableView and UICollectionView has contentInsetAdjustmentBehavior method to reset adjustedContentInset but i couldn't find such property for UIWebView.
Any help will be appreciated.
Actually, contentInsetAdjustmentBehavior is a UIScrollView property, that's why what you mentioned:
UITableView and UICollectionView has contentInsetAdjustmentBehavior
method to reset adjustedContentInset but i couldn't find such property
for UIWebView.
is correct.
That means contentInsetAdjustmentBehavior is not a property for the UIWebView (UIView), what would you need to do is to access it via the web view scrollView property:
The scroll view associated with the web view.
Discussion
Your app can access the scroll view if it wants to customize the
scrolling behavior of the web view.
which exactly what are you aiming to. Therefore:
webView.scrollView.contentInsetAdjustmentBehavior = ...
should work.

Detect when multiple UIWebViews on the same view have all finished loading?

Context
Single ViewController
Two UIWebView objects as subviews of a UIScrollView are in this ViewControllers view
The ViewController is the delegate of both UIWebViews
The delegate method - (void)webViewDidFinishLoad:(UIWebView *)webView gets called twice as expected for both UIWebViews
I'm setting the height of these UIWebViews using sizeThatFits once their content has loaded (in the webViewDidFinishLoad method
What I want to do
Once both WebViews have loaded their content I want to set the height of the UIScrollView that they are in relative to the WebViews height. Basically I want the UIScrollView to be tall enough to scroll all the way through the text in the UIWebViews.
Possible Solutions I've thought of
Have a counter within the webViewDidFinishLoad method, and when it is equal to the amount of WebViews on the view call a method that sets the height of the UIScrollView.
Call set height of UIScrollView in webViewDidFinishLoad - it will be called multiple times, but the last call will be the correct height.
Question
How do I work out when both webViews have loaded in a "better" way than the possible solutions above?
I think you're making this too complicated. As you suggest at the end of your question, why not just set the height (contentSize.height, I believe is what you want) with each webViewDidFinishLoad:? You can test to see which webview is the tallest and set the height to that value:
CGSize size = CGSizeMake(320, 0);
size.height = webView1.frame.size.height + webView2.frame.size.height;
scrollView.contentSize = size;
IMO it's not a great idea to be placing multiple webviews inside a scrollview (although I've done it before too...) Could you consider getting rid of the scroll view altogether and having a single webview (which is self-scrolling) and has two iframe elements, which each display your original webview1/webview2 content?

Showing scroll indicators on a UIScrollView when programmatically scrolling

EDIT: The crux of this problem is that scroll indicators do not show during programmatic scrolling, but I would like them to. My original question (provided below) assumed this had something to do with userInteractionEnabled, but it does not. The mention of a master and slave UIScrollView is also possibly distracting from my core problem (the need to show scroll indicators during a programmatic scroll). Apologies to those of you who answered or commented based on my misleading assumptions/info.
Possible Solution: The only way I found to do this was to use the fact that scroll indicators are instances of UIImageView and use a category on it to hack the alpha. This article shows the approach. It was then a case of using tags and scroll view delegate methods to turn the alpha permanently on prior to a programmatic scroll, and permanently off when the scroll is finished. This feels hacky though, so any further suggestions would be welcome!
Everything below this line is the original unedited question to provide context to users' answers and comments
Setting userInteractionEnabled in a UIScrollView object to NO appears to disable the scroll indicators upon programmatic scrolling. This happens even if you have self.showsVerticalScrollIndicator = self.showsHorizontalScrollIndicator = YES;
Is there any way to programmatically scroll the scroll view but still show the indicators?
(To provide some context: I have a slave scrollview that mimics a master scrollview by hooking up the scrollview delegate callbacks and passing the content offset to the slave scrollview. However, I don't want the user to be able to directly manipulate the slave scrollview, but I do want scroll indicators).
Instead of setting userInteractionEnabled to false try setting the UIScrollView's scrollEnabled property to false. The doc. says "When scrolling is disabled, the scroll view does not accept touch events" that should mean that you should still be able to programmatically scroll the UIScrollView. Hope this helps - Did not test it out let me know.
You could try putting a transparent UIView (alpha == 0.0) over your scroll view (but as a sibling in the view hierarchy, not as a subview). Set touchesEnabled to YES on the transparent view, and it will intercept touches heading for the scroll view.

UIScrollView unwanted scrolling after addSubview or changing frame

I have a UIScrollView filled with subviews, all is well when creating it and initially filling it.
But when I add a new subview that is positionned outside of the visible screen portion, or when I just resize an existing subview that is also outside of the visible screen portion, there is a subsequent 0.3s-long scroll animation (I can see it happening from my delegate) that seems to match the newly added/resized element.
Attempts:
pagingEnabled is always NO.
Setting scrollEnabled to NO during subview manipulations doesn't help.
Doing a setContentOffset:animated:NO after subview manipulations doesn't prevent the animation.
One single giant subview with all my subviews in it doesn't help.
My current workaround is to initially set the frame to fit inside the visible screen portion, or doing resizing work inside another superview, but it feels dirty, and won't handle all situations...
Is there a way to prevent this automatic scrolling animation when programmatically manipulating subviews?
Xcode 4.3, iOS SDK for 5.1.
I too discovered this problem and found this solution http://www.iphonedevsdk.com/forum/iphone-sdk-development/94288-disabling-uiscrollview-autoscroll.html
It involves subclassing the UIScrollView and entering no code in the following method.
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
}
Like the guy says on the link I've found it works and no problems so far. Hope it works for you.
I had this problem because I set the content size of the scroll view prior to adding the subview.
As soon as I change the code so that the content size of the scroll view was set after adding the subview the problem went away.

How can I let UIScrollView (pagingEnabled) to move 1.2 page each time?

Ok. here is the story:
I have a UIWebView in full screen, width = 768
It loads several images and those images are in one line, which means the UIWebView will scroll only horizontally.
Now, I set the UIScrollView inside the UIWebView to pagingEnabled = YES. So the scroll on the UIWebView will move page by page.
The problem is that every image's width is about 900. I won't scale them down and if I scroll the UIWebView, from the 2nd page on, always 132points of previous image will show. This is not good for me.
So how can I manipulate the UIWebView or the UIScrollView inside the UIWebView so that each scroll will move a page of 900 points, if the view's frame width is 768?
The constraints are:
I can't change its contentSize
I can't change its bounds or frame
I say these constraints because UIWebView will change them on its own purpose when it loads contents.
Anyone would like to join this brain storming?
Thanks
I'm thinking you're going to need to set a UIScrollView delegate that tracks touches beginning and ending. More specifically, scrollViewDidEndDragging:willDecelerate is what I'm thinking you'll need to use, as it fires the instant a user lifts their hand from the device.
First, pagingEnabled=NO seems obvious, we're going to have to control our own paging. Which, I don't feel is too tough. Track the direction of the scrolling in scrollViewDidScroll:, so that we have some global set:
BOOL isGoingRight = YES or NO (NO means the last movement was to the left)
float PAGESIZE = 900.0;
Here's how my scrollview delegate method would look like that handles paging.
- (void) scrollViewDidEndDragging:(UIScrollView*)_scrollView willDecelerate:(BOOL)willDecelerate {
float offsetProportion = _scrollView.contentOffset.x/_scrollView.frame.size.width;
//depending upon our direction, round up or down
int page = isGoingRight? ceilf(offsetProportion): floorf(offsetProportion);
//manually finish the scroll
[_scrollView scrollRectToVisible:CGRectMake(page*PAGESIZE, 0, _scrollView.frame.size.width, _scrollView.frame.size.height) animated:YES];
}
I didn't test this, you may or may not need to disable touches at points.

Resources