Ширина UIWebView iPhone не подходит после операции масштабирования + изменение UIInterfaceOrientation

Я создал приложение iPhone с голой костью с UIWebView (Scales Page to Fit = YES, shouldAutorotateToInterfaceOrientation = YES) и загрузил веб-страницу, например. /qaru.site/...

Вращение устройства показывает, что пользовательский интерфейс UIWebView автоматически изменяется в соответствии с шириной. Хорошо.

Неверно. Увеличьте масштаб страницы и уменьшите масштаб. Теперь вращение устройства показывает UIWebView в странной ширине в одной из ориентации (если и увеличить масштаб, ширина портрета странная, наоборот). Это поведение фиксируется только при переходе на другую страницу.

Исправить. Загрузите тот же URL-адрес в Mobile Safari. Вращающиеся работы и ширина подходят независимо от упражнений масштабирования.

Является ли это ошибкой UIWebView (возможно, нет)? Или есть что-то, что нужно сделать, чтобы вещи "просто работали", как в Mobile Safari?

Ответ 1

Я нашел кое-что, что сработало для меня. Проблема заключается в том, что когда uiwebview меняет ориентацию, веб-контент зомбируется в соответствии с видовым экраном. Но параметр zoomscale подпрограммы scrollview не обновляется правильно (и не обновляется minimumZoomScale или maximumZoomScale

Затем нам нужно сделать это вручную в willRotateToInterfaceOrientation:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    CGFloat ratioAspect = webview.bounds.size.width/webview.bounds.size.height;
    switch (toInterfaceOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown:
        case UIInterfaceOrientationPortrait:
            // Going to Portrait mode
            for (UIScrollView *scroll in [webview subviews]) { //we get the scrollview 
                // Make sure it really is a scroll view and reset the zoom scale.
                if ([scroll respondsToSelector:@selector(setZoomScale:)]){
                    scroll.minimumZoomScale = scroll.minimumZoomScale/ratioAspect;
                    scroll.maximumZoomScale = scroll.maximumZoomScale/ratioAspect;
                    [scroll setZoomScale:(scroll.zoomScale/ratioAspect) animated:YES];
                }
            }
            break;
        default:
            // Going to Landscape mode
            for (UIScrollView *scroll in [webview subviews]) { //we get the scrollview 
                // Make sure it really is a scroll view and reset the zoom scale.
                if ([scroll respondsToSelector:@selector(setZoomScale:)]){
                    scroll.minimumZoomScale = scroll.minimumZoomScale *ratioAspect;
                    scroll.maximumZoomScale = scroll.maximumZoomScale *ratioAspect;
                    [scroll setZoomScale:(scroll.zoomScale*ratioAspect) animated:YES];
                }
            }
            break;
    }
}

Надеюсь, это поможет!

Ответ 2

Я пробовал решение из M Penades, и это, похоже, работает и для меня.

Единственная проблема, с которой я столкнулся, заключается в том, что при запуске этого на 3Gs вращение, к сожалению, не очень плавное.

Теперь я использую другой подход:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {

[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];

CGFloat scale = browserWebView.contentScaleFactor;
NSString *javaStuff = [NSString stringWithFormat:@"document.body.style.zoom = %f;", scale];
[browserWebView stringByEvaluatingJavaScriptFromString:javaStuff];

}

С наилучшими пожеланиями,
Ральф

Ответ 3

- (UIScrollView *)findScrollViewInsideView:(UIView *)view
{
    for(UIView *subview in view.subviews){
        if([subview isKindOfClass:[UIScrollView class]]){
            return (UIScrollView *)subview;
        }

        UIScrollView *foundScrollView = [self findScrollViewInsideView:subview];
        if (foundScrollView){
            return foundScrollView;
        }
    }

    return nil;
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    switch (self.interfaceOrientation){
        case UIInterfaceOrientationLandscapeLeft:
        case UIInterfaceOrientationLandscapeRight:
        {
            UIScrollView *webViewScrollView = ([self.webView respondsToSelector:@selector(scrollView)])
                ? self.webView.scrollView
                : [self findScrollViewInsideView:self.webView];

            [webViewScrollView setZoomScale:1.01f animated:YES];
        }
            break;

        default:
            break;
    }
}

попробуйте этот код, он незначительно меняет уровень масштабирования (1.01), чтобы позволить UIWebView увеличить размер контента в ландшафтном режиме.

