WKWebView shows a cropped background and blank space after rotation on iPad - ios

I'm migrating my app from UIWebView to WKWebView for iOS8.x (maintaining support for iOS7.x) users and I'm having a trouble with the website I'm displaying, because after rotating the iPad, it displays the content but the background seems somehow cropped and therefore there is a blank space after the bg.
However as soon as I start scrolling the website, it fixes automatically by itself. This blank effect also happens when I pass from landscape to portrait orientation, which leaves the right side of the website with the blank background.
Here's how I tryed solving the issue:
(void)willRotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation duration (NSTimeInterval)duration
{
[self.view setNeedsUpdateConstraints];
[_webView setNeedsUpdateConstraints];
}
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self.view setNeedsUpdateConstraints];
[_webView setNeedsUpdateConstraints];
}
I've tried many things (reloading the background via JS, handling the willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation methods as shown above) but have not been able to solve the issue, and it only happens when using WKWebView, if I switch back to UIWebView, the website renders properly.
I also tested the website on Safari for the very same device (an iPad2 with iOS 8.1.3) and it doesn't show the error, no matter how many times I rotate the iPad, so I'm sure it can be solved =). Can anybody please help me?

This is a bug in WKWebKit that's usually triggered by HTML elements with position: fixed applied. The only workaround I've found is to use window.scrollTo() to scroll the page by one pixel a few hundred miliseconds after a resize event.

This save my life:
self.wkWebView.evaluateJavaScript("window.scrollBy(1, 1);window.scrollBy(-1, -1);", completionHandler: nil)
Add this line on wkwebview did finish navigation event!!

