WKWebview: On click of button in app, play directly embedded Youtube Video - objective-c

I have a play button overlayed on an UIImageView (headerView) in my app. After the user clicks this button I want to display an embedded YoutubeVideo directly within the frame of this UIImageView. However, first my Youtube Video doesn't auto play and 2nd the size is way too small. What am I doing wrong? I have been googling all possible answers and tried everything. Or is it a simulator related issue? Here is my code which is triggered by the click of the play button:
NSString *videoIdentifier = #"EXaEz0mJXWY";
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.headerView.bounds];
[webView.configuration setAllowsInlineMediaPlayback:YES];
[webView.configuration setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeNone];
[self.headerView addSubview:webView];
NSString* embedHTML = [NSString stringWithFormat:#"\
<html>\
<body style='margin:0px;padding:0px;'>\
<script type='text/javascript' src='http://www.youtube.com/iframe_api'></script>\
<script type='text/javascript'>\
function onYouTubeIframeAPIReady() \
{\
ytplayer=new YT.Player('playerId',{events:{onReady:onPlayerReady}})\
}\
function onPlayerReady(a)\
{ \
a.target.playVideo(); \
}\
</script>\
<iframe id='playerId' type='text/html' width='%0.0f' height='%0.0f' src='http://www.youtube.com/embed/%#?enablejsapi=1&rel=0&playsinline=1&autoplay=1' frameborder='0'>\
</body>\
</html>", webView.frame.size.width, webView.frame.size.height, videoIdentifier];
//NSLog(#"Embeddedhtml: %#", embedHTML);
[webView loadHTMLString:embedHTML baseURL:[[NSBundle mainBundle] resourceURL]];

my Youtube Video doesn't auto play
You have to initialize WKWebView with configuration parameter. Changing configuration afterwards has no effect.
WKWebViewConfiguration* config = [[WKWebViewConfiguration alloc] init];
[config setAllowsInlineMediaPlayback:YES];
[config setMediaTypesRequiringUserActionForPlayback:WKAudiovisualMediaTypeNone];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.headerView.bounds configuration:config];
the size is way too small
WKWebView is using actual device pixels for measuring, and some heuristics for default scaling, whereas UIKit returns width and height in points. Simple fix would be to add viewport meta to the page:
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
</head>

Related

Objective C iOS WKWebView Background

