3

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.

MrCranky
  • 1,498
  • 24
  • 32
  • 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 Aug 08 '11 at 13:02
  • 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 Aug 10 '11 at 12:47

2 Answers2

4

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!

h4xxr
  • 11,385
  • 1
  • 39
  • 36
  • Just tried this, rather than using a timer though I hooked it to viewDidAppear, which fits the desired behaviour. It does work: movie launches in landscape, then allows rotation back to portrait, but has a nasty flaw: even when hidden prior to launching the movie, the status bar reappears, and it's in the wrong place. Setting the status bar forcibly to LandscapeRight also seems to mean that shouldAutorotate doesn't get called properly; If I don't move the status bar it gets asked about portrait mode, but if I do it only ever asks about landscape mode. Still, closer than I've been so far. – MrCranky Jul 26 '11 at 11:41
  • Right, question edited. Sorry if I sounded negative as to the answer: it does achieve the desired effect based on the original question, so thanks! It needs something more to stand alone as a solution though. – MrCranky Jul 26 '11 at 11:56
  • 1
    Hmm, I think there might be a bug in your code. I've pulled up a landscape-only movie (what we're effectively doing here, although we're then allowing rotation back to portrait) from a portrait view many times, and iOS nicely rotates the status bar for me without my having to make any changes. I'm off to Xcode to investigate!... – h4xxr Jul 26 '11 at 14:01
  • +1 for this answer, it is the fix for what was asked. That said, WTF guys? Anybody actually talk to an actual user about this? Sounds to me like some kind of weird programmer requirement not at all driven by what users want. Then again I'm just old and snarky. – slf Aug 01 '11 at 19:41
  • If it was a programmer requirement, then I wouldn't expect it to be the default behaviour on YouTube, the native movies app on the phone! :-) But for us it's a client request, they want their lovingly crafted (landscape) video content to look its best, and not have the first impression of the content be all squashed up with bars top and bottom. – MrCranky Aug 02 '11 at 15:23
  • EUREKA! I ran out of time, so decided to go with this and live with the flaw. When porting the code over from my test project into the live project, I reinstated the code to set the status bar orientation. However instead of putting it outside the class where I launch the movie view, I put it inside, in the LandscapeMPMoviePlayerViewController viewWillAppear method. This gets the right behaviour: the autorotate decisions are made before the status bar orientation is changed, and the view responds correctly. Now I get all the behaviour I want. – MrCranky Aug 10 '11 at 12:45
  • Nice one! I was pretty sure it was a bug in your previous comment as we've done the equivalent of this (without the re-allow bit) many times. Glad it's all sorted. – h4xxr Aug 10 '11 at 21:30
0

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;
}
Brooks Hanes
  • 425
  • 2
  • 12
  • Won't that lock it into landscape mode? – MrCranky Jul 20 '11 at 14:29
  • Portrait most likely. But if you don't do this, the movie will rotate another 90 deg, endlessly. – Brooks Hanes Jul 20 '11 at 16:04
  • Yeah, that doesn't sound good. The Youtube implementation does something smarter - when you're holding the device in portrait mode it still starts in landscape, when you rotate the device to match the landscape view, it doesn't rotate again. It's only subsequent rotations of the device which cause the view to change back to portrait (or the opposite landscape view) – MrCranky Jul 21 '11 at 15:28
  • Also it appears someone has written a method to handle this: http://stackoverflow.com/questions/5700219/uiimagepickercontroller-using-camera-as-source-does-autorotate-on-ipad2-how-do – Brooks Hanes Jul 21 '11 at 20:32