findScrollViewInsideView: метод добавлен в поддержку ios4

Ответ 4

Я отправляю это, потому что у меня также была та же проблема, и здесь я следую подходом M Penades. M Penades Ответ woks хорош только для случая, если пользователь не перекосит (защелкнет Out) веб-просмотр, а затем поверните устройство и повторите этот процесс. Затем размер содержимого UiwebView постепенно уменьшается. так что проблема возникла в ответе M Penades. поэтому я исправил эту проблему, и мой код выглядит следующим образом.

1) Для этого я устанавливаю жест Pinch, чтобы при пользовательском перекос UIwebView мог проверять масштабный размер UIwebView.       //One This Пожалуйста, импортируйте протокол UIGestureRecognizerDelegate в '.h файл'

     //call below method in ViewDidLoad Method for setting the Pinch gesture 

- (void)setPinchgesture
{
     UIPinchGestureRecognizer * pinchgesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(didPinchWebView:)];

    [pinchgesture setDelegate:self];

    [htmlWebView addGestureRecognizer:pinchgesture];
    [pinchgesture release];
   // here htmlWebView is WebView user zoomingIn/Out 

}
   //Allow The allow simultaneous recognition

  - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer   *)otherGestureRecognizer
  {
     return YES;
  }

Возврат ДА гарантирует одновременное распознавание. возврат НЕТ не гарантируется, чтобы предотвратить одновременное распознавание, так как другой делегат может передать ДА

 -(void)didPinchWebView:(UIPinchGestureRecognizer*)gestsure
   {
   //check if the Scaled Fator is same is normal scaling factor the allow set Flag True.
    if(gestsure.scale<=1.0)
    {
    isPinchOut = TRUE;

    }
    else// otherwise Set false
    {
    isPinchOut = FALSE;
   }
  NSLog(@"Hello Pinch %f",gestsure.scale);
}

Если пользователь Hase Pinch In/Out Веб-просмотр в этом случае просто установите коэффициент масштабирования.   Так что WebView может отрегулировать свой ContentSize при изменении Oreintaion.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation   duration:(NSTimeInterval)duration {

   //Allow the Execution of below code when user has Skewed the UIWebView and Adjust the Content Size of UiwebView.

  if(isPinchOut){
  CGFloat ratioAspect = htmlWebView.bounds.size.width/htmlWebView.bounds.size.height;
  switch (toInterfaceOrientation) {
    case UIInterfaceOrientationPortraitUpsideDown:
    case UIInterfaceOrientationPortrait:
        // Going to Portrait mode
        for (UIScrollView *scroll in [htmlWebView subviews]) { //we get the scrollview
            // Make sure it really is a scroll view and reset the zoom scale.
            if ([scroll respondsToSelector:@selector(setZoomScale:)]){
                scroll.minimumZoomScale = scroll.minimumZoomScale/ratioAspect;
                scroll.maximumZoomScale = scroll.maximumZoomScale/ratioAspect;
                [scroll setZoomScale:(scroll.zoomScale/ratioAspect) animated:YES];
            }
        }
        break;

    default:
        // Going to Landscape mode
        for (UIScrollView *scroll in [htmlWebView subviews]) { //we get the scrollview
            // Make sure it really is a scroll view and reset the zoom scale.
            if ([scroll respondsToSelector:@selector(setZoomScale:)]){
                scroll.minimumZoomScale = scroll.minimumZoomScale *ratioAspect;
                scroll.maximumZoomScale = scroll.maximumZoomScale *ratioAspect;
                [scroll setZoomScale:(scroll.zoomScale*ratioAspect) animated:YES];
            }
        }
        break;
     }
  }

}

Это отлично работает для того, чтобы пользователь исказил UIWebView.

Ответ 5

У меня есть решение этой проблемы, но я должен сказать, что я не большой поклонник этого. Он отлично работает, но решение на самом деле вызывает еще одну проблему. У меня есть исправление для вторичной проблемы, но требуется немного усилий.

Просто имейте в виду, что с OS3.2 или iOS4 (не уверен, какой) UIWebView прямо подвью теперь UIScrollView вместо UIScroller, поэтому мы можем сделать с ним намного больше. Кроме того, поскольку доступ к представлениям в представлении View не является частным действием, ни один из них не использует подзаголовок, который выставляется как документальное представление, мы можем многое сделать с UIWebView, не нарушая правил.

Сначала нам нужно получить UIScrollView из UIWebview:

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

