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
Related
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>
I have an app and users are sharing screenshots with it from the app. The app is live and it was working correctly. Nowadays, I realized that when user clicks instagram story share, it goes to login page only. Even the user logins to instagram and re-clicks from my app to share story, it still goes to instagram login page. What is that issue?
- (IBAction)instagramClicked:(id)sender
{
[FIRAnalytics logEventWithName:#"instagram_share"
parameters:#{
#"name": #"instagram_share",
#"full_text": #"instagram_share"
}];
[self backgroundImage:UIImagePNGRepresentation([self getScreenshot])
attributionURL:#"abcapp://"];
}
//Get image of the view
- (UIImage *)getScreenshot {
CGRect rect = [_shareView bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,NO,2.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[_shareView.layer renderInContext:context];
UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return capturedScreen;
}
- (void)backgroundImage:(NSData *)backgroundImage
attributionURL:(NSString *)attributionURL {
// Verify app can open custom URL scheme, open if able
NSURL *urlScheme = [NSURL URLWithString:#"instagram-stories://share"];
if ([[UIApplication sharedApplication] canOpenURL:urlScheme]) {
// Assign background image asset and attribution link URL to pasteboard
NSArray *pasteboardItems = #[#{#"com.instagram.sharedSticker.backgroundImage" : backgroundImage,
#"com.instagram.sharedSticker.contentURL" : attributionURL}];
NSDictionary *pasteboardOptions = #{UIPasteboardOptionExpirationDate : [[NSDate date] dateByAddingTimeInterval:60 * 5]};
// This call is iOS 10+, can use 'setItems' depending on what versions you support
[[UIPasteboard generalPasteboard] setItems:pasteboardItems options:pasteboardOptions];
[[UIApplication sharedApplication] openURL:urlScheme options:#{} completionHandler:nil];
} else {
// Handle older app versions or app not installed case
}
}
This is a bug with the current version of the Instagram app. You have to wait for an update, even their apps like Boomerang are broken when sharing to Instagram.
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.
I want to have a UIWebView inside a UIScrollView. The UIWebView will sometimes go out of bounds of the iPhone screen so I need a way to scroll the view so I can see all the content (but I don't want to use the built in scrolling of the UIWebView). So I'm thinking of putting all the content inside of a UIScrollView and then making the height of the UIScrollView to equal the height of the UIWebView and other views that are in it.
Here's an image to help describing my problem:
I did exactly the same thing. Here's how I made it work:
Add the UIWebView as a subview of the UIScrollView (obviously ;-)
Disable the native scrolling of the UIWebView (you can do that by iterating through the subviews of the UIWebView until you find it's UIScrollView and set scrollEnabled = NO on it.
Set the contentSize of your UIScrollView and the UIWebView's frame to the size of the UIWebViews HTML content.
The last point is bit tricky because you cannot be sure that the HTML is completely rendered when webViewDidFinishLoad: gets called on the UIWebViewDelegate.
Here's a reliable way to get the HTML content size:
1.Add a javascript function to the HTML that gets called when the HTML Document is ready:
window.onload = function() {
window.location.href = "ready://" + document.body.offsetHeight;
}
This functions sends a request that has the content height in it's URL.
2.In your UIWebViewDelegate you intercept this request:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *url = [request URL];
if (navigationType == UIWebViewNavigationTypeOther) {
if ([[url scheme] isEqualToString:#"ready"]) {
float contentHeight = [[url host] floatValue];
yourScrollView.contentSize = CGSizeMake(yourScrollView.frame.size.width, contentHeight + yourWebView.frame.origin.y);
CGRect fr = yourWebView.frame;
fr.size = CGSizeMake(yourWebView.frame.size.width, contentHeight);
yourWebView.frame = fr;
return NO;
}
return YES;
}
Hope that helps
UPDATE
Here is the Swift 2 version:
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
guard navigationType == .Other else { return true }
if let url = request.URL, let host = url.host {
guard url.scheme == "ready" else { return true }
if let contentHeight = Float(host) {
yourScrollView.contentSize = CGSizeMake(yourScrollView.bounds.size.width, CGFloat(contentHeight))
var fr = webView.frame
fr.size = CGSizeMake(fr.size.width, CGFloat(contentHeight))
webView.frame = fr
}
return false
}
return true
}
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.