Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIWebView scrolling down on input focus iOS 6 only

I'm loading a UIWebView in a modal. The web page has some inputs that work exactly as intended in iOS 5. However in iOS 6, anytime an input gets focus, the keyboard is doing it's automatic 'centering' of the form even though the inputs have plenty of room to be shown above the keyboard. Since some of the inputs are at the top of the page, this forces them out of view so the user cannot see what they are typing. Scrolling back up to see the inputs causes the keyboard to stop working until dismissed and refocusing the input (in turn scrolling it out of view again). Is this an expected behavior in iOS 6? Is there any way to keep the webview from scrolling when an input gains focus (like how iOS 5 works)?

like image 741
Yeti42 Avatar asked Oct 25 '12 14:10

Yeti42


1 Answers

Without knowing the inner workings of your code, I'm going to try to help as much as I can. If nothing else, I hope to provide some food for thought. You asked two questions:

Is it expected behavior in iOS6?

What you're seeing is quite odd for sure. It's strange that the webView is centering the form rather than centering the input field. And it should definitely not cause the active input field to scroll out of view. Furthermore, the keyboard seems to stop working, which is quite odd. However, it is expected to see different behavior in iOS 5 and 6 with regards to the webView scrolling. As you said, iOS 5 scrolls the inputField into view while iOS6 puts it in center.

Is there any way to keep the webview from scrolling when an input gains focus (like how iOS 5 works)?

Yes. I provide code to do just this below -- namely to stop the webView from scrolling. If that is exactly what you want to do, then great. However, stopping the webView from scrolling is not the same as getting the same behavior as iOS5. Still, I wanted to give you this option as you requested it.

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate> {
    CGPoint origin;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
    [self.webView loadRequest:request];
    self.webView.scrollView.delegate = self;

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    // NOTE THAT SIMPLY DISABLING THE SCROLL WILL NOT PREVENT KEYBOARD SCROLL
    //[self.webView.scrollView setScrollEnabled:NO];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)keyboardWillShow {
    NSLog(@"keyboard");
    origin = self.webView.scrollView.contentOffset;

}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    NSLog(@"scroll");
    [self.webView.scrollView setContentOffset:origin];
}

@end

If you want consistent scroll behavior...

I took some time to code up this scroll modification. You can use it as a reference in making your own scroll behavior that is consistent across iOS 5&6. In this code: When the user clicks on a text input box on a webpage, the code will stop the default scrolling behavior by keeping the page scrolled to its current position. Unfortunately, there is no way to cancel all scrolling, but rather you have to override scrolls. Once the default scrolling has been "suppressed", it gets the position of the active input box and scrolls to that position. This will put the active input box in the top of the screen. You can modify this according to your preferences. For example, you can use [UIScrollView scrollRectToVisible] to make it more like iOS5.

#import "ViewController.h"

@interface ViewController () <UIScrollViewDelegate> {
    CGPoint origin;
    CGPoint activeElementOrigin;
    BOOL pauseScroll;
}

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
    [self.webView loadRequest:request];
    self.webView.scrollView.delegate = self;

    pauseScroll = NO;
    origin = CGPointZero;

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardDidShow)
                                                 name:UIKeyboardDidShowNotification
                                               object:nil];
}

- (void)keyboardWillShow {
    NSLog(@"keyboard");
    pauseScroll = YES;
    origin = self.webView.scrollView.contentOffset;
}

- (void)keyboardDidShow {
    NSLog(@"keyboard DID SHOW");
    pauseScroll = NO;
    [self.webView.scrollView setContentOffset:activeElementOrigin animated:YES];
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
    NSLog(@"scroll");

    if (pauseScroll) {
        [self.webView.scrollView setContentOffset:origin animated:NO];

        NSString *javaScript = [NSString stringWithFormat:@"function f(){ var textField = document.activeElement; return textField.getBoundingClientRect().top; } f();"];
        NSString *textFieldRectTop = [self.webView stringByEvaluatingJavaScriptFromString:javaScript];
        activeElementOrigin = origin;
        activeElementOrigin.y = [textFieldRectTop floatValue]-10;
    }
}


@end

If this code helped you, it would be very nice of you to reward the bounty to me. I would be humbly appreciative.

like image 96
Ed Chin Avatar answered Oct 29 '22 15:10

Ed Chin