Took me a while, but I got it working for pages without scroll:
window.addEventListener('resize', function(){
setTimeout(function () {
var vp = document.getElementsByName("viewport")[0],
vpContent = vp.content,
vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001;
vp.content = "user-scalable=no, initial-scale="+vpHack+", maximum-scale="+vpHack+", minimum-scale="+vpHack+",width=device-width";
setTimeout(function(){vp.content=vpContent;},10);
}, 500);
}, true);
That's assuming you use viewport. it changes the viewport by 0.0001 for 10 milliseconds
I implemented it like this:
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(#"###### FINISH");
[self.webView evaluateJavaScript:[NSString stringWithFormat:#"if (document.readyState==='complete'){ window.addEventListener('resize', function(){setTimeout(function () {var vp = document.getElementsByName('viewport')[0],vpContent = vp.content,vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001; vp.content = 'user-scalable=no, initial-scale='+vpHack+', maximum-scale='+vpHack+', minimum-scale='+vpHack+',width=device-width';setTimeout(function(){vp.content=vpContent;},10);}, 500);}, true); } else { window.addEventListener('load', function(){window.addEventListener('resize', function(){setTimeout(function () {var vp = document.getElementsByName('viewport')[0],vpContent = vp.content,vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001; vp.content = 'user-scalable=no, initial-scale='+vpHack+', maximum-scale='+vpHack+', minimum-scale='+vpHack+',width=device-width';setTimeout(function(){vp.content=vpContent;},10);}, 500);}, true);}, false );}"] completionHandler:nil];
}

I use document.head.appendChild(document.createElement('style')).remove();
to tickle my WKWebViews into redrawing after resize/rotate.

The following workaround worked for me, but unfortunately only on the Simulator:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// Workaround for WKWebView iOS 8 Rotation Bug
// Official Fix (iOS 9): http://trac.webkit.org/changeset/169625
if ([[[UIDevice currentDevice] systemVersion] integerValue] == 8) {
CGRect frame = self.view.frame;
frame.size.width += 1;
self.view.frame = frame;
frame.size.width -= 1;
self.view.frame = frame;
}
}
After some experimenting I got it to work on the device too using the following steps:
Keep two sets of AutoLayout constraints, the first one for the behavior you actually intend and the second with a small change (e.g. 1 point size difference)
Whenever the orientation changes, apply the second set to the view, then, after 1 second, apply the first constraints set.
While this solution is far from ideal, it is the only one I found without manipulating the Javascript on the website. I tried to lower the delay, but in my tests I had to wait for at least 0.8 seconds.

Related

iOS YTPlayerView force video quality

I am currently using iOS-youtube-player-helper library in our application. There is a view controller, with a YTPlayerView that has an aspect ratio of 16:9, which means it takes only a part of the screen. The video is loaded in medium and no matter how, I could not get it to play in 720P or 1080P. I am certain that these qualities are available, it's just the YTPlayerView forcing the quality based on the video player height. Because this is a library and not direct iframe embed, I cannot use "vq" parameter(specifying vq in playerVars does not seem to work), and setting the quality to be small then change it later does not work either(refer to this issue on GitHub)
Now, given the factor that I cannot make the YTPlayerView to fill up the whole screen, because of UI designing issues. So, is it possible to force the YTPlayerView to play in at least 720P? (Workarounds, changing the library code, ...)
Because this is an app that will be on App Store(and of course we don't want to have any legal disputes with google either), please don't suggest using libraries that are against the Youtube ToC such as XCDYouTubeKit
Many Thanks
I've found a workaround and this works well for me.
First of all, the problems depends by the webView size constructed inside the YTPlayerView. For example if you have a 320x200 playerView, try to forcing your video to 720hd don't work because the iFrame youtube player class re-switch to a better resolution according to your player size (in this case small quality because you have 320x200).
You can see this SO answer that explain this issue.
When you have imported the YTPlayerView class to your project you have two files: YTPlayerView.h and YTPlayerView.m
YTPlayerView.m (Update to work also on iPads)
I've change the function where the webview is initialized with a custom size (4k resolution) and to the last part I've added the possibility to scale the contents and restore the original frame, like this:
- (UIWebView *)createNewWebView {
CGRect frame = CGRectMake(0.0, 0.0, 4096.0, 2160.0); //4k resolution
UIWebView *webView = [[UIWebView alloc] initWithFrame:frame];
//UIWebView *webView = [[UIWebView alloc] initWithFrame:self.bounds];
webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
webView.scrollView.scrollEnabled = NO;
webView.scrollView.bounces = NO;
if ([self.delegate respondsToSelector:#selector(playerViewPreferredWebViewBackgroundColor:)]) {
webView.backgroundColor = [self.delegate playerViewPreferredWebViewBackgroundColor:self];
if (webView.backgroundColor == [UIColor clearColor]) {
webView.opaque = NO;
}
}
webView.scalesPageToFit = YES;
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
CGSize contentSize = webView.scrollView.contentSize;
CGSize viewSize = self.bounds.size;
float scale = viewSize.width / contentSize.width;
webView.scrollView.minimumZoomScale = scale;
webView.scrollView.maximumZoomScale = scale;
webView.scrollView.zoomScale = scale;
// center webView after scaling..
[webView setFrame:CGRectMake(0.0, self.frame.origin.y/3, 4096.0, 2160.0)];
} else {
webView.frame = self.bounds;
}
[webView reload];
return webView;
}
Hope this helps who try to use the Youtube original player in his project.
P.S.: All my tries were did with a swift project and the following vars:
var playerVars:Dictionary =
["playsinline":"1",
"autoplay":"1",
"modestbranding":"1",
"rel":"0",
"controls":"0",
"fs":"0",
"origin":"https://www.example.com",
"enablejsapi":"1",
"iv_load_policy":"3",
"showinfo":"0"]
Using the functions:
self.playerView.load(withVideoId: "Rk6_hdRtJOE", playerVars: self.playerVars)
and the follow method to force the resolution:
self.playerView.loadVideo(byId: "Rk6_hdRtJOE", startSeconds: 0.0, suggestedQuality: YTPlaybackQuality.HD720)
About iPads:
As reported by Edward in comments there was a little problem on iPads, that's because these devices seems don't apply scalesPageToFit. The goal is to check if the device is an iPad, then to scale (zooming out) the scrollView to host the little view bounds.
I've tested on my iPad air and it works. Let me know.

How can I scale the fixed size html content to fit the UIwebview frame size?(the content size is much smaller than the frame size)

I am now doing a advertSDK project like inmobi, the user is allowed to create the banner frame and locate them by CGrectmake, so I will create a UIWebview as they required, then in the UIwebview I just to load the html content to show it in the right position.
The question is: the banner content has a fixed size : 360 * 100, however in iphone 6s simulator, it is too small, I tried to set
webView.scalesPageToFit = YES
but it has no effects.
Anyone has a good idea to solve the problem? Thank you.
You have to use the UIWebView delegate methods and resize the web view like I'm doing here:
- (void) webViewDidFinishLoad:(UIWebView *)webView {
webView.frame = CGRectMake(webView.frame.origin.x, webView.frame.origin.y, webView.frame.size.width, webView.scrollView.contentSize.height);
}

How to overlay two images in ios6 with transparency

I am trying to overlay two images and put text on top in a view that I have. I have this working perfectly in ios7. Here is a screen shot of the results
Right now the gradient is simply an image on top of the other image as seen here in my layout
This works great except for when I test on my phone with ios6. Then everything goes nuts as seen here. *I've actually deleted the gradient layer and ran the app again and the background image remains the same size (about half of what it should be).
As you can see, the background image is only half of what it should be, and the second image is not overlaying. I've been at this for 5 hours and can't seem to find a solution that works.
Here is the code that sets the background image
-(void) SetDetails
{
if(_curInfo)
{
_lblTopName.text = _curInfo.company_name;
if(!_curInfo.img)
{
showActivity(self);
dispatch_queue_t aQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue1, ^{
_curInfo.img = getImageFromURL([NSString stringWithFormat:#"%#%#", g_serverUrl, _curInfo.imgPath]);
dispatch_async(dispatch_get_main_queue(), ^{
hideActivity();
[_imgCompany setImage:_curInfo.img];
});
});
}
[_imgCompany setImage:_curInfo.img];
/* FIX IMAGE SIZE */
_imgCompany.contentMode=UIViewContentModeScaleAspectFill;
CGRect photoFrame = _imgCompany.frame;
photoFrame.size = CGSizeMake(320, 180);
_imgCompany.frame=photoFrame;
[_imgCompany setClipsToBounds:YES];
_lblDistance.text = [NSString stringWithFormat:#"%.2f miles", _curInfo.distance];
_lblReward.text=_curInfo.reward;
CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scroller.subviews)
{
scrollViewHeight += view.frame.size.height;
}
[scroller setContentSize:(CGSizeMake(320, scrollViewHeight))];
}
}
Any help is greatly appreciated. I'm not opposed to drawing the gradient onto the image either.
Additional Info:
Here is how I have the two image views setup.
You need to unstick the 'opaque' option on your image view, given that it isn't opaque.
As to the other spacing issue, I'd guess it's a mismatch between iOS 7 view controllers always acting as if they had wantsFullScreenLayout set to YES but the default having been NO under iOS 6. The rest of your image is probably underneath your navigation bar. It looks like you're part trying the interface builder and part doing programmatic layout — why did you add the code underneath FIX IMAGE SIZE and what happens if you remove it?

blank space above the bar?

I have a iphone app which is installed in iPad its UI(blank space about 20 pixels above navigation bar) is correctly displayed in ios6 while a blank space appears in ios7 in iPad.
I tried using the default images and using the methods
- (void) viewDidLayoutSubviews {
if (([[UIDevice currentDevice].systemVersion floatValue] >= 7.0)) {
CGRect viewBounds = self.view.bounds;
viewBounds.origin.y = -20;
self.view.bounds = viewBounds;
}
}
but no use.How do i fix it? It is not a universal app.
The problem is related on the way iOS7 draws the views, please rewfer to these guide to understand the problem:
Explaining difference between automaticallyAdjustsScrollViewInsets, extendedLayoutIncludesOpaqueBars, edgesForExtendedLayout in iOS7

Get height of UIWebView loaded content

So what I am after is a way to put a UIButton on top of a UIScrollView and a UIWebView under it. The reason I want it this way is that I need the button to "scroll away" as the user scrolls the page I load into the UIWebView.
In order for this to work, I need to get the height of the content of the web page I load and then set the height of the web view to match this. If I can do this, I then intend to set the contentSize of the UIScrollView to match the heights of the button and web view.
I understand that somehow this is supposed to happen in the - (void)webViewDidStartLoad:(UIWebView *)webView method, which is confirmed on many threads on stackoverflow (for example this one). I also know that Apple says you shouldn't put a UiWebView in a UIScrollView, since the scrolling actions may interfere. Disabling scroll for the web view should avoid the problem though.
There are many threads discussing this matter, but none of them seem to work for me. Can it be because I am running on iOS7?
I am in big desperation here, help is much appreciated!
You can get height using following code :
NSString *heightStrig = [webView stringByEvaluatingJavaScriptFromString:#"(document.height !== undefined) ? document.height : document.body.offsetHeight;"];
float height = heightStrig.floatValue;
complete code :
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGRect frame = webView.frame;
NSString *heightStrig = [webView stringByEvaluatingJavaScriptFromString:#"(document.height !== undefined) ? document.height : document.body.offsetHeight;"];
float height = heightStrig.floatValue + 10.0;
frame.size.height = height;
webView.frame = frame;
}
You can get the height of the content loaded into a UIWebView as follows:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
CGFloat contentHeight = webView.scrollView.contentSize.height;
}
And then adjust the frame of the web view accordingly.

Resources