iOS: Movie player view doesn't rotate

2020-02-06 04:28发布

问题:

I want to rotate my movie player view explicitly (not using Autorotation delegate). I wrote following code for that and also put comments in it. The parent view of movie player does rotate but not movie player view itself. Could someone please assist me here what I am doing wrong? Thanks.

#import "CustomMoviePlayer.h"
#define degreesToRadian(x) (M_PI * (x) / 180.0)
#define radianToDegrees(x) ((x) * 180.0/M_PI)

@implementation CustomMoviePlayer

@synthesize moviePlayer, myParentViewController;

-(void)playMovie:(NSString*)filePath onViewController:(UIViewController*)controller
{

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rotateMoviePlayer) name: UIDeviceOrientationDidChangeNotification object:nil];
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];   

    myParentViewController = controller;

    NSURL *url = [NSURL fileURLWithPath:filePath];

    moviePlayer =  [[MPMoviePlayerController alloc] initWithContentURL:url];
    moviePlayer.controlStyle = MPMovieControlStyleDefault;
    moviePlayer.shouldAutoplay = YES;

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackComplete:) name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:moviePlayer];

    [controller.view addSubview:moviePlayer.view];
    [moviePlayer setFullscreen:YES animated:YES];
}

- (void)moviePlaybackStateChanged:(NSNotification *)notification
{
    NSLog(@"State changed... %d", [moviePlayer playbackState]);
}

- (void)moviePlaybackComplete:(NSNotification *)notification
{
    NSLog(@"Finished video...");

    [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:moviePlayer];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:moviePlayer];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];

    [myParentViewController.view setTransform:CGAffineTransformIdentity];
    [moviePlayer.view removeFromSuperview];
    [moviePlayer release];
}

-(void)rotateMoviePlayer
{
    NSLog(@"Rotate movie player");

    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

    if (orientation != UIDeviceOrientationUnknown) {

        CGAffineTransform transform = CGAffineTransformMakeRotation(degreesToRadian(0));

        switch (orientation) {
            case UIDeviceOrientationLandscapeLeft:
                transform = CGAffineTransformMakeRotation(M_PI / 2); 
                break;
            case UIDeviceOrientationLandscapeRight:
                transform = CGAffineTransformMakeRotation(-M_PI / 2);
                break;
            case UIDeviceOrientationPortraitUpsideDown:
                transform = CGAffineTransformMakeRotation(M_PI);
                break;  
            default:
                break;
        }
        // This view does rotate and I can see it rotating when there is no moviePlayer on top of it!
        [myParentViewController.view setTransform:CGAffineTransformIdentity];
        [myParentViewController.view setTransform:transform];

        // It doesn't mater whether I put following two lines or not...Movie player view doesn't rotate!
        [moviePlayer.view setTransform:CGAffineTransformIdentity];
        [moviePlayer.view setTransform:transform];
    }    
}

- (void)dealloc {
    [moviePlayer release];
    [super dealloc];
}

@end

回答1:

You are using MPMoviePlayerController in fullscreen mode, hence the rotation/s applied to its view (and/or its superview) are not being effective.

When using fullscreen mode, MPMoviePlayerController is actually using a different approach by adding its rendering layer directly to a UIWindow - that is, it does effectively not use its view property.

For getting the current UIWindow when a movie is playing in fullscreen, you may use the following snippet;

UIWindow *window = [UIApplication sharedApplication].keyWindow;
if (!window)
{
    window = [[UIApplication sharedApplication].windows objectAtIndex:0];
}

Once you got that window, apply your rotation/s directly to it.

This however will result in many possible issues that are hard to overcome (trust me, been there, got a collection of Tshirts). As an alternative way, I would strongly suggest you to use a fake-fullscreen mode.

Instead of initializing the player like you did

[moviePlayer setFullscreen:YES animated:YES];

Why not simply initializing its view's frame size to the entire screen - or the bounds of its superview like this

moviePlayer.view.frame = myParentViewController.view.bounds;

Then, for getting the fullscreen interface, use the following control style:

moviePlayer.controlStyle = MPMovieControlStyleFullscreen;

That way, your resulting playback will adhere to any transformation done on the superview and you will be free of side effects.



回答2:

[UIView beginAnimations:nil context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationRepeatCount:1];
player.view.transform = CGAffineTransformRotate(player.view.transform, M_PI_2);
[UIView commitAnimations];
[player.view setContentMode:UIViewContentModeScaleAspectFit];


回答3:

This consumed me for a while and I got so many different horrifying errors, but eventually I ended up NOT doing it through MPMoviePlayerController but MPMoviePlayerViewController. I just rotated the self.playerView which is a property, before presenting it. Also I added the NSNotification that will lead back to the main control and the main ViewController after the video finishes. Here's how I went about executing it:

        [[NSNotificationCenter defaultCenter] removeObserver:self.playerView
                                                        name:MPMoviePlayerPlaybackDidFinishNotification
                                                      object:self.playerView.moviePlayer];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(movieFinishedCallback:)
                                                     name:MPMoviePlayerPlaybackDidFinishNotification
                                                   object:self.playerView.moviePlayer];

        self.playerView = [[MPMoviePlayerViewController alloc] initWithContentURL:docUrl];
        self.playerView.view.frame = CGRectMake(10, 10, self.frame.size.width-20, 180);
        [self.playerView.moviePlayer prepareToPlay];

        if(IS_IPHONE_6P)
        {
            [self.playerView.view setBounds:CGRectMake(0, 0, 736, 414)];
            [self.playerView.view setCenter:CGPointMake(212, 368)];
        }
        else if(IS_IPHONE_6)
        {
            [self.playerView.view setBounds:CGRectMake(0, 0, 375, 667)];
            [self.playerView.view setCenter:CGPointMake(187, 333)];
        }
        else if (IS_IPHONE_5)
        {
            [self.playerView.view setBounds:CGRectMake(0, 0, 736, 414)];
            [self.playerView.view setCenter:CGPointMake(160, 284)];
        }
        else
        {
            [self.playerView.view setBounds:CGRectMake(0, 0, 480, 320)];
            [self.playerView.view setCenter:CGPointMake(160, 240)];
        }

        [self.playerView.view setTransform:CGAffineTransformMakeRotation(M_PI / 2)];
        self.playerView.modalPresentationStyle = UIModalPresentationFormSheet;
        self.playerView.modalTransitionStyle = UIModalTransitionStyleCoverVertical;

        [self presentViewController:self.playerView animated:YES completion:nil];

And the callback movieFinishedCallback: is as follows,

    - (void)movieFinishedCallback:(NSNotification*)aNotification
{
    // Obtain the reason why the movie playback finished
    NSNumber *finishReason = [[aNotification userInfo] objectForKey:MPMoviePlayerPlaybackDidFinishReasonUserInfoKey];

    // Dismiss the view controller ONLY when the reason is not "playback ended"
    if ([finishReason intValue] != MPMovieFinishReasonPlaybackEnded)
    {
        MPMoviePlayerController *moviePlayer = [aNotification object];
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                        name:MPMoviePlayerPlaybackDidFinishNotification
                                                      object:moviePlayer];

        NSLog(@"Video Closed");
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^
        {
            [self dismissViewControllerAnimated:NO completion:nil];
            self.playerView = nil;

        });
    }
}

This worked for me. Hope it helps.