137 votes

Bloc d'achèvement pour popViewController

Lors du rejet d'un contrôleur de vue modal à l'aide de dismissViewController il est possible de fournir un bloc d'achèvement. Existe-t-il un équivalent similaire pour popViewController ?

L'argument d'achèvement est très pratique. Par exemple, je peux l'utiliser pour retarder la suppression d'une ligne d'un tableau jusqu'à ce que la modale soit hors écran, laissant l'utilisateur voir l'animation de la ligne. Lorsque je reviens d'un contrôleur de vue poussé, j'aimerais avoir la même opportunité.

J'ai essayé de placer popViewController dans un UIView bloc d'animation, où j'ai accès à un bloc d'achèvement. Cependant, cela produit des effets secondaires indésirables sur la vue vers laquelle on se dirige.

Si une telle méthode n'est pas disponible, quelles sont les solutions de rechange ?

215voto

Joris Kluivers Points 5311

Je sais qu'une réponse a été acceptée il y a plus de deux ans, mais cette réponse est incomplète.

Il n'y a aucun moyen de faire ce que vous voulez dans la boîte.

C'est techniquement correct car le UINavigationController L'API ne propose aucune option à cet effet. Toutefois, en utilisant le cadre CoreAnimation, il est possible d'ajouter un bloc d'achèvement à l'animation sous-jacente :

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    // handle completion here
}];

[self.navigationController popViewControllerAnimated:YES];

[CATransaction commit];

Le bloc d'achèvement sera appelé dès que l'animation utilisée par popViewControllerAnimated: extrémités. Cette fonctionnalité est disponible depuis iOS 4.

79voto

HotJard Points 393

Version Swift 5 - fonctionne comme un charme. Basé sur cette réponse

extension UINavigationController {
    func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping () -> Void) {
        pushViewController(viewController, animated: animated)

        if animated, let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }

    func popViewController(animated: Bool, completion: @escaping () -> Void) {
        popViewController(animated: animated)

        if animated, let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}

41voto

Arbitur Points 2539

J'ai fait un Swift version avec des extensions avec @JorisKluivers réponse.

Ceci appellera une fermeture d'achèvement après la fin de l'animation pour les deux types d'utilisateurs. push y pop .

extension UINavigationController {
    func popViewControllerWithHandler(completion: ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.popViewControllerAnimated(true)
        CATransaction.commit()
    }
    func pushViewController(viewController: UIViewController, completion: ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: true)
        CATransaction.commit()
    }
}

20voto

Muhammad Waqas Points 352

SWIFT 4.1

extension UINavigationController {
func pushToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.pushViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popViewController(animated: animated)
    CATransaction.commit()
}

func popToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToViewController(viewController, animated: animated)
    CATransaction.commit()
}

func popToRootViewController(animated:Bool = true, completion: @escaping ()->()) {
    CATransaction.begin()
    CATransaction.setCompletionBlock(completion)
    self.popToRootViewController(animated: animated)
    CATransaction.commit()
}
}

17voto

Jos Jong Points 181

J'ai eu le même problème. Et parce que je devais l'utiliser en de multiples occasions, et dans des chaînes de blocs d'achèvement, j'ai créé cette solution générique dans une sous-classe de UINavigationController :

- (void) navigationController:(UINavigationController *) navigationController didShowViewController:(UIViewController *) viewController animated:(BOOL) animated {
    if (_completion) {
        dispatch_async(dispatch_get_main_queue(),
        ^{
            _completion();
            _completion = nil;
         });
    }
}

- (UIViewController *) popViewControllerAnimated:(BOOL) animated completion:(void (^)()) completion {
    _completion = completion;
    return [super popViewControllerAnimated:animated];
}

En supposant que

@interface NavigationController : UINavigationController <UINavigationControllerDelegate>

y

@implementation NavigationController {
    void (^_completion)();
}

y

- (id) initWithRootViewController:(UIViewController *) rootViewController {
    self = [super initWithRootViewController:rootViewController];
    if (self) {
        self.delegate = self;
    }
    return self;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X