Close button on adaptive popover

*爱你&永不变心* 提交于 2019-12-03 11:36:16

问题


In a storyboard I have a root view controller with a button which triggers a 'Present as Popover' segue to a UINavigationController containing a UITableViewController. I want the nav controller to be present on both iPhone and iPad.

On an iPad, this works great in a popover.

On an iPhone, I get the modal presentation, so now I need an additional bar button item to dismiss the modal view. From watching the WWDC videos, I've attempted the following in the root view controller:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController *vc = segue.destinationViewController;
    vc.popoverPresentationController.delegate = self;
}

- (void)dismissPopover {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style {
    UINavigationController *nvc = (UINavigationController *)controller.presentedViewController;
    UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(dismissPopover)];
    nvc.topViewController.navigationItem.leftBarButtonItem = bbi;
    return nvc;
}

I understand the -presentationController:viewControllerForAdaptivePresentationStyle: method should only get called when the UI is adaptive i.e. modal, however it doesn't get called at all, even when running as a modal on iPhone.


回答1:


Here's the Swift version of Nick's correct answer for those who want a quick cut and paste.

Note: This is to create a popover on your iPad but a modal sheet with a close button on your iPhone.

In Xcode 6.3 storyboard, you hook up a view controller and designate the segue as a "Present as Popover"

The below code should go in the view controller that segues to the popover, not in the popover itself:

First you set up the popover delegate:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "myPopoverSegueName" {
        segue.destination.popoverPresentationController?.delegate = self
        return
    }
}

Then you add the delegate extension and create the navigation controller / close button on the fly:

extension myViewController: UIPopoverPresentationControllerDelegate {

    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        let btnDone = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.dismissPopover))
        let nav = UINavigationController(rootViewController: controller.presentedViewController)
        nav.topViewController?.navigationItem.leftBarButtonItem = btnDone
        return nav
    }

    @objc private func dismissPopover() {
        dismiss(animated: true, completion: nil)
    }

}



回答2:


Ok, I have managed to get it working. I think my problem was that the popoverPresentationController property traverses up the view controller heirarchy until it finds a view controller with a popoverPresentationController, i.e. if I have a view controller inside a navigation controller inside a popover the view controller popoverPresentationController would go to the nav controller and use it's property. For this to work, the view controller has to be a child of the navigation controller. At all the points I was trying to use the popoverPresentationController, this was not the case, e.g init, viewDidLoad, viewWillAppear. For some reason, willMoveToParentViewController is not called, even though didMove does get called. So I have no idea how to reference popoverPresentationController in a vc inside a nav controller before the ppc delegate methods are called.

However, you can reference it in the presenting view controller in prepareForSegue, but you do need to explicitly tell it what presentation style to use. Here's my code that works when placed in the presenting view controller:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController *dest = segue.destinationViewController;
    dest.popoverPresentationController.delegate = self;
}

- (void)dismiss {
    [self dismissViewControllerAnimated:YES completion:nil];
}


- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
    return UIModalPresentationFullScreen; // required, otherwise delegate method below is never called.
}

- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style {
    // If you don't want a nav controller when it's a popover, don't use one in the storyboard and instead return a nav controller here
    UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(dismiss)];
    UINavigationController *nc = (UINavigationController *)controller.presentedViewController;
    nc.topViewController.navigationItem.leftBarButtonItem = bbi;
    return controller.presentedViewController;
}



回答3:


I found that the accepted answer doesn't correctly display a "done" button when in Compact mode (e.g. iPhone) but remain as popover in Regular mode (e.g. iPad).

The following code is the bare minimum to make this work – place these in the presenting view controller class.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController *dest = segue.destinationViewController;
    dest.popoverPresentationController.delegate = self;
}

- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style {
    UIViewController* presentedViewController = controller.presentedViewController;
    if ([controller isKindOfClass:[UIPopoverPresentationController class]] && style == UIModalPresentationFullScreen) {
        UINavigationController* navCtrl = [[UINavigationController alloc] initWithRootViewController:presentedViewController];
        UIBarButtonItem *bbi = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(dismiss:)];
        presentedViewController.navigationItem.rightBarButtonItem = bbi;
        return navCtrl;
    }
    return presentedViewController;
}

-(IBAction)dismiss:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

Notice the three differences with the accepted answer:

  • We don't need to override adaptivePresentationStyleForPresentationController:
  • We check whether the presentation controller is the popover but requesting full-screen mode instead.
  • We return a new navigation controller instance instead of (incorrectly) casting the presented view controller as a navigation controller.


来源:https://stackoverflow.com/questions/25275271/close-button-on-adaptive-popover

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