Dismissing a UIDocumentInteractionController in some cases will remove the presenting view controller's views in IOS 7 iPad

耗尽温柔 提交于 2019-12-03 06:25:36

I have this same problem when presenting a UIDocumentInteractionController from a view controller presented as a modal form sheet on iPad in iOS 7 (worked fine in iOS 6).

It looks like that during the transition from the document interaction controller back to the presenting view controller, the presenting view controller's view is wrapped in a temporary UITransitionView, and then that transition view is being removed from the view hierarchy after the transition is complete, along with the presenting view controller's view, leaving just UIDropShadowView that is the backing view of the modal form sheet visible (the gray box).

I worked around the problem by keeping a reference to my presenting view controller's root view (the one just before the drop shadow view in the hierarchy) when the document interaction controller preview will begin, and restoring that view to the hierarchy when the document interaction controller preview has ended.

Here's sample code:

    - (void)documentInteractionControllerWillBeginPreview:(__unused UIDocumentInteractionController *)controller {

    if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
        // work around iOS 7 bug on ipad

        self.parentView = [[[self.view superview] superview] superview];
        self.containerView = [self.parentView superview];

        if (![[self.containerView superview] isKindOfClass: [UIWindow class]]) {
            // our assumption about the view hierarchy is broken, abort
            self.containerView = nil;
            self.parentView = nil;
        }
    }
}

    - (void)documentInteractionControllerDidEndPreview:(__unused UIDocumentInteractionController *)controller {

    if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
        if (!self.view.window && self.containerView) {
            assert(self.parentView);
            CGRect frame = self.parentView.frame;
            frame.origin = CGPointZero;
            self.parentView.frame = frame;
            [self.containerView addSubview: self.parentView];
            self.containerView = nil;
            self.parentView = nil;
        }
    }
}

I found Michael Kuntscher answer to be right on target. A slight modification is necessary if the UIDocumentInteractionController is presented from a popover.

There is a slight dependence on the view hierarchy that can be eliminated by iterating over the parent views until one with a UIWindow superview is found. In addition, I found that when a document interaction controller is presented from within a popover it is slightly different views that need to be stored as the parentView and containerView (specifically we want to find a containerView such that its superview is a UIPopoverView). The following snippet is a re-worked version of Michael's answer to incorporate these changes (note that UIPopoverView is a private class, so we use string representations of the class rather than making a direct reference to each class):

- (void)documentInteractionControllerWillBeginPreview:(UIDocumentInteractionController *)controller {

    /* iOS 7 DIC bug workaround  */
    if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
        UIView *a_view = self.view;

        self.dicParentView = nil;
        self.dicContainerView = nil;

        while (a_view != nil) {
            UIView *super_super_view = [[a_view superview] superview];
            NSString *str_class = NSStringFromClass([super_super_view class]);

            if ([str_class isEqualToString:@"UIWindow"] ||
                [str_class hasSuffix:@"PopoverView"]) {
                self.dicParentView = a_view;
                self.dicContainerView = [a_view superview];
                break;
            }
            a_view = [a_view superview];
        }

        if (self.dicParentView == nil) {
            NSLog(@"Could not appropriate superview, unable to workaround DIC bug");
        }
    }
    /* end work around */
}

- (void)documentInteractionControllerDidEndPreview:(__unused UIDocumentInteractionController *)controller {
    /* iOS 7 DIC bug workaround */
    if (UIUserInterfaceIdiomPad == UI_USER_INTERFACE_IDIOM()) {
        if ((self.view.window == nil) &&
            (self.dicContainerView != nil) &&
            (self.dicParentView != nil)) {
            NSLog(@"Restoring view for DIC presenter in the view hierarchy");
            CGRect frame = self.dicParentView.frame;
            frame.origin = CGPointZero;
            self.dicParentView.frame = frame;
            [self.dicContainerView addSubview: self.dicParentView];
            self.dicContainerView = nil;
            self.dicParentView = nil;
        }
    }
    /* end work around */
}

I had the exact same problem with the app stalling on a grey form sheet modal view that had lost all of its content after a UIDocumentInteractionController had been presented and dismissed. The two solutions here are great but I simplified them to cater for my particular case, which was a UINavigationController inside a form sheet modal that can present a PDF in the UIDocumentInteractionController, which I wanted to be full screen modal instead of pushed in the navigation controller because the form sheet area is too small for the PDF to be easily readable.

I implemented two UIDocumentInteractionControllerDelegate methods. Assume the following:

  • self.navController is a reference to the UINavigationController that is presented inside the form sheet modal.
  • there is a member variable declared in the UIViewController subclass @property (nonatomic, strong) UIView* docInteractionControllerWorkaroundSuperview;
  • SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO is a #define for ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

Firstly:

-(UIViewController*)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController*)controller
{    
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") && self.navigationController.modalPresentationStyle == UIModalPresentationFormSheet)
    {
        self.docInteractionControllerWorkaroundSuperview = [self.navigationController.view superview];
    }
    return self.navigationController.visibleViewController;
}

then:

- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
    if (self.docInteractionControllerWorkaroundSuperview != nil)
    {
        NSLog(@"Workaround iOS 7 document interaction bug... resetting nav controller view into modal");

        //reset the nav controller view from whence it came.
        self.navigationController.view.frame = CGRectMake(0.0, 0.0, self.navigationController.view.frame.size.width, self.navigationController.view.frame.size.height);
        [self.docInteractionControllerWorkaroundSuperview addSubview:self.navigationController.view];

        self.docInteractionControllerWorkaroundSuperview = nil;
    }
}

i.e. when presenting the UIDocumentInteractionController, look at the superview of the navigation controller's view. It works out to be a UIDropShadowView which I assume is the partially transparent grey/black background to the form sheet modal that dims out the view behind that presented the modal.

When the PDF has been dismissed, in documentInteractionControllerDidEndPreview the superview of the navigation controller's view is now a UITransistionView. But then shortly after that (when the transition has completed), it gets set to nil. Somehow it got detached (or didn't get re-attached) from the UIDropShadowView. By keeping a reference to view this when presenting the UIDocumentInteractionController, we can reattach it manually and everything works fine. Then be sure to nil out the reference to make sure we don't accidentally retain it.

This method does not affect the behaviour on iOS 6 or any previous iOS versions.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!