Cannot Dismiss a View Controller after Dismissing a Media Player View Controller

Understanding the Issue: Cannot Dismiss a View Controller after Dismissing a Media Player View Controller

In this article, we will delve into the world of iOS view controllers and explore why it is not possible to dismiss a view controller that presents a media player view controller.

Background

In iOS development, presenting a view controller is a way to show its content on screen. When a view controller is presented, it becomes the topmost view in the navigation hierarchy. Dismissing a view controller means removing it from the navigation hierarchy and hiding its view.

When a view controller presents another view controller, such as MediaPlayerViewController, it essentially adds a new layer on top of the original view controller. This allows the media player to overlay its content on top of the original view controller’s content.

However, when the media player view controller is dismissed, the underlying navigation hierarchy changes. The MediaPlayerViewController is no longer the topmost view, and the original view controller becomes visible again.

The Problem

The issue arises because both views are presented with an animation (YES). When you try to dismiss the first view controller after presenting the media player view controller, it seems like the animation prevents the dismissal from occurring. This is a common problem in iOS development, especially when working with present-and-dismiss animations.

The Code

To understand why this issue occurs, let’s take a closer look at the provided code:

MPMoviePlayerViewController *mp = [[MPMoviePlayerViewController alloc] initWithContentURL:fileURL];
[mp moviePlayer].controlStyle = MPMovieControlStyleNone;
[[mp moviePlayer] prepareToPlay];
[[mp moviePlayer] setShouldAutoplay:YES];
[mp moviePlayer].fullscreen = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];

[self presentMoviePlayerViewControllerAnimated:mp];
-(void)videoPlayBackDidFinish:(NSNotification*)notification {
    AppstersAppDelegate *appDelegate = (AppstersAppDelegate *)[[UIApplication sharedApplication] delegate];

    // [appDelegate continueSetup];

    [self dismissMoviePlayerViewControllerAnimated];

    [appDelegate.viewController dismissModalViewControllerAnimated:NO];
}

The key lines of code here are:

  1. self.parentViewController dismissModalViewControllerAnimated;
  2. [appDelegate.viewController dismissModalViewControllerAnimated:NO];
  3. [[mp moviePlayer] prepareToPlay]; and [[mp moviePlayer] setShouldAutoplay:YES];

Explanation

When a view controller presents another view controller, it uses the presentViewController:animated:completion: method to add it to the navigation hierarchy. This method takes three parameters:

  • The view controller that is being presented
  • A boolean indicating whether the presentation should be animated (YES or NO)
  • An optional completion handler that can be used to perform some action after the presentation has completed

When a view controller presents another view controller, it becomes the parent of that new view controller. The presenting view controller also adds itself as an observer of its child’s lifecycle events.

In this case, when we call [self presentMoviePlayerViewControllerAnimated:mp];, we are adding MediaPlayerViewController to the navigation hierarchy and making it visible on screen. We are also setting up an observer for the media player’s playback did finish notification.

When the media player finishes playing back a movie, its playbackDidFinishNotification is fired. This notification is sent to all observers that were registered with the media player controller. In our case, we have set ourselves as an observer for this notification in [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoPlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil];.

The videoPlayBackDidFinish: method is called when the playback did finish notification is received. Inside this method, we dismiss the media player view controller and then try to dismiss our original view controller.

However, there’s a problem here. When we call [self.parentViewController dismissModalViewControllerAnimated];, it doesn’t work because the media player view controller has already been removed from the navigation hierarchy by its parent (MediaPlayerViewController).

The issue arises because both views were presented with an animation (YES). This means that when we try to dismiss our original view controller, it seems like the animation prevents the dismissal from occurring.

Solution

To solve this problem, we need to use a different method to dismiss our view controller. We can do this by calling [self.parentViewController parentViewController].dismissModalViewControllerAnimated:] instead of self.parentViewController dismissModalViewControllerAnimated;. This ensures that we are dismissing the correct view controller in the navigation hierarchy.

Here’s the corrected code:

-(void)videoPlayBackDidFinish:(NSNotification*)notification {
    AppstersAppDelegate *appDelegate = (AppstersAppDelegate *)[[UIApplication sharedApplication] delegate];

    // [appDelegate continueSetup];

    [self dismissMoviePlayerViewControllerAnimated];

    [[(MPMoviePlayerViewController *)[appDelegate.viewController presentationController].parentViewController dismissModalViewControllerAnimated:NO];]
}

Explanation of the Fix

In this corrected code, we are accessing the presentationController property of our view controller (AppstersAppDelegate *appDelegate = (AppstersAppDelegate *)[[UIApplication sharedApplication] delegate];). The presentationController is an object that represents the presentation hierarchy of our view controller.

We then access the parentViewController property of the presentationController. This gives us the view controller that is presenting our original view controller in the navigation hierarchy.

Finally, we call [self.parentViewController parentViewController].dismissModalViewControllerAnimated:NO]; to dismiss our original view controller.


Last modified on 2024-10-09