Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
246 views
in Technique[技术] by (71.8m points)

iphone - MPMoviewPlayerController fullscreen playback rotation with underlying UIViewController with portrait mode only (rotation disallowed)

Hallo,

I have a simple application, which does contain UITabBarController with two UIViewControllers. Both UIViewControllers are portrait only (no rotation allowed). One UIViewController's UIView does contain MPMoviePlayerController's view to allow video playback inside this view with possibility to make it fullscreen via controls (MPMovieControlStyleEmbedded). The code is simple and does look like ...

__moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:@"MOVIE_URL"]];
__moviePlayer.controlStyle = MPMovieControlStyleEmbedded;
__moviePlayer.view.frame = CGRectMake( 10, 10, 300, 200 );
__moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
__moviePlayer.shouldAutoplay = NO;
[__moviePlayer prepareToPlay];  
[self.view addSubview:__moviePlayer.view];

... this does work perfectly unless user switches to fullscreen playback where I want to allow rotation to allow landscape playback too. Rotation doesn't work, because UITabBarController disallows it (and both UIViewControllers too).

So, I tried two approaches, but none of them does work as expected.

1) Subclassed UITabBarController

I did add property BOOL __allowRotation and if it is set to YES, I do return YES in UITabBarController's shouldAutorotateToInterfaceOrientation method.

I'm listening for MPMoviePlayerDidEnterFullscreenNotification and MPMoviePlayerWillExitFullscreenNotification notifications to set this property to YES and NO.

It does work, but the problem is, that when the user ends video playback in landscape, underlying view is not rotated back to portrait. The only way to rotate back to portrait is to use private API, which is no no.

2) View/layer transformation

I also did try to listen for MPMoviePlayerDidEnterFullscreenNotification and MPMoviePlayerWillExitFullscreenNotification notifications.

When I receive MPMoviePlayerDidEnterFullscreenNotification, I'm starting UIDevice orientation notifications to get device orientation. I'm trying to transform MPMoviePlayerController's view layer based on current device orientation, but it's kinda immune, because it does nothing. I can assign whatever to transform property, but it does nothing.

It does nothing is not quite correct. When I apply transformation during rotation, I can see effect of this transformation when I switch back from fullscreen to embedded video playback.

3) Separate UIWindow

I did not test this yet, but I've found somewhere that MPMoviePlayerController creates separate UIWindow for fullscreen playback, which should be accessible via [[UIApplication sharedApplication] windows]. This does explain why transformation is not applied during fullscreen playback.

But I quite dislike this solution, because the UIWindow can't be identified and I do not want to use magic constants like objectAtIndex:1 or apply transformation to all UIWindows except the main one, etc.

Beside the fact that the underlying implementation can be modified and it will stop working.

Question

So, the question is, how to allow MPMoviePlayerController fullscreen playback only rotation when underlying UIView (ie. UIView's UIViewController) prohibits rotation and allows portrait only?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I have a very similar situation. My app is portrait-only. But I need to show full-screen videos in any orientation, and then get back to the portrait orientation, after the user quits full-screen mode.

Split's method doesn't work for me, because I would like to let user watch the video in fullscreen and embedded, and switch between modes, not loosing the play position, and without any pauses.

I found this workaround:

First, I have a root UINavigationController subclass, that receives all messages regarding rotation.

I forbid the rotation in this controller with:

- (BOOL) shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)toInterfaceOrientation {
    return (UIInterfaceOrientationPortrait == toInterfaceOrientation);
}

I am overriding the

- (id) initWithRootViewController:(UIViewController *)rootViewController; method. 

Adding the support for device orientation modifications:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];

[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(receivedRotate:) name: UIDeviceOrientationDidChangeNotification object: nil];

Now I have a handler receivedRotate: - that catches all the device rotations in spite of not auto-rotating to any orientations except portrait:

- (void) receivedRotate:(NSNotification*) notify {
    if(isVideoFullscreen) {
        UIDeviceOrientation toInterfaceOrientation = [[UIDevice currentDevice] orientation];
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.4];
        [UIView setAnimationCurve:2];

        if(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft){
            self.view.transform = CGAffineTransformMakeRotation(-M_PI_2);
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
            self.view.bounds = CGRectMake(0, 0, 1024, 768);
        } else if(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
            self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight]; 
            self.view.bounds = CGRectMake(0, 0, 1024, 768);            
        } else if(toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
            self.view.transform = CGAffineTransformMakeRotation(M_PI);
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortraitUpsideDown];
            self.view.bounds = CGRectMake(0, 0, 768, 1024);
        } else if(toInterfaceOrientation == UIInterfaceOrientationPortrait) {
            self.view.transform = CGAffineTransformMakeRotation(0);
            [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
            self.view.bounds = CGRectMake(0, 0, 768, 1024);
        }

        [UIView commitAnimations];
    }

}

I just check the rotations of the device, and rotate my view accordingly.

Then - how do the root controller knows, when the video is fullscreen? Just add two other message handlers to the init:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willEnterFullscreen:) name:MPMoviePlayerWillEnterFullscreenNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(willExitFullscreen:) name:MPMoviePlayerWillExitFullscreenNotification object:nil];

And the handlers themselves:

- (void) willEnterFullscreen: (NSNotification *) notify {
    isVideoFullscreen = YES;
}

- (void) willExitFullscreen: (NSNotification *) notify {
    self.view.transform = CGAffineTransformMakeRotation(0);
    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
    self.view.bounds = CGRectMake(0, 0, 768, 1024);    
    isVideoFullscreen = NO;
}

When exiting fullscreen - we restore the portrait orientation. So, this works for me, hope it will help someone.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...