在iPad上检测UIWebView完成播放YouTube视频

10
我正在使用 UIWebView 在iPad上播放YouTube视频。
如何检测YouTube视频何时播放完毕? 我看到状态栏中的播放图标,并尝试使用 MPMusicPlayerController 通知来检测 playbackStateDidChange,但它没有起作用。
有什么办法可以检测到这个事件?再次说明,我是在iPad而不是iPhone上操作。
先感谢您。
更新: 如果您使用零解决方案来检测播放结束,并希望Youtube视频自动开始,请将 UIWebView 设置为:
self.webView.mediaPlaybackRequiresUserAction = NO ;

我想要澄清一下关于YouTube帧API的内容:

"重要说明:这是一个实验性功能,这意味着它可能会出现意外变化"(08/05/2012)

4个回答

15
不,无法直接从UIWebView获取网页事件。但是我们可以使用Javascript来完成这个任务。
  • 首先,在您的自定义HTML中嵌入Javascript以检测视频结束播放事件。
  • 然后,尝试使用JavaScript加载一个自定义方案请求,UIWebView可以捕获该请求。

以下链接可能有所帮助:

  1. 在iPhone UIWebView中从JavaScript调用Objective-C

  2. Objective-C中的Javascript回调

  3. YouTube iFrame API

更新示例:

在UIWebView的代理中,我添加了:

#pragma - mark WebView Delegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {


if ( [[[request URL] scheme] isEqualToString:@"callback"] ) {

    NSLog(@"get callback");

    return NO;
}

return YES;

}

网页在 viewDidLoad 时加载:
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle       mainBundle] pathForResource:@"youtube" ofType:@"html" ]]]];

在youtube.html中,我放置了:


<html>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>

<script>

  // 2. This code loads the IFrame Player API code asynchronously.
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

  // 3. This function creates an <iframe> (and YouTube player)
  //    after the API code downloads.
  var player;
  function onYouTubePlayerAPIReady() {
    player = new YT.Player('player', {
      height: '390',
      width: '640',
      videoId: 'u1zgFlCw8Aw',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange
      }
    });
  }

  // 4. The API will call this function when the video player is ready.
  function onPlayerReady(event) {
    event.target.playVideo();
  }

  // 5. The API calls this function when the player's state changes.
  //    The function indicates that when playing a video (state=1),
  //    the player should play for six seconds and then stop.
  var done = false;
  function onPlayerStateChange(event) {

    if (event.data == YT.PlayerState.PLAYING && !done) {
      setTimeout(stopVideo, 6000);
      done = true;
    }
    if (event.data == YT.PlayerState.ENDED) {
      window.location = "callback:anything"; //here's the key
    };
  }
  function stopVideo() {
    player.stopVideo();
  }
</script>
</body>
</html>

你可以看到我添加了

if (event.data == YT.PlayerState.ENDED) {
      window.location = "callback:anything";
    };

这是关于YouTube的iFrame API演示,它捕获了播放器结束播放事件,并尝试使用“callback”方案加载请求,然后UIWebView代理可以捕获它。

您可以使用此方法使用JavaScript触发任何事件;


但这取决于YouTube播放器是否支持此事件,对吧? 你成功做到了吗? - user1105951
@user1105951,是的,YouTube播放器支持称为“onStateChange”的结束事件,并且您应该按照YouTube JavaScript API YouTube JavaScript API - Events添加一个侦听器。 - ZeR0
@Zero,我查看了你的链接,并且我记得已经测试过了。请尝试在iPad的Safari浏览器上运行此演示:https://developers.google.com/youtube/youtube_player_demo ,你会看到:“您需要Flash Player 9+和启用JavaScript才能查看此视频”。 - user1105951
@user1105951,iOS设备上的Safari浏览器不支持Flash,因此您需要遵循YouTube的iFrame API来加载HTML5播放器。很抱歉之前给您带来了误导,现在我更新了我的答案并提供了一个我编写和测试过的简单示例,它可以正常工作。希望能对您有所帮助。 - ZeR0
@zero - 我尝试了上面的代码,但 WebView 没有显示初始视频图像。我也尝试使用 iFrame API。在这两种情况下,JavaScript 代码都没有被调用。iFrame HTML 页面-https://developers.google.com/youtube/iframe_api_reference - Salil
显示剩余5条评论

