ios uinavigationcontroller handler pushviewcontroller

ios - Controlador de finalización para UINavigationController "pushViewController: animado"?



handler (9)

Actualmente el UINavigationController no es compatible con esto. Pero está el UINavigationControllerDelegate que puede usar.

Una manera fácil de lograr esto es subclasificando UINavigationController y agregando una propiedad de bloque de terminación:

@interface PbNavigationController : UINavigationController <UINavigationControllerDelegate> @property (nonatomic,copy) dispatch_block_t completionBlock; @end @implementation PbNavigationController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.delegate = self; } return self; } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"didShowViewController:%@", viewController); if (self.completionBlock) { self.completionBlock(); self.completionBlock = nil; } } @end

Antes de presionar el nuevo controlador de vista, debe establecer el bloque de finalización:

UIViewController *vc = ...; ((PbNavigationController *)self.navigationController).completionBlock = ^ { NSLog(@"COMPLETED"); }; [self.navigationController pushViewController:vc animated:YES];

Esta nueva subclase se puede asignar en Interface Builder o se puede utilizar mediante programación de esta manera:

PbNavigationController *nc = [[PbNavigationController alloc]initWithRootViewController:yourRootViewController];

Estoy a punto de crear una aplicación usando un UINavigationController para presentar los siguientes controladores de vista. Con iOS5 hay un nuevo método para presentar UIViewControllers :

presentViewController:animated:completion:

Ahora me pregunto por qué no hay un controlador de finalización para UINavigationController ? Solo hay

pushViewController:animated:

¿Es posible crear mi propio controlador de finalización como el nuevo presentViewController:animated:completion: :?


Aquí está la versión Swift 4 con el Pop.

extension UINavigationController { public func pushViewController(viewController: UIViewController, animated: Bool, completion: (() -> Void)?) { CATransaction.begin() CATransaction.setCompletionBlock(completion) pushViewController(viewController, animated: animated) CATransaction.commit() } public func popViewController(animated: Bool, completion: (() -> Void)?) { CATransaction.begin() CATransaction.setCompletionBlock(completion) popViewController(animated: animated) CATransaction.commit() } }

Solo en caso de que alguien más necesitara esto.


Basado en la respuesta de par (que era la única que funcionaba con iOS9), pero más simple y con un elemento faltante (lo que podría haber llevado a que nunca se llamara a la finalización):

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


Desde iOS 7.0, puede usar UIViewControllerTransitionCoordinator para agregar un bloque de finalización de inserción:

UINavigationController *nav = self.navigationController; [nav pushViewController:vc animated:YES]; id<UIViewControllerTransitionCoordinator> coordinator = vc.transitionCoordinator; [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { } completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { NSLog(@"push completed"); }];


Para ampliar la respuesta de @Klaas (y como resultado de this pregunta) he agregado bloques de finalización directamente al método push:

@interface PbNavigationController : UINavigationController <UINavigationControllerDelegate> @property (nonatomic,copy) dispatch_block_t completionBlock; @property (nonatomic,strong) UIViewController * pushedVC; @end @implementation PbNavigationController - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { self.delegate = self; } return self; } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { NSLog(@"didShowViewController:%@", viewController); if (self.completionBlock && self.pushedVC == viewController) { self.completionBlock(); } self.completionBlock = nil; self.pushedVC = nil; } -(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if (self.pushedVC != viewController) { self.pushedVC = nil; self.completionBlock = nil; } } -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(dispatch_block_t)completion { self.pushedVC = viewController; self.completionBlock = completion; [self pushViewController:viewController animated:animated]; } @end

Para ser utilizado de la siguiente manera:

UIViewController *vc = ...; [(PbNavigationController *)self.navigationController pushViewController:vc animated:YES completion:^ { NSLog(@"COMPLETED"); }];


Se necesita un poco más de tuberías para agregar este comportamiento y conservar la capacidad de establecer un delegado externo.

Aquí hay una implementación documentada que mantiene la funcionalidad de delegado:

LBXCompletingNavigationController


Swift 2.0

extension UINavigationController : UINavigationControllerDelegate { private struct AssociatedKeys { static var currentCompletioObjectHandle = "currentCompletioObjectHandle" } typealias Completion = @convention(block) (UIViewController)->() var completionBlock:Completion?{ get{ let chBlock = unsafeBitCast(objc_getAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle), Completion.self) return chBlock as Completion }set{ if let newValue = newValue { let newValueObj : AnyObject = unsafeBitCast(newValue, AnyObject.self) objc_setAssociatedObject(self, &AssociatedKeys.currentCompletioObjectHandle, newValueObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } func popToViewController(animated: Bool,comp:Completion){ if (self.delegate == nil){ self.delegate = self } completionBlock = comp self.popViewControllerAnimated(true) } func pushViewController(viewController: UIViewController, comp:Completion) { if (self.delegate == nil){ self.delegate = self } completionBlock = comp self.pushViewController(viewController, animated: true) } public func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool){ if let comp = completionBlock{ comp(viewController) completionBlock = nil self.delegate = nil } } }


iOS 7+ Swift

EDITAR: He agregado una versión de Swift 3 de mi respuesta original. En esta versión eliminé la co-animación de ejemplo que se muestra en la versión de Swift 2, ya que parece haber confundido a muchas personas.

Swift 3:

import UIKit // Swift 3 version, no co-animation (alongsideTransition parameter is nil) extension UINavigationController { public func pushViewController( _ viewController: UIViewController, animated: Bool, completion: @escaping (Void) -> Void) { pushViewController(viewController, animated: animated) guard animated, let coordinator = transitionCoordinator else { completion() return } coordinator.animate(alongsideTransition: nil) { _ in completion() } } }

Swift 2:

import UIKit // Swift 2 Version, shows example co-animation (status bar update) extension UINavigationController { public func pushViewController( viewController: UIViewController, animated: Bool, completion: Void -> Void) { pushViewController(viewController, animated: animated) guard animated, let coordinator = transitionCoordinator() else { completion() return } coordinator.animateAlongsideTransition( // pass nil here or do something animated if you''d like, e.g.: { context in viewController.setNeedsStatusBarAppearanceUpdate() }, completion: { context in completion() } ) } }


Vea la respuesta de par para otra solución más actualizada

UINavigationController animaciones de UINavigationController se ejecutan con CoreAnimation , por lo que tendría sentido encapsular el código dentro de CATransaction y así establecer un bloque de finalización.

Swift :

Para swift, sugiero crear una extensión como tal

extension UINavigationController { public func pushViewController(viewController: UIViewController, animated: Bool, completion: @escaping (() -> Void)?) { CATransaction.begin() CATransaction.setCompletionBlock(completion) pushViewController(viewController, animated: animated) CATransaction.commit() } }

Uso:

navigationController?.pushViewController(vc, animated: true) { // Animation done }

C objetivo

Encabezamiento:

#import <UIKit/UIKit.h> @interface UINavigationController (CompletionHandler) - (void)completionhandler_pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion; @end

Implementación:

#import "UINavigationController+CompletionHandler.h" #import <QuartzCore/QuartzCore.h> @implementation UINavigationController (CompletionHandler) - (void)completionhandler_pushViewController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion { [CATransaction begin]; [CATransaction setCompletionBlock:completion]; [self pushViewController:viewController animated:animated]; [CATransaction commit]; } @end