[ios] Bloc d'achèvement pour popViewController



6 Answers

Pour iOS9 version SWIFT - fonctionne comme un charme (n'avait pas testé pour les versions antérieures). Basé sur cette réponse

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

        if let coordinator = transitionCoordinator() where animated {
            coordinator.animateAlongsideTransition(nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }

    func popViewController(animated: Bool, completion: () -> ()) {
        popViewControllerAnimated(animated)

        if let coordinator = transitionCoordinator() where animated {
            coordinator.animateAlongsideTransition(nil) { _ in
                completion()
            }
        } else {
            completion()
        }
    }
}
Question

Lorsque vous dismissViewController un contrôleur de vue modale à l'aide de la méthode dismissViewController , vous dismissViewController la possibilité de fournir un bloc d'achèvement. Existe-t-il un équivalent similaire pour popViewController ?

L'argument d'achèvement est assez pratique. Par exemple, je peux l'utiliser pour suspendre la suppression d'une ligne d'une tableview jusqu'à ce que le modal soit hors écran, laissant l'utilisateur voir l'animation de la ligne. En revenant d'un contrôleur de vue poussé, je voudrais la même opportunité.

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

S'il n'y a pas une telle méthode disponible, quelles sont certaines solutions de contournement?




Travailler avec ou sans animation correctement, et comprend également popToRootViewController :

 // updated for Swift 3.0
extension UINavigationController {

  private func doAfterAnimatingTransition(animated: Bool, completion: @escaping (() -> Void)) {
    if let coordinator = transitionCoordinator, animated {
      coordinator.animate(alongsideTransition: nil, completion: { _ in
        completion()
      })
    } else {
      DispatchQueue.main.async {
        completion()
      }
    }
  }

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

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

  func popToRootViewController(animated: Bool, completion: @escaping (() -> Void)) {
    popToRootViewController(animated: animated)
    doAfterAnimatingTransition(animated: animated, completion: completion)
  }
}



Le bloc d'achèvement est appelé après l'appel de la méthode viewDidDisappear sur le contrôleur de vue présenté. Le code putant dans la méthode viewDidDisappear du contrôleur de vue éclatée doit donc fonctionner comme un bloc d'achèvement.




Pour 2018 ...

si vous avez ceci ...

    navigationController?.popViewController(animated: false)
    nextStep()

et vous voulez ajouter une complétion ...

    CATransaction.begin()
    navigationController?.popViewController(animated: true)
    CATransaction.setCompletionBlock({ [weak self] in
       self?.nextStep() })
    CATransaction.commit()

c'est si simple.




J'ai réalisé exactement cela avec précision en utilisant un bloc. Je voulais que mon contrôleur de résultats récupéré affiche la ligne ajoutée par la vue modale, seulement une fois qu'il avait complètement quitté l'écran, afin que l'utilisateur puisse voir le changement se produire. En préparation de segue qui est responsable de l'affichage du contrôleur de vue modale, je définis le bloc que je veux exécuter lorsque le modal disparaît. Et dans le contrôleur de vue modale, je substitue viewDidDissapear, puis appelle le bloc. Je commence simplement les mises à jour quand le modal va apparaître et met fin aux mises à jour quand il disparaît, mais c'est parce que j'utilise un NSFetchedResultsController mais vous pouvez faire ce que vous voulez dans le bloc.

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
    if([segue.identifier isEqualToString:@"addPassword"]){

        UINavigationController* nav = (UINavigationController*)segue.destinationViewController;
        AddPasswordViewController* v = (AddPasswordViewController*)nav.topViewController;

...

        // makes row appear after modal is away.
        [self.tableView beginUpdates];
        [v setViewDidDissapear:^(BOOL animated) {
            [self.tableView endUpdates];
        }];
    }
}

@interface AddPasswordViewController : UITableViewController<UITextFieldDelegate>

...

@property (nonatomic, copy, nullable) void (^viewDidDissapear)(BOOL animated);

@end

@implementation AddPasswordViewController{

...

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    if(self.viewDidDissapear){
        self.viewDidDissapear(animated);
    }
}

@end



Il n'y a aucun moyen de faire ce que vous voulez out-of-the-box. c'est-à-dire qu'il n'y a pas de méthode avec un bloc d'achèvement pour faire apparaître un contrôleur de vue à partir d'une pile de navigation.

Ce que je ferais, c'est mettre la logique dans viewDidAppear . Cela sera appelé quand la vue aura fini d'apparaître à l'écran. Il sera appelé pour tous les différents scénarios du contrôleur de vue apparaissant, mais cela devrait aller.

Ou vous pouvez utiliser la méthode UINavigationControllerDelegate navigationController:didShowViewController:animated: pour faire une chose similaire. Ceci est appelé lorsque le contrôleur de navigation a fini d'appuyer sur ou d'ouvrir un contrôleur de vue.




Juste pour être complet, j'ai créé une catégorie Objective-C prête à l'emploi:

// UINavigationController+CompletionBlock.h

#import <UIKit/UIKit.h>

@interface UINavigationController (CompletionBlock)

- (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion;

@end
// UINavigationController+CompletionBlock.m

#import "UINavigationController+CompletionBlock.h"

@implementation UINavigationController (CompletionBlock)

- (UIViewController *)popViewControllerAnimated:(BOOL)animated completion:(void (^)()) completion {
    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        completion();
    }];

    UIViewController *vc = [self popViewControllerAnimated:animated];

    [CATransaction commit];

    return vc;
}

@end



Related