11

当视频播放完毕时,重新加载YouTube页面是很好的选择,否则它将显示由YouTube提供的相关视频。 - chings228
3
不要浪费时间尝试其他解决方案。这个方法非常有效,只需要一行代码。无论如何,这是调用观察者的代码,仅供参考 - (void)youTubePlayed:(id)sender。 - Alejandro Luengo
2
只有当用户播放视频到结尾并按下“完成”按钮时,此功能才能正常工作。如果他们没有将视频播放完毕,此通知将不会被调用。 - Enrico Susatyo
太好了!UIWebView甚至没有为嵌入的视频提供重播按钮。唯一(简单)可用的解决方案是通过在youTubePlayed:(id)中重新加载视频来避免结尾处的“相关视频”。 - cirronimbo

3

以下是@zero先生为您提供的完整类作为参考:

@interface YouTubeWebView () <UIWebViewDelegate>

@end


@implementation YouTubeWebView 

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self == nil) return nil;

    self.mediaPlaybackRequiresUserAction = NO;
    self.delegate = self;
    self.alpha = 0;

    return self;
}

- (void)loadVideo:(NSString *)videoId
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"youtube" ofType:@"html"];
    //    if (filePath == nil)

    NSError *error;
    NSString *string = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
    // TODO: error check

    string = [NSString stringWithFormat:string, videoId];

    NSData *htmlData = [string dataUsingEncoding:NSUTF8StringEncoding];
    //    if (htmlData == nil)

    NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *targetPath = [documentsDirectoryPath stringByAppendingPathComponent:@"youtube.html"];
    [htmlData writeToFile:targetPath atomically:YES];

    [self loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:targetPath]]];
}

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] scheme] isEqualToString:@"callback"]) {
        [self removeFromSuperview];

        NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        NSString *targetPath = [documentsDirectoryPath stringByAppendingPathComponent:@"youtube.html"];
        NSError *error;
        [[NSFileManager defaultManager] removeItemAtPath:targetPath error:&error];
//        TODO: error check
    }

    return YES;
}

请使用这个版本的youtube.html,其中视频ID的位置有替换代码(%@):
<html>
<head><style>body{margin:0px 0px 0px 44px;}</style></head>
<body>
<!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
<div id="player"></div>

<script>
  // 2. This code loads the IFrame Player API code asynchronously.
  var tag = document.createElement('script');
  tag.src = "http://www.youtube.com/player_api";
  var firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

  // 3. This function creates an <iframe> (and YouTube player)
  //    after the API code downloads.
  var player;
  function onYouTubePlayerAPIReady() {
    player = new YT.Player('player', {
      height: '320',
      width: '480',
      videoId: '%@',
      events: {
        'onReady': onPlayerReady,
        'onStateChange': onPlayerStateChange
      }
    });
  }

  // 4. The API will call this function when the video player is ready.
  function onPlayerReady(event) {
    event.target.playVideo();
  }

  // 5. The API calls this function when the player's state changes.
  //    The function indicates that when playing a video (state=1),
  //    the player should play for six seconds and then stop.
  var done = false;
  function onPlayerStateChange(event) {
    if (event.data == YT.PlayerState.PLAYING && !done) {
      setTimeout(stopVideo, 6000);
      done = true;
    }
    if (event.data == YT.PlayerState.ENDED) {
      window.location = "callback:anything"; 
    };
  }
  function stopVideo() {
    player.stopVideo();
  }
</script>
</body>
</html>

我需要克服的主要障碍是将文件作为字符串加载以进行替换。不幸的是,它必须被重新编写为文件才能使自动播放功能正常工作。如果这对您的用例不是必需的,请直接将HTML作为字符串加载到Web视图中。


为什么不将视频ID作为参数传递给你的JavaScript函数,并调用该函数来加载视频呢? - João Nunes

0

这是一个Swift 3的答案

   NotificationCenter.default.addObserver(
    self,
    selector: #selector(self.videoEnded),
    name: .AVPlayerItemDidPlayToEndTime,
    object: nil)


}

func videoEnded(){
    print("video ended")
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接