【问题标题】:editable webView getting scrolled to top by itself可编辑的 webView 自行滚动到顶部
【发布时间】:2025-12-13 18:40:01
【问题描述】:

我在可编辑的 web 视图中显示了一些文本。一旦我向下滚动并触摸某处以编辑呈现的文本,它就会滚动到顶部并出现键盘,因此我必须再次向下滚动以进行编辑。有没有办法阻止 webView 这样做?

【问题讨论】:

    标签: ipad ios5 uiwebview contenteditable


    【解决方案1】:

    遇到同样的问题,仍在寻找这种奇怪行为的正常解决方案。 我们仍然无法阻止 UIWebView 这样做,如果你在 iPad 上查看 Evernote 应用程序,你会在那里看到同样的问题,不幸的是:( 我们唯一能做的就是在显示键盘时保存 UIWebView 的 contentOffset 并在打开键盘后恢复。

    这看起来像:

    //register your controller for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) UIKeyboardDidShowNotification object:nil];
    

    然后您将需要处理键盘通知,例如:

    - (void)keyboardWillShow:(NSNotification *)aNotification {
    
    // scroll view will scroll to beginning, but we save current offset
    [_yourViewWithWebView saveOffset];
        ...
    }
    

    之后,您需要在显示键盘时处理事件:

    - (void)keyboardWasShown:(NSNotification*)aNotification{
    ...
    // scroll view scrolled to beginning, but we restore previous offset
    [_yourViewWithWebView restoreOffset];
    }
    

    因此,在您需要实现的包含 UIWebView 的视图中:

    static CGPoint editableWebViewOffsetPoint;
    
    - (void) saveOffset{
        editableWebViewOffsetPoint = yourWebView.scrollView.contentOffset;
    }
    
    - (void) restoreOffset{
        //just use animation block to have scroll animated after jumping to top and back to old position
        [UIView animateWithDuration:.2
                delay:0
                options:UIViewAnimationOptionCurveEaseIn
                animations:^{
                    yourWebView.scrollView.contentOffset = editableWebViewOffsetPoint;
                }
                completion:nil];
    
    }
    

    总体而言,希望这将帮助您至少部分解决您的问题。

    如果有人能帮助我们防止每次显示键盘时 UIWebView 滚动到顶部,我将不胜感激。

    UIWebView.scrollView.scrollsToTop = NO; 没有帮助。

    在显示键盘之前禁用滚动并在显示键盘后启用它也不起作用。

    此外,当光标不在 UIWebView 的可见区域时,您将面临编辑文本的问题 - 它不会自动滚动以使光标可见。我们已经解决了这个问题,但我正在创建详细且易读的教程,说明我们如何做到这一点。如果您已经解决了这个问题,我会很高兴看到您的解决方案:)

    PS:http://www.cocoanetics.com/2011/01/uiwebview-must-die/

    谢谢你, 谢尔盖 N.

    【讨论】:

    • 非常感谢您提供如此全面的答案。我很感激。解决方案确实在一定程度上解决了问题。关于“当光标不在可见区域时编辑文本”问题,我使用以下两个函数在 textView 中解决了它。虽然我也必须在 webView 中尝试它们。
    • Sergey,你写过那个教程了吗? :) 我已经为这些UIWebViews 苦苦挣扎了很长时间。
    • 嗨,Leo,我们还不能解决这个滚动问题,只是在用户进入编辑模式和键盘出现之前尝试保存光标,然后在键盘打开和视图后恢复该位置滚动到顶部。我正在准备的教程是当用户手动滚动 Web 视图并尝试开始编辑它时如何处理打开的键盘和情况 - 光标不在可见区域。同样,当键盘打开/取消停靠/使用 BT 键盘+不同的屏幕方向以允许用户始终看到他开始输入某些文本的位置时,我们正在处理更改视图插图的问题
    【解决方案2】:

    “当光标不在可见区域时编辑文本”问题的功能。

    - (void)keyboardWasShown:(NSNotification *)aNotification {
    
    //if(self.navigationController.viewControllers objectAtIndex:([self.navigationController.viewControllers count]-1)==self.)
    NSLog(@"keyboardshown");
    if (keyboardshown)
        return;
    keyboardshown=YES;
    NSDictionary* userInfo = [aNotification userInfo];
    
    CGRect keyboardEndFrame;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
    
    CGRect newFrame = self.textView.frame;
    CGRect keyboardFrame = [self.textView convertRect:keyboardEndFrame toView:nil];
    
    newFrame.size.height -= keyboardFrame.size.height;    
    [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
    [UIView setAnimationBeginsFromCurrentState:YES];
    
    [UIView setAnimationDuration:0.3];
    [self.textView setFrame:newFrame];
    [UIView commitAnimations];
    
    
    
    }
    
    - (void)keyboardWasHidden:(NSNotification *)aNotification {
    
    if (!keyboardshown)
        return;
    keyboardshown=NO;
    
    NSDictionary* userInfo = [aNotification userInfo];
    
    CGRect keyboardEndFrame;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
    
    CGRect newFrame = self.textView.frame;
    CGRect keyboardFrame = [self.textView convertRect:keyboardEndFrame toView:nil];
    
    newFrame.size.height += keyboardFrame.size.height;
    [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
    [UIView setAnimationDuration:0.3];
    self.textView.frame = newFrame;
    [UIView commitAnimations];
    
    
    
    
    }
    

    【讨论】:

      【解决方案3】:

      实际上keyboardWasShown 并不总是被调用,尤其是当用户连接了BT 键盘并且虚拟键盘可以通过Eject 键隐藏/显示时。我们已经实现了自己的类,例如:

          @implementation KeyboardUtils
      
      + (CGRect) convertRect:(CGRect)rect toView:(UIView *)view {
          UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *) view : [view window];
          return [view convertRect:[window convertRect:rect fromWindow:nil] fromView:nil];
      }
      
      /**
       * This is working but deprecated solution
       * Based on UIKeyboardCenterBeginUserInfoKey and UIKeyboardCenterEndUserInfoKey which are deprecated since iOS 3.2
       */
      + (BOOL)checkKeyboardOnDisplayCenterBegin:(CGRect)centerBegin centerEnd:(CGRect)centerEnd{
      
          CGRect mainScreen = [UIApplication currentBounds];
      
          BOOL isKeyboardOnDisplay = CGRectContainsPoint(mainScreen, centerEnd.origin);
      
          [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:isKeyboardOnDisplay] forKey:@"isKeyboardOnDisplay"];
          [[NSUserDefaults standardUserDefaults] synchronize];
      
          return isKeyboardOnDisplay;
      }
      
      /**
       * This method allows to verify if software keyboard is currently present on screen for the application
       * Allows to handle undocked, split states of keyboard, as well as connected Bluetooth keyboard.
       * Needed to adjust UI - scrolling and insets for editable parts of the app, as well as avoid application be beneath open keyboard
       */
      + (BOOL)checkKeyboardOnDisplayBeginFrame:(CGRect)frameBegin endFrame:(CGRect)frameEnd{
          CGRect mainScreen = [UIApplication currentBounds];
          UIView *firstView = [[(AppDelegate *)[[UIApplication sharedApplication] delegate] window].subviews objectAtIndex:0];
      
          CGRect convertedEndFrame = [KeyboardUtils convertRect:frameEnd toView:firstView];
      
          BOOL isKeyboardOnDisplay = CGRectContainsRect(mainScreen, convertedEndFrame);
      
          [[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:isKeyboardOnDisplay] forKey:@"isKeyboardOnDisplay"];
          [[NSUserDefaults standardUserDefaults] synchronize];
      
          return isKeyboardOnDisplay;
      }
      
      
      
      + (BOOL)checkKeyboardOnDisplayFromNotification:(NSNotification *)aNotification{
          BOOL isKeyboardOnDisplay = [KeyboardUtils checkKeyboardOnDisplayBeginFrame:[[aNotification.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] 
                                                                            endFrame:[[aNotification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]];    
          return isKeyboardOnDisplay;
      }
      

      然后你可以像这样使用它:

      - (void)keyboardWillChangeFrame:(NSNotification*)aNotification{
          [KeyboardUtils checkKeyboardOnDisplayFromNotification:aNotification];
      }
      

      其中 keyboardWillChangeFrame 是选择器观察者:

      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
      

      通过这种方式,您可以将键盘的状态(如果它显示为停靠的键盘并且确实出现在显示屏上,并且未使用 BT 键盘)到 NSUserDefaultSettings。在侦听键盘通知或方向更改的处理程序中,您应该从默认值中检查此键值。

      另外一种方法是 [UIApplication currentBounds]; 它通过使用以下类别扩展它存在于应用程序中:(.h 文件)

      #import <UIKit/UIKit.h>
      
      @interface UIApplication (AppDimensions)
      +(CGSize) currentSize;
      +(CGRect) currentBounds;
      +(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation;
      @end
      

      .m 文件:

      #import "UIApplication+AppDimensions.h"
      
      @implementation UIApplication (AppDimensions)
      
      +(CGSize) currentSize
      {
          return [UIApplication sizeInOrientation:[UIApplication sharedApplication].statusBarOrientation];
      }
      
      +(CGRect) currentBounds{
          CGRect bounds = [UIScreen mainScreen].bounds;
          bounds.size = [UIApplication currentSize];
          return bounds;
      }
      
      +(CGSize) sizeInOrientation:(UIInterfaceOrientation)orientation
      {
          CGSize size = [UIScreen mainScreen].bounds.size;
          UIApplication *application = [UIApplication sharedApplication];
          if (UIInterfaceOrientationIsLandscape(orientation))
          {
              size = CGSizeMake(size.height, size.width);
          }
          if (application.statusBarHidden == NO)
          {
              size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height);
          }
          return size;
      }
      
      @end
      

      希望这将帮助任何关心处理屏幕上存在键盘的人。

      【讨论】:

        【解决方案4】:

        一种效果很好的方法是在显示键盘时暂时禁用UIScrollView 上的setContentOffset:。不过这有点骇人听闻,因此在某些情况下可能会导致其他问题。

        @Sergey N. 的响应,注册键盘通知,但不要存储/恢复 contentOffset,而是使用这些:

        - (void)keyboardWillShow:(NSNotification *)aNotification {
            [self disableMethod:@selector(setContentOffset:) onClass:[UIScrollView class]];
        }
        - (void)keyboardWasShown:(NSNotification *)aNotification {
            [self enableMethod:@selector(setContentOffset:) onClass:[UIScrollView class]];
        }
        

        类中的其他位置(或其他类中,只要您替换上述调用中的self),放置这些:

        -(void)swizzleMethod:(SEL)origSel from:(Class)origClass toMethod:(SEL)toSel from:(Class)toClass{
            Method origMethod = class_getInstanceMethod(origClass, origSel);
            Method newMethod = class_getInstanceMethod(toClass, toSel);
            method_exchangeImplementations(origMethod, newMethod);
        }
        -(void)disableMethod:(SEL)sel onClass:(Class)cl{
            [self swizzleMethod:sel from:cl toMethod:@selector(doNothing) from:[self class]];
        }
        -(void)enableMethod:(SEL)method onClass:(Class)cl{
            [self swizzleMethod:@selector(doNothing) from:[self class] toMethod:method from:cl];
        }
        -(void)doNothing{
        
        }
        

        这首先阻止了 webview 滚动到顶部,因此它不会显示糟糕的动画,但是,在某些情况下它可能会导致一些问题(例如,在持有 webview 的视图中有更多的输入控件)。在 iOS 5.0+ 中成功测试了这个。 在 iOS 6.0 中,滚动到顶部似乎是固定的,因此不需要解决方法。

        【讨论】:

        • 谢谢你!奇迹般有效! ^^ 它适用于 4.3 模拟器。更喜欢这个解决方案,因为它消除了屏幕碰撞。对于想要完全控制其页面的 phonegap 开发人员非常有用