3
votes

There are a multitude of questions here relating to locking movie playback into landscape mode, or supporting landscape playback of movies using MPMoviePlayerViewController or MPMoviePlayerController. I already have landscape/portrait functionality, and I don't want to lock the playback to either mode.

What I have is code which uses MPMoviePlayerViewController as the docs suggest, essentially:

    MPMoviePlayerViewController* movieViewController = 
        [[MPMoviePlayerViewController alloc] initWithContentURL:url];
    [parentViewController presentMoviePlayerViewControllerAnimated:movieViewController];

My app is mostly locked to portrait mode, but this code presents the movie controller modally in its own view, and supports both portrait and landscape orientations. All of which is very nice.

Here is my problem though. 99% of the videos I will be presenting are landscape videos, and the above code starts the movie playback in portrait mode, because the user is (likely) holding the device in portrait mode.

What I want is the behaviour of the native YouTube app; that when you present the movie controller, it first presents in landscape mode, which will prompt the user to change their device's orientation. If they later on want to rotate it back to portrait they are allowed to. When the movie is done (or dismissed), the movie view controller will be dismissed, and the device should be in portrait mode.

It seems impossible to hide the status bar properly (it's tied to the full screen controls regardless of the state of 'hideStatusBar' before launching the movie), so it seems that getting the status bar to be in the right place also needs to be a part of this.

Edited to add debugging notes for status bar orientation: If I call setStatusBarOrientation:UIInterfaceOrientationLandscapeRight before launching the movie, the status bar is in the right place, but the system no longer calls shouldAutorotateToInterfaceOrientation in the same way.

If I don't call setStatusBarOrientation, prior to the movie appearing I get the following sequence of calls:

shouldAutorotateToInterfaceOrientation(Portrait)
shouldAutorotateToInterfaceOrientation(Portrait)
shouldAutorotateToInterfaceOrientation(Portrait)
shouldAutorotateToInterfaceOrientation(Right)
shouldAutorotateToInterfaceOrientation(Portrait)

I answer YES only to the Right, and the movie launches in LandscapeRight, but with the status bar in the wrong place. Subsequent changes of device orientation generate exactly the shouldAutorotateToInterfaceOrientation calls you'd expect (e.g. if I rotate to Portrait, it asks me if it's okay to rotate to Portrait).

If I do call setStatusBarOrientation:UIInterfaceOrientationLandscapeRight, I get the following sequence:

shouldAutorotateToInterfaceOrientation(Right)
shouldAutorotateToInterfaceOrientation(Right)

Both of which I answer YES to. The status bar is in the right place, but I no longer get an calls to shouldAutorotateToInterfaceOrientation asking about Portrait mode. So if I rotate the device to Right, then back to Portrait, then back to Right, I see it call shouldAutorotateToInterfaceOrientation(Right) twice. Even stranger, if I rotate it all the way around to the Left orientation, I do get shouldAutorotateToInterfaceOrientation(Left) and from then on everything works fine. Which is most annoying.

I think it must be a bug on the iOS side, and that's why the YouTube app doesn't use the animated UI rotation effects. Instead it hides the full screen controls, including the status bar, rotates invisibly in the background, and then re-shows the controls.

2
NB: I've accepted h4xxr's answer and awarded the bounty, because it does cover the question as written; however this still isn't a workable solution due to the status-bar issues.MrCranky
Final resolution: shifting the setStatusBarOrientation call into the viewWillAppear method of the subclassed LandscapeMPMoviePlayerViewController makes it work. The shouldAutorotate calls happen as per my first debugging case (which handles subsequent rotations correctly), then the status bar is reoriented, then the movie view appears correctly in landscape mode. So that ticks all of my boxes for a solution.MrCranky

2 Answers

4
votes

I would suggest setting off a timer; allow only landscape in shouldAutorotateToInterfaceOrientation: at first (which will enforce a rotation to landscape), and then after (say) 5 seconds, allow all orientations.

Firstly you need to either subclass MPMoviePlayerViewController or create an MPMoviePlayerController in its own view. The extra code would look something like this:

// On load, init or similar method
BOOL showLandscapeOnly = YES;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@(allowAllOrientations) userInfo:nil repeats:NO];

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
  if (showLandscapeOnly == YES)
    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeRight);
  else
    return YES;
}

- (void)allowAllOrientations {
  showLandscapeOnly = NO;
}

I reckon this would deliver exactly what you're after; on showing the modal video controller, it would flip the orientation to landscape as this is the only supported orientation. But if the user turned it back to portrait, it would then respond fine.

You could also try experimenting with other time intervals; perhaps 2 seconds would be better? Suggest you see what test user subjects do. They probably rotate the device pretty quickly.

Hope that helps!

0
votes

You're going to have to rotate the movie's frame, and then tell the app delegate to not rotate itself when it hears the rotation messages.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return NO;
}