iPhone UIWebView width does not fit after zooming operation + UIInterfaceOrientation change

前端 未结 8 1437
庸人自扰
庸人自扰 2020-12-02 19:20

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

相关标签:
8条回答
  • 2020-12-02 20:09

    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!

    0 讨论(0)
  • 2020-12-02 20:12

    I was looking into this myself and found out some more information:

    Issues when changing zoom:

    1. Safari often doesn't repaint properly (if at all) even though zoom level changed.
    2. Changing the width forces a repaint.
    3. you would think width=device-width in landscape would use 1024 but it seems to use 768 (screen.width happens too).

    e.g. if current width is 1024 and you want to zoom from 1 to 1.5 in landscape you could:

    • change combination of width and zoom e.g. width to 2048 and zoom to 0.75
    • change width to 1023 (ugly aliasing?)
    • change width to say 1023, then next line back to 1024 (double repaint, but at least window is repainted).
    0 讨论(0)
提交回复
热议问题