Кнопка "Закрыть" на адаптивной навигации

В раскадровке у меня есть контроллер корневого представления с кнопкой, которая запускает "Present as Popover" в UINavigationController, содержащий UITableViewController. Я хочу, чтобы навигационный контроллер присутствовал как на iPhone, так и на iPad.

На iPad это отлично работает в popover.

На iPhone я получаю модальную презентацию, поэтому теперь мне нужно добавить дополнительную кнопку панели, чтобы отклонить модальное представление. От просмотра видео WWDC я попытался выполнить следующие действия в контроллере корневого представления:

- (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;
}

Я понимаю, что метод -presentationController:viewControllerForAdaptivePresentationStyle: должен вызываться только тогда, когда пользовательский интерфейс является адаптивным, т.е. модальным, однако он вообще не называется вызываемым, даже если он работает как модальный на iPhone.

Ответы

Ответ 1

Хорошо, мне удалось заставить его работать. Я думаю, что моя проблема заключалась в том, что свойство popoverPresentationController проходит иерархию представлений контроллера до тех пор, пока не найдет контроллер вида с popoverPresentationController, т.е. если у меня есть контроллер представления внутри контроллера навигации внутри popover, контроллер вида popoverPresentationController будет перейдите к контроллеру nav и используйте его свойство. Чтобы это работало, контроллер просмотра должен быть дочерним элементом навигационного контроллера. Во всех точках, которые я пытался использовать popoverPresentationController, это было не так, например init, viewDidLoad, viewWillAppear. По какой-то причине willMoveToParentViewController не вызывается, хотя вызов didMove вызывает. Поэтому я не знаю, как ссылаться на popoverPresentationController в vc внутри контроллера nav до того, как будут вызваны методы делегата ppc.

Однако, вы можете ссылаться на него в контроллере представления в prepareForSegue, но вам нужно явно указать, какой стиль презентации использовать. Здесь мой код, который работает при размещении в представлении контроллера представления:

- (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 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;
}

Ответ 2

Здесь версия Swift версии Nick правильно отвечает тем, кто хочет быстро вырезать и вставить.

Примечание. Это делается для создания всплывающего окна на вашем iPad, но модального листа с кнопкой закрытия на вашем iPhone.

В раскадровке Xcode 6.3 вы подключаете контроллер вида и обозначаете segue как "Present as Popover"

Нижеприведенный код должен идти в контроллере представления, который перебирается к popover, а не к самому popover:

Сначала вы настроили делегат popover:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "myPopoverSegueName") {
        let vc = segue.destinationViewController
        vc.popoverPresentationController?.delegate = self
        return
    }
}

Затем вы добавляете расширение делегата и нажимаете кнопку навигации/закрытия на лету:

extension myViewController: UIPopoverPresentationControllerDelegate {

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

}

Затем вы добавляете функцию увольнения, и вам должно быть хорошо:

func dismiss() {
    self.dismissViewControllerAnimated(true, completion: nil)
}

Ответ 3

Я обнаружил, что принятый ответ неправильно отображает кнопку "done" в компактном режиме (например, iPhone), но остается как popover в обычном режиме (например, IPAD).

Следующий код является минимальным для выполнения этой работы - поместите их в класс контроллера представления.

-(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];
}

Обратите внимание на три отличия с принятым ответом:

  • Нам не нужно переопределять adaptivePresentationStyleForPresentationController:
  • Мы проверяем, является ли диспетчер презентации popover, но запрашивает полноэкранный режим.
  • Мы возвращаем новый экземпляр контроллера навигации вместо (некорректно) литья представленного контроллера представления в качестве навигационного контроллера.