views:

650

answers:

2

I created a bare bones iPhone app with a UIWebView (Scales Page to Fit = YES, shouldAutorotateToInterfaceOrientation = YES) and loaded a webpage, e.g. http://stackoverflow.com/

Rotating the device shows that UIWebView is auto-resized to fit the width. Good.

Incorrect: Zoom into the page and zoom out. Now rotating the device shows UIWebView in a weird width in one of the orientation (if u zoom in landscape, the portrait width is weird, vice versa). This behavior is fixed only when you navigate to another page.

Correct: Load the same URL in Mobile Safari. Rotating works & the width fits regardless of the zooming exercise.

Is this a UIWebView bug (probably not)? Or is there something that needs to be done to make things "just work" like in Mobile Safari?

A: 

I have a solution to this problem, but I gotta say I'm not a huge fan of it. It works great, but the solution actually causes another problem. I have a fix for the secondary issue, but it takes a bit of effort.

Just keep in mind that since OS3.2 or iOS4 (not sure which) UIWebView's direct subview is now UIScrollView instead of UIScroller, so we can do a lot more with it. Also, since accessing subviews of a View is not a private action, neither is using a subview that is casted as a documented view we can do a lot with the UIWebView without breaking the rules.

First we need to get the UIScrollView from the UIWebview:

UIScrollView *sview = [[webView subviews] objectAtIndex:0];

Now we need to change the delegate of this scrollview so we can override scrollview delegate calls (which may actually be the cause of a secondary bug as a result of this solution, which I'll share in a moment):

sview.delegate = self;

Now, if you try it at this point, zooming is broken. We need to implement a UIScrollViewDelegate method to fix it. add:

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    UIView *webBrowserView = [[scrollView subviews] objectAtIndex:10];
    return webBrowserView;
}

webBrowserView is actually a UIWebBrowserView, but that isn't a documented class, so we are just going to treat it as a UIView.

Now run your app, zoom in and then zoom out the webpage. Rotate, and it should appear correctly.

This does cause a rather large bug, that is perhaps worse than the original.

If you zoom in and then rotate, you will loose scrolling ability, but your view will be zoomed in still. Here is the fix To complete the whole thing.

First, we need to keep track of a few numbers, and have a flag defined:

I have these defined in my h file:

BOOL updateZoomData;
float zoomData; //this holds the scale at which we are zoomed in, scrollView.zoomScale
CGPoint zoomOffset; //this holds the scrollView.contentOffset
CGSize zoomContentSize; //this holds the scrollView.contentSize

You may think you can just grab these numbers from UIScrollView, but when you need them, they will have changed, so we need them stored elsewhere.

We need to use another delegate method:

- (void)scrollViewDidZoom:(UIScrollView *)scrollView{
    if(updateZoomData){
        zoomData = scrollView.zoomScale;
        zoomOffset = scrollView.contentOffset;
        zoomContentSize = scrollView.contentSize;
    }
}

Now it gets into a mess I feel.

We need to track rotation, so you'll need to add this to your viewDidLoad, loadView, or whatever method you use to register notifications:

[[NSNotificationCenter defaultCenter] addObserver:self 
            selector:@selector(webViewOrientationChanged:) 
            name:UIDeviceOrientationDidChangeNotification 
            object:nil];

and create this method:

- (void)webViewOrientationChanged:(NSNotification *)notification{
    updateZoomData = NO;
    [self performSelector:@selector(adjustWithZoomData) withObject:nil afterDelay:0.0];
}

So now anytime you rotate webViewOrientationChange will be called. The reason performSelector is delayed for 0.0 seconds is because we want to call adjustWithZoomData on the next runloop. If you call it directly, the adjustWithZoomData will adjust for the previous orientation.

Here is the adjustWithZoomData method:

- (void)adjustWithZoomData{
    UIScrollView *sview = [[webView subviews] objectAtIndex:0];
    [sview setZoomScale:zoomData animated:YES];
    [sview setContentOffset:zoomOffset animated:YES]; 
    [sview setContentSize:zoomContentSize];
    updateZoomData = YES;
}

Thats it! Now when you rotate it will maintain zoom, and roughly maintain the correct offset. If anyone wants to do the math on how to get the exact correct offset then go for it!

maxpower
I guess I didn't mention, I think this is a bug in the first place.
maxpower
I've also notice that this breaks double tapping for zoom out, sortof.
maxpower
A: 

I try to use your solution but when I zoom, my text is blurry. What can I do to "refresh"the text like in Safari ?

tibo17