Как правильно отклонить UINavigationController, который представлен как модальный?

В моем TabBarViewController я создаю UINavigationController и представляю его как модальный.

var navController =  UINavigationController()
let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
self.presentViewController(self.navController, animated: false, completion: nil)
self.navController.pushViewController(messageVC, animated: false)

Внутри моего MessageViewController, вот как я хочу отклонить его:

func swipedRightAndUserWantsToDismiss(){
    if self == self.navigationController?.viewControllers[0] {
        self.dismissViewControllerAnimated(true, completion: nil) //doesn't deinit
    }else{
        self.navigationController?.popViewControllerAnimated(true) //deinits correctly
    }
}

deinit{
    print("Deinit MessagesViewController")
}

Проблема заключается в том, что когда я добираюсь до корневого View Controller и пытаюсь уволить как дочерний, так и UINavigationController, мой MessagesViewController deinit не вызывается. Что-то держит за него - скорее всего, UINavigationController

Ответ 1

Ваша иерархия контроллера выглядит следующим образом:

UITabViewController
    |
    | presents
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

Теперь, если вы находитесь внутри MessagesViewController, тогда его navigationController будет тем, который представлен, и что тот, который вы должны увольнять, но вызывающий dismiss на MessagesViewController тоже должен работать.

Однако проблема заключается в том, что отклонение контроллера навигации не приведет к удалению его контроллеров представлений. Кажется, вы держитесь за свой навигационный контроллер (поскольку вы представляете его с помощью self.navController), поэтому состояние станет

UITabViewController
    |
    | self.navController holds a reference to
    |
UINavigationController
    |
    | contains view controllers
    |
[root, MessagesViewController]

Чтобы правильно уничтожить MessagesViewController, вам придется либо отпустить navController, либо вам нужно будет постить его до корня (таким образом удалив MessagesViewController из иерархии представлений).

Типичным решением было бы не сохранять ссылку на navController вообще. При представлении всегда можно создать новый UINavigationController. Другим решением является использование делегата - вместо увольнения изнутри MessagesViewController, позвольте ему перезвонить ведущему, который будет вызывать

self.navController.dismissViewControllerAnimated(true, completion: {
   self.navController = nil;
});

Ответ 2

Попробуйте это

func swipedRightAndUserWantsToDismiss(){
    self.navigationController.dismissViewControllerAnimated(false, completion:nil);
}

Ответ 3

Не нужно иметь члена для navController. Используйте следующий код, чтобы представить свой MessageViewController.

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let pesentingNavigationController = UINavigationController(rootViewController: messageVC)
self.presentViewController(pesentingNavigationController, animated: true, completion: nil)

Ваш код контроллера отклонения будет

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

Ответ 4

Я предлагаю вам использовать другой инициализатор для вашего UINavigationController:

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let navController = UINavigationController(rootViewController: messageVC)
self.presentViewController(self.navController, animated: true, completion: nil)

Чтобы уменьшить, просто сделайте

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

Ответ 5

если вы хотите просто представить диспетчер представлений, тогда вы можете представить этот диспетчер представлений и не нужно принимать контроллер навигации для этого конкретного диспетчера представлений.

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

let messageVC = self.storyboard?.instantiateViewControllerWithIdentifier("MessagesViewController") as! MessagesViewController
let MynavController = UINavigationController(rootViewController: messageVC)
self.presentViewController(MynavController, animated: true, completion: nil)

и из представленного контроллера представления вы можете нажать на другой контроллер представления, а также поплыть с другого контроллера.

И из представленного контроллера представления, здесь messageVC, мы должны отклонить это как

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

который успешно удалит messageVC и вернется в исходный viewcontroller, откуда мы представили messageVC.

Это правильный поток для выполнения presentViewController с навигационным контроллером, чтобы продолжить навигацию между контроллерами представлений.

И для большего, если вы не уверены, что messageVC представлен или нажат, вы можете проверить его этим ответом.

И быстрая версия, чтобы проверить, что это

func isModal() -> Bool {
    if((self.presentingViewController) != nil) {
        return true
    }

    if(self.presentingViewController?.presentedViewController == self) {
        return true
    }

    if(self.navigationController?.presentingViewController?.presentedViewController == self.navigationController) {
        return true
    }

    if((self.tabBarController?.presentingViewController?.isKindOfClass(UITabBarController)) != nil) {
        return true
    }

    return false
}

Итак, наше окончательное действие для отклонения похоже на

func swipedRightAndUserWantsToDismiss() {

            if self.isModal() == true {
                self.dismissViewControllerAnimated(true, completion: nil)
            }
            else {
                self.navigationController?.popViewControllerAnimated(true)
            }

        }

Ответ 6

Вот как я решаю проблему в Objective C.

Вы можете вызвать rejectViewControllerAnimated: NO на свой self.navigationController.

Цель C

[self.navigationController dismissViewControllerAnimated:NO completion:nil];

Swift

self.navigationController.dismissViewControllerAnimated(false, completion: nil)

Ответ 7

В Swift 3 это достигается с помощью:

self.navigationController?.dismiss(animated: true, completion: nil)