I would like to resize a WKWebView to fit above the keyboard in both landscape and portrait and to cope with user changing between landscape and portrait whilst the keyboard is in use. I would also like the WKWebView to be resized when the keyboard disappears.
The default behaviour is to scroll the WKWebView so that the text field in use is visible above the keyboard. I want to override this behaviour.
I expect that I will have to add some code to respond to keyboardDidShow and to keyboardDidHide, to change the GCRect used by the WebView. Are these the correct events to use?
How do I access the height of the keyboard in both portrait and landscape?
Do I have to add a delay to this, or will the default scrolling of the webview have already occurred by the time keyboardDidShow fires?
When I change the webview GCRect size, will this trigger window.onresize in the javascript?
Will doing this allow the device to be rotated whilst the keyboard is in use or do I need to add further code to handle this?
Thanks for any help anyone can give me.
To solve this I added the following declarations to ViewController.h
int appH ; // device width
int appW ; // device height
int keyH ; // keyboard height
int topMargin ; // safe area size at top of screen
int bottomMargin ; // safe area size at bottom of screen
bool smallScreen ; // whether want to see status bar in landscape
I expect that I should have declared these as properties of my ViewController object, but that is noting to do with the answer
I then create the web view as follows
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:(BOOL)animated];
NSString *deviceType = [UIDevice currentDevice].model;
smallScreen = [deviceType hasPrefix:@"iPhone"];
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
[theConfiguration.userContentController addScriptMessageHandler:self name:@"MyApp"];
[theConfiguration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];
// note that in viewDidLoad safeAreaInsets is set to zero
topMargin = [UIApplication sharedApplication].statusBarFrame.size.height ;
if (@available(iOS 11, *)) topMargin = self.view.safeAreaInsets.top ;
bottomMargin = 0 ;
if (@available(iOS 11, *)) bottomMargin = self.view.safeAreaInsets.bottom ;
int top = (smallScreen && appW > appH)? 0 : topMargin ;
int height = (smallScreen && appW > appH)? appH : appH - topMargin - bottomMargin ;
webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, top , appW, height) configuration:theConfiguration];
webView.navigationDelegate = self;
webView.UIDelegate = self;
I then react to changes in size as follows
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
// code here executes before rotation
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
// code here executes during rotation
appH = size.height ;
appW = size.width ;
int top = (smallScreen && appW > appH)? 0 : topMargin ;
int height = (smallScreen && appW > appH)? appH : appH - topMargin - bottomMargin ;
webView.frame = CGRectMake(0, top , appW, height);
}
completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
// code here executes after rotation
[webView evaluateJavaScript:@"IOSOrientationChanged()" completionHandler:nil];
}
];
}
I also register for notification of keyboard events as follows
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
And react to these in this way
- (void)keyboardWillShow: (NSNotification *) notif
{
keyH = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height ;
NSString *javascriptString ;
javascriptString = [NSString stringWithFormat:@"IOSKeyboardChanged(%d)",keyH];
[webView evaluateJavaScript:javascriptString completionHandler:nil];
}
- (void)keyboardWillHide: (NSNotification *) notif
{
[webView evaluateJavaScript:@"IOSKeyboardChanged(0)" completionHandler:nil];
}
In the above sections I have referred to two Javascript functions
IOSOrientationChanged redraws the app content based on the size of the screen found in window.innerWidth and window.innerHeight.
IOSKeyboard Changed does the same but uses window.innerHeight less the height of the keyboard (which is passed as the only parameter to it).
Points to note
UPDATE IOS 13.3.1
After updating to this version, I found that the code above was insufficient to prevent the web view occasionally scrolling partially out of sight. Please note the "occasionally". Most of the time the code worked as it did previously, however the occasional issues were sufficient to warrant a fix and to do this I did the following :
add to the ViewController interface statement
UIScrollViewDelegate
after creating the web view add the following line
webView.scrollView.delegate = self ;
also add
{ [scrollView setContentOffset:CGPointMake(0,0)]; };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With