I have a UIWebView which I'm using to display a variety of content, sometimes HTML and sometimes PDF/Powerpoint/etc (which is all seemingly handled by the same underlying control).
What I want to do is harness the underlying UIScrollView (a subview of the UIWebView) to capture the exact perspective of the scroll view and recreate the scroll position in another UIWebView, thus keeping them both in sync.
The problem that I'm having is that the zoomScale property of the UIScrollView is always 1.0 while displaying HTML content (though when displaying PDF content, zoomScale is correct).
I managed to find that the only place where I can get any inkling of the zoom scale of an HTML page is in the scrollViewDidEndZooming:withView:atScale: delegate method. I've found that the atScale: argument seems to provide a relative zoom scale, to what the zoom scale was before the zooming operation began. So for example when zooming in, that argument is >1, and when zooming out it is <1, and it seems to be a relative value between the two zoom scales rather than an absolute zoom scale).
What I've further found is that fundamentally, HTML and PDF content is handled by two different underlying views: there's UIWebBrowserView which handles HTML content, and UIWebPDFView to handle displaying of PDFs.
So in other words, when using a UIWebPDFView, the zoomScale property of the UIScrollView is completely reliable (e.g. 1.0 for zoomed-out, 2.4 for zoomed-in, for example). On the other hand, UIWebBrowserView is much more fiddly, and when asking the UIScrollView for its zoomScale, always returns 1.0, but when receiving the delegate callback, receives a scale value that is a relative value.
So my questions are:-
Is this a bug? (This behaviour is seen on both iOS 4 and 5 from what I've seen).
How can I get both the zoomScale (or some other property/properties) for both HTML and PDF content, and get it in such a way that the state can be duplicated and kept in sync with another UIWebView.
I'm not beholden to using zoomScale, so I'm open to other suggestions for how the scroll state of a UIWebView can be captured and reproduced on another UIWebView.
When loading HTML, and UIWebBrowserView is being used, you can use
zoomScale = 1.0 / webView.scrollView.minimumZoomScale
Of course this is assuming that you can reliably ensure that the minimum zoom of your HTML page is 1.0, since some pages can alter the minimum zoom using the "viewport" meta tag.
Have you tried this?
set scalesPageToFit=YES;
That's the most common way to deal with the problem i think. Setting that enables you to pinch and zoom UIWebView.I don't think you can actually zoom in/out UIWebView without that.
Now the zoom is going to be relative to whatever you're showing on your UIWebView. If it is a big table, it's not so close but it's a good start point if you're going to pinch for zoom in.
This Logic for zooming of UIWebView, no need to add UIWebView on UIScrollView
Well only problem with webView.scalesPageToFit=YES; is, it change initial content font size but I have got other option by my self, Its working very well for me.
Add <UIWebViewDelegate, UIScrollViewDelegate> to your .h file
Creation of your UIWebView.
self.mWebview = [[UIWebView alloc] init];
self.mWebview.delegate = self; /// set delegate method of UIWebView
self.mWebview.frame = CGRectMake(0, 35, self.view.bounds.size.width, self.view.bounds.size.height - 80); // set frame whatever you want..
[self.mWebview setOpaque:NO];
self.mWebview.backgroundColor = [UIColor clearColor];
[self.view addSubview:self.mWebview];
With load HTML file/content.
NSString* htmlString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"File Name"ofType:#"html"] encoding:NSUTF8StringEncoding error:nil];
[self.mWebview loadHTMLString:htmlString baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
#pragma mark -
#pragma mark - Webview Delegate Methods
- (void) webViewDidFinishLoad:(UIWebView *)webView
{
webView.scrollView.delegate = self; // set delegate method of UISrollView
webView.scrollView.maximumZoomScale = 20; // set as you want.
webView.scrollView.minimumZoomScale = 1; // set as you want.
//// Below two line is for iOS 6, If your app only supported iOS 7 then no need to write this.
webView.scrollView.zoomScale = 2;
webView.scrollView.zoomScale = 1;
}
#pragma mark -
#pragma mark - UIScrollView Delegate Methods
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
self.mWebview.scrollView.maximumZoomScale = 20; // set similar to previous.
}
NOTE: I had to tested on Mac OS X - 10.9.3 with Xcode 5.1.1 and iOS version 6.1 and latter.
I hope this will helpful for you. :)
Unfortunately the only way I found to get the actual scale of the web content in a UIWebView is by accessing to a private member of the UIWebBrowserView : initialScale.
float initialScale = (access to [UIWebBrowserView initialScale])
float zoomScale = 1.0 / webView.scrollView.minimumZoomScale;
float actualScale = zoomScale * initialScale;
I am wondering if this can be done without accessing to private member.
This may be a little late but for those who need to determine whether the current UIWebView contains HTML. A method I wrote to determine this was below. I called this immediately after the webView has loaded
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (!webViewIsHTMLHasBeenSet) {
webViewIsHTML = [self containsWebBrowserView];
NSLog(#"webViewIsHTML = %#",webViewIsHTML ? #"YES": #"NO");
}
}
- (BOOL)containsWebBrowserView {
BOOL result = NO;
for (UIView * WVSubview in [webView.scrollView subviews]) {
if ([NSStringFromClass([WVSubview class]) isEqualToString:#"UIWebBrowserView"]) {
result = YES;
webViewIsHTMLHasBeenSet = YES;
return result;
}
}
webViewIsHTMLHasBeenSet = YES;
return result;
}
just simply set,
scalesPageToFit=YES for pich zoom on a UIWebView
Related
I have add html file to load inside UIWebview and its working fine.
Then (UIWebview) scrolling faster delay to load text, its appear white background while scrolling faster. I want to remove the white background while scrolling faster.
Is it possible to load UIActivityIndicatorView at the timw, please hlep me
Thanks in Advance
Because event tracking in UIScrollview blocks the main thread.
You can try to force scrollView end scrolling(or decelerating) before another web action.
I played with your original solution a bit, and this seems to work just fine. I think you almost had it, but you were just offsetting the rect that you used too much, and forgot that you could just scroll the rect straight back to the original rect.
The generalized solution for any scrolling action is this:
- (void)killScroll
{
CGPoint offset = scrollView.contentOffset;
offset.x -= 1.0;
offset.y -= 1.0;
[scrollView setContentOffset:offset animated:NO];
offset.x += 1.0;
offset.y += 1.0;
[scrollView setContentOffset:offset animated:NO];
}
[Edit] As of iOS 4.3 (and possibly earlier) this also appears to work
- (void)killScroll
{
CGPoint offset = scrollView.contentOffset;
[scrollView setContentOffset:offset animated:NO];
}
I think I found the problem. It looks like NSURLRequestReturnCacheDataElseLoad and NSURLRequestReturnCacheDataDontLoad is broken on the iPhone. When a NSCachedURLResponse is returned from the cache and it contains HTTP headers indicating that the content has expired (e.g. Expires, Cache-Control etc), the cached response is ignored and the request is made to the original source.
The solution is as follows:
Implement your own subclass of NSHTTPURLResponse which allows you to modify the allHeaderFields dictionary.
Implement your own NSURLCache, override cachedResponseForRequest: and return a new NSCachedURLResponse containing an instance of your NSHTTPURLResponse subclass with the relevant "expiry" HTTP headers stripped.
I know this has been asked many times but I can not get it to work. In short my UIWebview pulls in some HTML which has a none fixed height (width always the same), I just want the height to change accordingly.
I have tried a number of fixes I have found online but my problem is the webview will change to the correct size but only for a brief second and then in flicks back to the original size.
I should point out that my this view is invoked by a segue, that is the only thing I can think is different! I am wondering if the code works but when the segue finishes it's crossfade, flip or whatever it then redraws the webview.
Here is my current code (as I say I have tried many fixes including the Java versions most of which do the same), I have also played with scalesPageToFit and srollEnabled plus a few of the ticks in the Storyboard.
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(#"Starting Size : %f", headerWebView.frame.size.height);
headerWebView.scalesPageToFit = YES;
headerWebView.scrollView.scrollEnabled = NO;
CGRect frame = headerWebView.frame;
frame.size.width = frame.size.width; // I used this to test, I put 200 in and it changes then flicks back too
frame.size.height = 1;
headerWebView.frame = frame;
frame.size.height = headerWebView.scrollView.contentSize.height;
headerWebView.frame = frame;
NSLog(#"Finished Size : %f", headerWebView.frame.size.height);
}
The logs say;
Starting Size : 176.000000
Finished Size : 246.000000
Anyone got an idea as to why the Webview appears fine just long enough for me to notice and then "shrink" back?
Anyone got an idea as to why the Webview appears fine just long enough
for me to notice and then "shrink" back?
I would say that it appears fine because you are showing the changes as they are happening. Why not hide the web view at some earlier point in the view lifecycle or even in webViewDidFinishLoading? I am sharing my layout settings from IB in case that may provide further assistance. Please advise if this helps or whether additional information is needed. I am using springs and struts rather than auto layout. I would try these method to see if it works for you.
Hiding my web view data loads prevents users from seeing anything until all of my settings/data are in place.
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[webView setAlpha:0];
[webView loadData:[dataString dataUsingEncoding:NSUTF8StringEncoding] MIMEType:#"text/html" textEncodingName:#"UTF-8" baseURL:baseURL];
[UIView animateWithDuration:0.1 animations:^{
[webView setAlpha:1.0];
} completion:nil];
I've been looking for the past week for the answer to this question.
I have a UIWebView, inside of a UIScrollView. Everything works great, but I want the content of the UIWebView to reset its zoom, when the orientation changes.
In the HTML inside the UIWebView, I set the width of the viewport (w/ a meta tag) to "device-width" and then on the Obj-C side, I set the scalesPagesToFit = YES;
I've tried resetting the zoom with javascript; by replacing the meta tags in runtime; reloading; accessing the UIScrollView inside of the UIWebView; etc...
but with no success.
Any of you gods know a workaround?
The only one I can think off is to recreate the UIWebViews every time we change the orientation, but that makes them flash to white whilst rendering content, which looks terrible :(
Any thoughts?
Many thanks,
Andre
I'm just guessing here and haven't tried, but AFAIK a UIWebView has a UIScrollView child. So one should be able to do:
for (UIScrollView *scroll in [myWebView subviews]) {
// Make sure it really is a scroll view and reset the zoom scale.
if ([scroll respondsToSelector:#selector(setZoomScale:)])
[scroll setZoomScale:1.0];
}
On iOS 5+ you have access to scrollView.
Just do:
[webView.scrollView setZoomScale:1.0];
If you want to do it programmatically this is the only way I could find to accomplish it: (specify your own sizes if you wish, i was attempting to zoom out after typing into a form field)
UIScrollView *sv = [[webViewView subviews] objectAtIndex:0];
[sv zoomToRect:CGRectMake(0, 0, sv.contentSize.width, sv.contentSize.height) animated:YES];
Update:
Downscaling wasn't working properly when using
[[[webView subviews] lastObject] setZoomScale:0.25];
The quality of the images being downscaled on the page was awful. Doing:
[[[webView subviews] lastObject] setZoomScale:0.25 animated:YES];
Fixed it. So that last line is the one you could use.
webView was subclassed of a UIWebView which lies on some IB file. I didn't use the Viewport at all. I find that one should pick by either doing this from the Cocoa Touch side or use JS.
I used:
webView.scalesPageToFit = YES;
I wonder if there's a way of resetting the scalesPageToFit.
Adapting from Captnwalker1's answer, I came up with this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if(toInterfaceOrientation ==UIInterfaceOrientationPortrait||toInterfaceOrientation==UIInterfaceOrientationMaskPortraitUpsideDown)
{
currentScrollView = [[webView subviews] objectAtIndex:0];
[currentScrollView zoomToRect:CGRectMake(0, 0, currentScrollView.contentSize.width, currentScrollView.contentSize.height) animated:NO];
}
else
{
currentScrollView = [[webView subviews] objectAtIndex:0];
[currentScrollView zoomToRect:CGRectMake(0, 0, currentScrollView.contentSize.width, currentScrollView.contentSize.height) animated:NO];
}
}
So load your webview image, and the image will reset it's size when rotated.
I'm trying to use a UIWebView for displaying content higher than the screen of the iPhone, without needing to scroll in the UIWebView itself.
The UIWebView is placed as a subview to a UIScrollView, along with some other objects that I want the UIScrollView to scroll up and down with the UIWebView.
I know you can do this with a UITextView like this:
CGRect frame = _textView.frame; frame.size.height = _textView.contentSize.height; _textView.frame = frame;
But the UIWebView does not inherit from UIScrollView and does therefore not contain the contentSize-property.
I'd really like to keep it a UIWebView, because the data I get is in HTML-blocks.
Thank you!
In fact, no Javascript is needed!
In UIView there is a nice method:
- (CGSize)sizeThatFits:(CGSize)size
You pass it anything (not really meaningful here), and it returns the size it'd like to be, using its content (subviews, etc) to compute the size.
Of course, since content loading in a UIWebView in asynchronous, you should do this after the webview has loaded, for instance in the UIWebViewDelegate method:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGSize size = [webView sizeThatFits: CGSizeMake(1.0f, 1.0f)]; // Pass about any size
CGRect frame = webView.frame;
frame.size.height = size.height;
webView.frame = frame;
}
Et voila!
I think the only way you'll be able to do this is to use some javascript to get the size of the web page and adjust the size of the UIWebView control.
Something like the following should do the trick
int content_height = [[theWebView stringByEvaluatingJavaScriptFromString: #"document.body.offsetHeight"] integerValue];
CGRect rect = theWebView.frame;
rect.size.height = content_height;
theWebView.frame = rect;
You may need to add some sort of fudge factor to the content height.
Sometimes, my UIWebView will have a grey box over part or all of the content. I can't make heads or tails of why it's happening. It happens regularly for certain content.
Thanks!
--Update--
It seems to occur when the webview is not immediately viewable on the screen -- ie i've got a scrollview, and the webview is below the fold.
--Update #2--
When I bring the content above the fold, it loads fine most of the time. There are still instances when the grey box is still showing up. The weird part is if i double-tap it, it finishes loading the content just fine. bizarre
--Update #3--
Okay, so it seems to be that if my uiwebview has a height greater than 1000px, a grey box appears on the rest of the content below 1000px. A double-tap reveals the actual content.
All UIViews have a size limit of 1024x1024 pixels. This is stated at the end of the Overview section of the UIView documentation.
If your web view must have more than 1024px of content, you will have to take it out of the parent scroll view and let it manage scrolling on its own.
Nudging the UIScrollView in the UIWebView it fixes this for me:
[UIScrollView *webScroller= [[webView subviews] lastObject];
[webScroller setContentOffset:CGPointMake(0,1) animated:NO];
[webScroller setContentOffset:CGPointMake(0,0) animated:NO];
I have been dealing with this glitch as well, and have opened a bug report at apple.
I would have commented above, but I don't have the 50 rep yet.
For anyone else encountering this glitch, send them a report, I included a full project demonstrating it, with a few screenshots from another app I am working on.
The more bug reports they get on a topic, the more likely they are to address it, apparently.
https://bugreport.apple.com/
I've got the same problem. I put a UIWebView inside a big TableViewCell (>1024px) and when I scroll to the bottom of the cell, there is this grey box.
But, if I put a UILabel (also with a big size > 1024px), there is no grey box. So I think this has nothing to do with a max height of a UIView (BTW I can't find anything about this so called 1024 max height). I think it's more a UIWebView issue.
The solution for me is to reload the content of the webview when the grey box appears. Actually, I just have a HTMLString to load so I call [webview loadHTMLstring:] again, and the grey box disappear.
Hope that will help
I found a very intersting post about that, it solved the problem for me :
link ttp://pinchzoom.com/blog/items/view/1386/one-of-the-problems-with-the-uikit-at-the-moment-is-an-issue-embedding-a-uiwebview-within-a-table
Hope that helps
I've used stringByEvaluatingJavaScriptFromString plus moving UIWebView origin.
Design overview: I had a set of controls(UIImage in this example) above(I mean frame.origin.y) UIWebView. So, layout was:
UIView
UIScrollView
UIImageView
UIWebView
Since UIWebView doesn't support scrolling in such hierarchy I've rearrange it like:
UIView
UIScrollView
UIImageView
UIWebView
My view controller is delegate for UIWebViewDelegate and UIScrollViewDelegate.
Then,
- (void)viewDidLoad
{
// set delegates
self.scrollView.delegate = self;
self.webView.delegate = self;
// store original UIWebView position in ivar
webViewPosY = self.webView.frame.origin.y;
// load html into UIWebView
[self.webView loadHTMLString:someHTML baseURL:nil];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// get UIWebView size and store in ivar
webSize = [self.webView sizeThatFits:CGSizeMake(1.0,1.0)];
// set proper content height for UIScrollView
CGSize contentSize = self.scrollView.contentSize;
contentSize.height = webViewPosY + webSize.height;
self.scrollView.contentSize = contentSize;
// set UIWebView's frame height same as UIScrollView has
CGRect wf = self.webView.frame;
wf.size.height = self.scrollView.frame.size.height;
self.webView.frame = wf;
}
// scrolling logic:
// 1. if origin of UIWebView > 0 then move UIWebView itself
// 2. if origin of UIWebView == 0 then scroll with javascript
// 3. if origin is 0 and whole html is scrolled then move UIWebView again(this happens to support scroll "bouncing", or if you have some views below UIWebView
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollPosY = self.scrollView.contentOffset.y;
// (1) and (2) ifs
// how much to move UIWebView
CGFloat scrollOriginY = (scrollPosY >= webViewPosY) ? webViewPosY : scrollPosY;
// how much to scroll via JS
CGFloat scrollJSY = scrollPosY - scrollOriginY;
// (3) if
if ( scrollPosY > (webSize.height - scrollViewSize.height + webViewPosY ) )
scrollOriginY += scrollPosY - (webSize.height - scrollViewSize.height + webViewPosY);
// scroll with JS
[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"document.body.scrollTop = %f;", scrollJSY]];
// move UIWebView itself
CGRect wf = self.webView.frame;
wf.origin.y = webViewPosY - scrollOriginY;
self.webView.frame = wf;
}
This works just fine for me.
I ran into the same issue when dynamically resizing a UIWebView. Here's what worked for me:
#define LAYER_FOR(ui) [(ui) layer]
#define FRAME_FOR(ui) [LAYER_FOR((ui)) frame]
#define SET_FRAME_FOR(ui, frame) [LAYER_FOR((ui)) setFrame: (frame)]
+ (void) setHeightTo: (CGFloat *) height_ptr forView: (UIView *) a_view {
CGFloat height = *height_ptr;
CGRect existing_frame = [[a_view layer] frame];
existing_frame.size.height = height;
// need to reassign the same frame !?
NSLog(#"setting text view: %# to height: %f", a_view, (float) height);
SET_FRAME_FOR(a_view, existing_frame);
}
+ (void) resizeWebView: (UIWebView *) webView {
NSString *js = #" \
var __html_element = document.getElementsByTagName('html')[0]; \
var __height_string = document.defaultView.getComputedStyle(__html_element, null).getPropertyValue('height'); \
__height_string.replace('px', ''); \
";
NSString *heightString = [webView stringByEvaluatingJavaScriptFromString: js];
float height = [heightString floatValue];
if (height != UI_VIEW_HEIGHT(webView)) {
[self setHeightTo: &height forView: webView];
// resize scrollView inside webview to the same height
UIScrollView *webScroller = [[webView subviews] lastObject];
[self setHeightTo: &height forView: webScroller];
}
}
I called this code from webview's delegate 'webViewDidFinishLoad:' method.
Basically, the trick is to resize the webScroller inside webview.
Thanks to Padraig for the suggestion to nudge the webview's subview (the scrollview).