Hopefully someone can help.
I've got an application that currently uses the UIWebview to play audio, which works even if the device is locked as I enable mediaPlaybackAllowsAirPlay.
However I started to move the code to use the new WKWebView and I've enabled mediaPlaybackAllowsAirPlay, However when I press the lock button, the WebView stops playing the audio.
Any ideas how I can get WKWebview to behave like the UIWebView?
Update, Here's an example of my code:
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
theConfiguration.userContentController = wkUController;
theConfiguration.allowsAirPlayForMediaPlayback = YES;
webViewActive = [[WKWebView alloc] initWithFrame:viewForWeb.frame configuration:theConfiguration];
webViewActive.navigationDelegate = self;
webViewActive.frame = viewForWeb.bounds;
[webViewActive setAutoresizingMask: UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[viewForWeb addSubview:webViewActive];
NSURL *nsurl=[NSURL URLWithString:#"URL TO AUDIO"];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[webViewActive loadRequest:nsrequest];
Thanks

WebView: Blocked a frame with origin www.youtube.com

I get this message when I try to play youtube videos in my WebView
Blocked a frame with origin "https://www.youtube.com" from accessing a
frame with origin
"applewebdata://b102c2f1-19a4-4dea-bff2-b131be89929f". The frame
requesting access has a protocol of "https", the frame being accessed
has a protocol of "applewebdata". Protocols must match.
The baseUrl of my webview is currently set to nil.
I know I should change it to #"http://", but a lot of links and stuff in the view is based on it. If I change it I get a blank screen, because the html breaks.
So I was trying to intercept the call to youtube, when the user click on the youtube link, to change the baseUrl on the fly. But this seems to be not possible. For other actions I use this function and it works well:
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
How could I solve this issue? The videos were working correctly on previous OSX and I'm using the usual iFrame snippet to include the video.
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}"
src="http://www.youtube.com/embed/{$youtubelink}?html5=1" frameborder="0">
UPDATE
I've to encode the url as workaround:
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}" src="data:text/html;base64,aHR0cDovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC97JHlvdXR1YmVsaW5rfT9odG1sNT0x" frameborder="0">
but what I get is the URL as string text in the html.
UPDATE 2
Using Google APIs.
<div id="youtubep" class="youtube-player"></div>
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('youtubep', {
height: '390',
width: '640',
videoId: 'M7lc1UVf-VE',
events: {
'onReady': onPlayerReady
}
});
}
function onPlayerReady(event) {
event.target.playVideo();
}
</script>
Still it is not working:
[Warning] Untrusted origin: applewebdata://f53223b5-2587-4c0e-9937-bf3e9b07ff92 (www-embed-player.js, line 152, x26)
[Error] XMLHttpRequest cannot load https://redirector.googlevideo.com/videoplayback?mt=1449582813&mv=m&ms=au&source=youtube&key=yt6&clen=41094349&requiressl=yes&mm=31&mn=sn-5hnedn76&gcr=nl&initcwndbps=1451250&id=o-ALVwEvIylUeyOATePUVUFg-cbUR-qvn9AnZ9FdKuwcLH&upn=RA7SrQEDQdw&lmt=1432476385386698&ip=95.97.243.202&sparams=clen%2Cdur%2Cgcr%2Cgir%2Cid%2Cinitcwndbps%2Cip%2Cipbits%2Citag%2Clmt%2Cmime%2Cmm%2Cmn%2Cms%2Cmv%2Cnh%2Cpl%2Crequiressl%2Csource%2Cupn%2Cexpire&fexp=9407194%2C9408500%2C9408710%2C9415030%2C9416126%2C9417204%2C9417683%2C9418204%2C9419541%2C9420310%2C9420452%2C9422141%2C9422596%2C9422618%2C9423662%2C9424480&gir=yes&pl=18&dur=1343.599&sver=3&expire=1449604537&nh=IgpwcjA0LmFtczE2Kg4yMTMuNDYuMTgyLjEwNQ&mime=video%2Fmp4&itag=133&signature=4D2797CB2BE5FCFA7CB78FACD14E8423EC2013B7.329BD308AA58011A920BAEFACA0F5869C7B84FC1&ipbits=0&cpn=b5qfNKIpHmOvNtyU&alr=yes&keepalive=yes&ratebypass=yes&c=WEB&cver=html5&cmo=pf=1&range=0-3730&rn=12&playerretry=2&rbuf=0. Origin https://www.youtube.com is not allowed by Access-Control-Allow-Origin.
Basically no need of embedCode.
float width = 200.0f;
float height = 200.0f;
UIWebView *wv = [UIWebView new];
wv.frame = CGRectMake(60, 60, width, height);
wv.delegate = self;
// URL from safari address bar.
NSURL *url = [NSURL URLWithString:#"http://www.youtube.com/watch?v=fDXWW5vX-64"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[wv loadRequest:request];
[self.view addSubview:wv];
and add the UIWebViewDelegate protocol to the View controller.Hope it works.
Going the javascript route has worked well for me:
https://developers.google.com/youtube/iframe_api_reference?hl=en
I did have to add a base URL of "https://" to be able to interact with the callback events. Otherwise, they didn't appear to get fired.
Try replacing the iframe with the following:
<iframe class="youtube-player" type="text/html" width="{$width}" height="{$height}" src="data:text/html,<iframe src="http://www.youtube.com/embed/{$youtubelink}?html5=1" width=100% height=100% frameborder=0>" frameborder="0"></iframe>
Not 100% sure if this is the answer, but this is how I get videos to play with iframe. Maybe it will help you a bit:
NSString *website = model.contentVideoURL;
NSInteger height = self.view.frame.size.height - self.topView.frame.size.height - 70;
NSString *embedCode = [NSString stringWithFormat:#"<iframe width=\"100%%\" height=\"%li\" src=\"%#\" frameborder=\"0\" allowfullscreen></iframe>",(long)height, website];;
[[self webView] loadHTMLString:embedCode baseURL:nil];
Also try adding:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
To your info.Plist source code

Playing Youtube Video from UIWebView inside UIModalPresentationPageSheet limits full screen to size of modal view

I have UIWebView inside a modal popover that's a PresentationPageSheet style. When Youtube videos are played in full screen the expected behaviour is for them to popover full screen. Instead they just play inside the popover controllers dimensions.
The simple answer is that you have to embed the video inside an iframe.
if (self.youTubeID) {
NSString *embeddedYouTubeURL = [NSString stringWithFormat:#"http://www.youtube.com/embed/%#",self.youTubeID];
//NSString *controls = #"?controls=2&autohide=1";
self.webView.opaque = NO;
self.webView.backgroundColor = [UIColor blackColor];
NSString *html = [NSString stringWithFormat:#"<!DOCTYPE HTML><head></head><html><body><iframe width='%f' height='%f' src='%#' allowfullscreen></iframe></body></html>", self.webView.frame.size.width *2, self.webView.frame.size.height*2, embeddedYouTubeURL];
[self.webView loadHTMLString:html baseURL:nil]; }

UIWebView scaling page so that there is no need for horizontal scrolling

Is there a way to scale a web page in a UIWebView that maintains the aspect ratio but alleviates the need to scroll horizontally to read an article, for example. Vertical scrolling is fine, I just don't want the user to have to constantly be scrolling back and forth horizontally to read each line of the article. Thanks!
Edit: the code I'm using to create the view
_webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, [[self view] frame].size.width, [[self view] frame].size.height)];
[_webView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[_webView setDelegate:self];
[_webView setScalesPageToFit:YES];
[_webView loadRequest:[NSURLRequest requestWithURL:[headline link]]];
The link is always formatted for mobile already.
I think that defining the viewport for your UIWebView could help:
<meta name="viewport" content="width=device-width"/>
this is to use the full width of the device; if your UIWebView is smaller, that you can specify its width in points.
In case you need to add the meta tag to the HTML of your page after loading it, you can use this code:
- (void) webViewDidFinishLoad:(UIWebView *)webView {
NSString* js =
#"var meta = document.createElement('meta'); "
"meta.setAttribute( 'name', 'viewport' ); "
"meta.setAttribute( 'content', 'width = device-width' ); "
"document.getElementsByTagName('head')[0].appendChild(meta)";
[webView stringByEvaluatingJavaScriptFromString: js];
}
Also check this S.O. thread for another technique for the same.

Play youtube videos in fullscreen auto

I am trying to play a youtube video using youtube embedded player in my ipad app. I want that as soon as the user clicks on the video thumbnail the video automatically should be loaded fullscreen on switching to landscape mode in ipad. Currently the user has to click the fullscreen button to play the video in fullscreen mode.
I want the same effect that is seen when we play the video using the default youtube app that comes with ipad.
This is the code that I am using to play you tube video on ipad.
embedHTML = #"<object width=\"640\" height=\"390\">
<param name=\"movie\"value=\"http://www.youtube.com/v/IYX_Ql-3U10&fs=1\">
<param name=\"allowFullScreen\" value=\"true\"></param>
<param name=\"allowScriptAccess\" value=\"always\"></param>
<embed id=\"yt\" src=\"http://www.youtube.com/v/youtube_video_id&fs=1\"
type=\"application/x-shockwave-flash\" position=\"fixed\"
allowfullscreen=\"true\" allowScriptAccess=\"always\"
width=\"640\" height=\"390\"></embed></object>";
And if this is not possible does anybody know if there is a reason or documentation supporting this decision.
You can't, it's not allowed :)
I was able to access this functionality from a subview titled FigPluginView within the youtube player's webview, using the iOS youtube helper iOS-youtube-player-helper.
FigPluginView class dump reference
Create a custom UIView class header that exposes the following method:
#import <Foundation/Foundation.h>
#interface WMFigPluginView : NSObject
-(void)scriptEnterFullScreen;
#end
Import the custom FigPluginView and YouTube player to the ViewController interface()
#import <youtube-ios-player-helper/YTPlayerView.h>
#import "WMFigPluginView.h"
#property(nonatomic, strong) YTPlayerView *playerView;
Use the following methods to init the youtube player, monitor state change, find and call the fullscreen script within the FigPluginView.
- (void)presentYoutubeVideo {
if ([self.view.subviews containsObject:_playerView]){
[_playerView removeFromSuperview];
}
CGRect playerFrame = CGRectMake(0, 0, 0, 0);
_playerView = [[YTPlayerView alloc]initWithFrame:playerFrame];
_playerView.delegate = self;
[_playerView.webView setAllowsInlineMediaPlayback: NO];
[self.view addSubview:_playerView];
[self.playerView loadWithVideoId:#"YouTubeVideoID" playerVars:#{#"showinfo":#0,#"modestbranding":#1,#"autoplay":#1,#"playsinline":#0}];
}
-(void)playerViewDidBecomeReady:(YTPlayerView *)playerView {
[_playerView playVideo];
}
-(void)playerView:(YTPlayerView *)playerView didChangeToState:(YTPlayerState)state {
//stop activity indicator
if (state == kYTPlayerStateBuffering) {
//start activity indicator
}
if (state == kYTPlayerStatePlaying) {
WMFigPluginView *figPluginView = (WMFigPluginView*)[self findFigPluginView:_playerView.webView];
[figPluginView scriptEnterFullScreen];
}
}
- (UIView *)findFigPluginView:(UIView *)view {
for (__unsafe_unretained UIView *subview in view.subviews) {
if ([NSStringFromClass(subview.class) hasSuffix:#"FigPluginView"]) {
return subview;
} else if (subview.subviews.count > 0) {
return [self findFigPluginView:subview];
}
}
return nil;
}
Hope this helps!
Looks like there is no straightforward solution. If my client will want it (I will know in a few days :-)) I will probably try to open new controller with webview that covers entire screen and then autoplay it like in this post:
How to make YouTube video starts to play automatically inside UIWebView
With respect to the authors of the other answers, it is completely possible to do this using UIWebViews in iOS AND without leveraging private APIs. Here's a pretty simple method to do so.
- (void)playYoutubeVideo:(NSString *)videoId {
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
webView.center = self.view.center; //Technically not needed, I've found it looks better with this added (when the video is done playing it looks better while being minimized)
webView.mediaPlaybackRequiresUserAction = NO;
[self.view addSubview:webView];
NSString *youTubeVideoHTML = [NSString stringWithFormat:#"<html><head><style>body{margin:0px 0px 0px 0px;}</style></head> <body> <div id=\"player\"></div> <script> var tag = document.createElement('script'); tag.src = 'http://www.youtube.com/player_api'; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); var player; function onYouTubePlayerAPIReady() { player = new YT.Player('player', { width:'768', height:'1024', videoId:'%#', events: { 'onReady': onPlayerReady } }); } function onPlayerReady(event) { event.target.playVideo(); } </script> </body> </html>", videoId];
[webView loadHTMLString:youTubeVideoHTML baseURL: [NSBundle mainBundle].bundleURL];
}
The crux of this implementation is using a UIWebView with a frame of CGRectZero together with custom HTML/Javascript that handles autoplaying the video. With the UIWebView with the CGRectZero frame, the user doesn't see any annoying intermediate page before the video is autoplayed, and the HTML/Javascript does the heavy lifting involved with getting the page to autoplay the video.
There's a slight delay before the video is presented. I haven't found a way to eliminate this couple second delay unfortunately.