Теперь нам нужно изменить делегат этого прокрутки, чтобы мы могли переопределить вызовы делегатов scrollview (что может быть причиной вторичной ошибки в результате этого решения, которое я расскажу в данный момент):

sview.delegate = self;

Теперь, если вы попытаетесь в этот момент, масштабирование будет нарушено. Нам нужно реализовать метод UIScrollViewDelegate для его исправления. добавить:

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

webBrowserView на самом деле является UIWebBrowserView, но это не документированный класс, поэтому мы будем рассматривать его как UIView.

Теперь запустите приложение, увеличьте масштаб и уменьшите масштаб веб-страницы. Поверните, и он должен выглядеть правильно.

Это вызывает довольно большую ошибку, которая, возможно, хуже оригинала.

Если вы увеличиваете масштаб и затем поворачиваете, вы потеряете способность прокрутки, но ваше изображение будет уменьшаться. Вот исправление Чтобы завершить все это.

Во-первых, нам нужно отслеживать несколько чисел и определять флаг:

У меня эти определены в моем файле h:

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

Вы можете подумать, что можете просто захватить эти числа из UIScrollView, но когда они вам понадобятся, они будут изменены, поэтому мы должны их хранить в другом месте.

Нам нужно использовать другой метод делегата:

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

Теперь он попадает в беспорядок, который я чувствую.

Нам нужно отслеживать ротацию, поэтому вам нужно добавить это в свой viewDidLoad, loadView или любой другой метод, который вы используете для регистрации уведомлений:

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

и создайте этот метод:

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

Итак, теперь, когда вы поворачиваете webViewOrientationChange, вызывается. Причина, по которой функция Selectlect задерживается на 0,0 секунды, заключается в том, что мы хотим вызвать adjustWithZoomData на следующей runloop. Если вы вызываете его напрямую, настройкаWithZoomData будет корректироваться для предыдущей ориентации.

Вот метод adjustWithZoomData:

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

Вот оно! Теперь, когда вы вращаетесь, он будет поддерживать масштабирование и примерно поддерживать правильное смещение. Если кто-то хочет сделать математику о том, как получить точное правильное смещение, то идите за ней!

Ответ 6

Я изучал это сам и узнал дополнительную информацию:

Проблемы при изменении масштаба:

  • Safari часто неправильно перерисовывает (если вообще), даже если уровень масштабирования изменился.
  • Изменение ширины заставляет перерисовать.
  • вы бы подумали, что ширина = ширина устройства в ландшафте будет использовать 1024, но, похоже, использует 768 (screen.width тоже).

например. если текущая ширина равна 1024, и вы хотите увеличить от 1 до 1,5 в ландшафте, вы можете:

  • изменить комбинацию ширины и масштабирования, например. ширина до 2048 и увеличение до 0,75
  • изменить ширину до 1023 (уродливое сглаживание?)
  • измените ширину, чтобы сказать 1023, затем следующая строка назад до 1024 (двойная перерисовка, но по крайней мере окно перекрашено).

Ответ 7

Так что, видимо, я не использовал решение M Penades в конце (и забыл обновить этот пост! извините).

Я сделал, чтобы изменить размер всего документа (и изменить размер шрифта, чтобы сохранить пропорции). Это, видимо, решило проблему.

Однако мой UIWebView предназначен только для загрузки моего собственного HTML и CSS из файловой системы iOS - если вы создаете универсальный веб-браузер, этот трюк может и не работать.

ViewController.m

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    switch (toInterfaceOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown:
        case UIInterfaceOrientationPortrait:
            if ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)) {
                [webview stringByEvaluatingJavaScriptFromString:@"document.body.className = 'ppad'"];
            } else {
                [webview stringByEvaluatingJavaScriptFromString:@"document.body.className = 'pphone'"];
            }
            break;
        default:
            if ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)) {
                [webview stringByEvaluatingJavaScriptFromString:@"document.body.className = 'lpad'"];
            } else {
                [webview stringByEvaluatingJavaScriptFromString:@"document.body.className = 'lphone'"];
            }
            break;
    }
}

И app.css

html>body.pphone { font-size:12px; width: 980px; }
html>body.lphone { font-size:18px; width: 1470px; }
html>body.ppad { font-size:12px; width: 768px; }
html>body.lpad { font-size:15.99999996px; width: 1024px; }

Gist at https://gist.github.com/d6589584944685909ae5