ios transition - Bloc d'achèvement pour popViewController




style interactive (13)

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

Cela appellera une fermeture d'achèvement une fois que l'animation est terminée pour les fonctions push et 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()
    }
}

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?


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

Swift 3 réponse, merci à cette réponse: https://.com/a/28232570/3412567

    //MARK:UINavigationController Extension
extension UINavigationController {
    //Same function as "popViewController", but allow us to know when this function ends
    func popViewControllerWithHandler(completion: @escaping ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.popViewController(animated: true)
        CATransaction.commit()
    }
    func pushViewController(viewController: UIViewController, completion: @escaping ()->()) {
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        self.pushViewController(viewController, animated: true)
        CATransaction.commit()
    }
}

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 out-of-the-box

Ceci est techniquement correct car l'API UINavigationController n'offre aucune option pour cela. Cependant, en utilisant le framework 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: se termine. Cette fonctionnalité est disponible depuis iOS 4.


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.


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.


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

Version Swift 4 avec le paramètre facultatif viewController pour afficher un paramètre spécifique.

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

        pushViewController(viewController, animated: animated)

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

func popViewController(viewController: UIViewController? = nil, 
    animated: Bool, completion: @escaping () -> ()) {
        if let viewController = viewController {
            popToViewController(viewController, animated: animated)
        } else {
            popViewController(animated: animated)
        }

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

Basé sur la réponse de @ HotJard, quand tout ce que vous voulez est juste quelques lignes de code. Rapide et facile.

Swift 4 :

_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
    doWantIWantAfterContollerHasPopped()
}

J'ai récemment trouvé cette application appelée Icon Set Creator dans l' App Store qui est gratuit, sans publicité, mis à jour sur les nouvelles modifications, simple et fonctionne très bien pour toutes les tailles d'icônes possibles dans OSX, iOS et WatchOS:

Dans Icon Set Creator:

  1. Faites glisser votre image dans la vue
  2. Choisissez votre plateforme cible
  3. Exporter le dossier Icon Set

En XCode:

  1. Accédez au dossier Assets.xcassets
  2. Supprimer l'AppIcon préexistant
  3. Clic droit -> Importer votre Icon-Set créé en tant qu'AppIcon et vous avez terminé





ios cocoa-touch uiview uiviewcontroller uiviewanimation