0

My app is portrait only but, I would like to allow the user to rotate to landscape when watching full screen videos through a UIWebview. I've done some research and found that I should add my class as an observer for these notifications:

UIMoviePlayerControllerDidEnterFullscreenNotification UIMoviePlayerControllerWillExitFullscreenNotification

I add and remove the class as an observer like this:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerDidEnterFullScreen:) name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayerWillExitFullScreen:) name:@"UIMoviePlayerControllerWillExitFullscreenNotification" object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIMoviePlayerControllerDidEnterFullscreenNotification" object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIMoviePlayerControllerWillExitFullscreenNotification" object:nil];
}

- (void)moviePlayerDidEnterFullScreen:(NSNotification *)notification
{
    self.videoPlayingFullScreen = YES;
}

- (void)moviePlayerWillExitFullScreen:(NSNotification *)notification
{
    self.videoPlayingFullScreen = NO;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.videoPlayingFullScreen)
    {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }

    return UIInterfaceOrientationMaskPortrait;
}

My problem is: I never receive the "UIMoviePlayerControllerWillExitFullscreenNotification". I can't use the UIMoviePlayerControllerDidExitFullscreenNotification because if the user is finished watching the fullscreen video in landscape orientation and presses "done" the previous view controller also appears in landscape orientation when it should be in portrait.

Is there another way to detect when the user "did" enter fullscreen and "will" exit fullscreen? Or is there something that I am missing?

EDIT: My app is for iOS 7 only.

Jonathan
  • 2,623
  • 3
  • 23
  • 38
  • 1
    Those notifications are not documented, hence super risky when being used as they might and will break the app once a new OS update is released. For example, the `UIMoviePlayerControllerDidExitFullscreenNotification` never got sent on iOS6. – Till Oct 02 '13 at 15:46
  • Is there a documented way to detect when the UIWebview did enter and will exit full screen? – Jonathan Oct 02 '13 at 15:48
  • 1
    Use JavaScript events as described by [this answer](http://stackoverflow.com/a/8554077/91282). – Till Oct 02 '13 at 15:50

2 Answers2

3

The reason you're not getting the UIMoviePlayerControllerWillExitFullscreenNotification callback is because you're removing yourself as an observer on viewWillDisappear:

Guy Kogus
  • 7,251
  • 1
  • 27
  • 32
  • Thanks for the answer. In which method should I remove myself as an observer? – Jonathan Oct 02 '13 at 15:47
  • 2
    When using dealloc for removing the notifications, make sure you attach to them within your init method, not within the appearance callbacks. – Till Oct 02 '13 at 15:52
0

Since these callbacks are undocumented I used Javascript events (as H2CO3 suggested here) to determine when the video began, ended, or paused.

By the way I'm using the YouTube Player.

First, I setup the UIWebview and set my ViewController as the delegate.

Next, I loaded the HTML file into the UIWebview.

Index.html

<html>
    <body bgcolor=#8C1717 style="margin:0px;">
        <div id="ytplayer"></div>
        <script type="text/javascript">
            var tag = document.createElement('script');
            tag.src = "https://www.youtube.com/player_api";

            var firstScriptTag = document.getElementsByTagName('script')[0];
            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

            var player;
            function onYouTubeIframeAPIReady()
            {
                player = new YT.Player('ytplayer',
                                       {
                                       height: 'videoHeight',
                                       width: 'videoWidth',
                                       videoId: 'videoID',
                                       playerVars: { 'showinfo':0, 'rel':0 },
                                       events: { 'onStateChange': onPlayerStateChange }
                                       });
            }

            function playerDidBeginPlaying()
            {
                document.location.href = "fake://video-began";
            }

            function playerDidEndPlaying()
            {
                document.location.href = "fake://video-ended";
            }

            var done = false;
            function onPlayerStateChange(event)
            {
                if (event.data == YT.PlayerState.PLAYING && !done)
                {
                    done = true;
                    playerDidBeginPlaying();
                }
                else if (event.data == YT.PlayerState.ENDED)
                {
                    playerDidEndPlaying();
                }
                else if (event.data == YT.PlayerState.PAUSED)
                {
                    playerDidEndPlaying();
                }
            }
        </script>
    </body>
</html>

Inside the ViewController

    NSError *error = NULL;

    NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];

    if (error)
    {
#ifdef DEBUG
        NSLog(@"[YouTube Webview] Error: %@", [error description]);
#endif
    }

    [self.webView loadHTMLString:html baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];

Then, I implemented the method webView:shouldStartLoadWithRequest:navigationType: to get notified when the events happened.

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    if ([[[request URL] absoluteString] hasPrefix:@"fake://video-began"])
    {
        self.videoPlayingFullScreen = YES;

        return NO;
    }
    else if ([[[request URL] absoluteString] hasPrefix:@"fake://video-ended"])
    {
        self.videoPlayingFullScreen = NO;

        return NO;
    }

    return YES;
}
Community
  • 1
  • 1
Jonathan
  • 2,623
  • 3
  • 23
  • 38