pckill
pckill

Reputation: 3759

Modal popover controller does not show its content when presented from viewDidAppear

I have a view controller, that is shown as a modal over another view controller. If it is presented with animated:NO from viewDidAppear, it does not show its content on the second time it is presented. I have minified the issue to this: I have added two controllers connected by a segue to the storyboard, each of them presents the same popover controller in viewDidAppear. Each of them is an instance of the same class:

#import "ViewController.h"
#import "PopupViewController.h"

@interface ViewController () <UIPopoverPresentationControllerDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self showPopover];
}

-(UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection {
    return UIModalPresentationNone;
}

- (void)showPopover {
    PopupViewController* popoverController = [[PopupViewController alloc] initWithNibName:NSStringFromClass([PopupViewController class]) bundle:nil];
    popoverController.modalPresentationStyle = UIModalPresentationPopover;
    popoverController.preferredContentSize = CGRectInset(self.view.bounds, 20, 100).size;
    popoverController.popoverPresentationController.sourceView = self.view;
    popoverController.popoverPresentationController.sourceRect = CGRectMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds), 0, 0);
    popoverController.popoverPresentationController.permittedArrowDirections = 0;
    popoverController.popoverPresentationController.delegate = self;
    [self presentViewController:popoverController animated:NO completion:nil];
}

@end

The popover is presented correctly on the app launch, but is empty on pushing the second view controller, and on tapping back button. If I change animated to YES, the popovers are shown correctly.

Edit: If I debug view hierarchy in Xcode, the views are displayed correctly, with all their frames as they should be:

View hierarchy

This is how it looks like on the device (correct popover on the left and empty on the right): Example

Upvotes: 0

Views: 1011

Answers (1)

Sulthan
Sulthan

Reputation: 130082

This issue can be reproduced easily. I started logging the events and it seems this is caused by the way UINavigationController is handling animations.

When viewDidAppear is called for the pushed controller, the animation is not finished yet. You can see that for yourself by checking the navigationController:didShowViewController method in UINavigationControllerDelegate.

The event log looks like this:

Navigation controller will show VC 1
VC 1 did layout subviews
VC 1 did layout subviews
VC 0 did move to parent <UINavigationController: 0x7feaf181ec00>
VC 1 did appear
VC 1 did move to parent <UINavigationController: 0x7feaf181ec00>
Navigation controller did show VC 1

Note that the second controller is not even completely moved to the parent in viewDidAppear:.

The only viable workaround I have found is:

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    dispatch_async(dispatch_get_main_queue(), ^{
        [self showPopover];
    });
}

Which will let the navigation controller to finish its internal work.

Note the issue does not appear when pushing without animation so this is probably caused by the fact the we are trying to show a view controller without animation from another animation completion block and that means the order of view controller hierarchy events is broken. Adding the dispatch_async moves the code from the completion block and everything starts to work correctly.

Upvotes: 2

Related Questions