diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h index 05fbd92..60b11e0 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreDismissViewControllerOperation.h @@ -9,9 +9,12 @@ #import "MVMCoreOperation.h" #import +#import @interface MVMCoreDismissViewControllerOperation : MVMCoreOperation +@property (nullable, weak, nonatomic) NSObject *delegate; + // Goes up the presentation chain to only dismiss the top presented. - (nullable instancetype)initAndDismissTopViewController:(nullable UIViewController *)viewController animated:(BOOL)animated; diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h index 32789d0..c235315 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h @@ -14,7 +14,7 @@ @class MVMCoreNavigationObject; @class MVMCoreLoadObject; -@interface MVMCoreNavigationHandler : NSObject +@interface MVMCoreNavigationHandler : NSObject // Returns the shared instance of this singleton + (nullable instancetype)sharedNavigationHandler; @@ -25,6 +25,8 @@ // reference to main navigation controller @property (nullable, weak, nonatomic) UINavigationController *navigationController; +/// A list of possible delegates looking for information. +@property (nonnull, strong, nonatomic) NSPointerArray *delegates; // Will navigate appropriately based on the load style - (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock; @@ -35,6 +37,14 @@ // pops or dimisses as needed - (void)removeCurrentViewController; +#pragma mark - Delegate Handling + +/// Adds a listener for navigation delegate functions +- (void)addDelegate:(nonnull id )delegate; + +/// Removes a listener for navigation delegate functions +- (void)removeDelegate:(nullable id )delegate; + #pragma mark - Navigation Simple // Uses the default navigation controller in app delegate diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m index 260a34f..a6f43d3 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m @@ -19,7 +19,7 @@ #import "MVMCoreLoadingOverlayHandler.h" #import "MVMCoreDispatchUtility.h" -@interface MVMCoreNavigationHandler () +@interface MVMCoreNavigationHandler() @property (strong, nonatomic) NSOperationQueue *navigationQueue; @property (strong, nonatomic) NSOperationQueue *presentationQueue; @@ -47,10 +47,14 @@ self.presentationQueue = [[NSOperationQueue alloc] init]; self.presentationQueue.maxConcurrentOperationCount = 1; + + self.delegates = (NSPointerArray *)[NSPointerArray weakObjectsPointerArray]; } return self; } +#pragma mark - Navigation Helpers + - (void)navigateWithLoadObject:(nullable MVMCoreLoadObject *)loadObject viewController:(nonnull UIViewController *)viewController delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { BOOL shouldAnimate = !loadObject.requestParameters.shouldNotAnimatePush; @@ -97,8 +101,8 @@ navigationObject.stopLoadingOverlay = YES; MVMCoreNavigationOperation *navigationOperation = [[MVMCoreNavigationOperation alloc] initWithNavigationObject:navigationObject]; - navigationOperation.delegate = delegate; - navigationOperation.completionBlock = completionBlock; + navigationOperation.delegate = self; + navigationOperation.completionBlock = [self getCompletionAndHandleDelegate:delegate completionHandler:completionBlock]; [self.navigationQueue addOperation:navigationOperation]; } @@ -284,8 +288,8 @@ [MVMCoreLoggingHandler addErrorToLog:error]; } else { MVMCorePresentViewControllerOperation *operation = [[MVMCorePresentViewControllerOperation alloc] initWithPresentingViewController:controllerToPresentOn presentedViewController:viewController animated:animated]; - operation.delegate = delegate; - operation.completionBlock = completionBlock; + operation.delegate = self; + operation.completionBlock = [self getCompletionAndHandleDelegate:delegate completionHandler:completionBlock]; [self.presentationQueue addOperation:operation]; } }]; @@ -296,7 +300,8 @@ // Dismiss on the main navigation controller. UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [UIApplication sharedApplication].keyWindow.rootViewController; MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissTopViewController:controllerToPresentOn animated:animated]; - operation.completionBlock = completionBlock; + operation.delegate = self; + operation.completionBlock = [self getCompletionAndHandleDelegate:delegate completionHandler:completionBlock]; [[NSOperationQueue mainQueue] addOperation:operation]; }]; } @@ -304,7 +309,8 @@ - (void)dismissViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissViewController:viewController animated:animated]; - operation.completionBlock = completionBlock; + operation.delegate = self; + operation.completionBlock = [self getCompletionAndHandleDelegate:delegate completionHandler:completionBlock]; [[NSOperationQueue mainQueue] addOperation:operation]; } @@ -313,9 +319,78 @@ // Dismiss on the main navigation controller. UIViewController *controllerToPresentOn = self.viewControllerToPresentOn ?: [UIApplication sharedApplication].keyWindow.rootViewController; MVMCoreDismissViewControllerOperation *operation = [[MVMCoreDismissViewControllerOperation alloc] initAndDismissViewController:controllerToPresentOn animated:animated]; - operation.completionBlock = completionBlock; + operation.delegate = self; + operation.completionBlock = [self getCompletionAndHandleDelegate:delegate completionHandler:completionBlock]; [[NSOperationQueue mainQueue] addOperation:operation]; }]; } +#pragma mark - Delegate Handling + +- (void)addDelegate:(nonnull id )delegate { + [self.delegates addPointer:(__bridge void *)(delegate)]; +} + +- (void)removeDelegate:(nullable id )delegate { + NSUInteger index = NSNotFound; + for (NSUInteger i = 0; i < self.delegates.count; i++) { + if (delegate == [self.delegates pointerAtIndex:i]) { + index = i; + break; + } + } + if (index != NSNotFound) { + [self.delegates removePointerAtIndex:index]; + } +} + +- (nullable void (^)(void))getCompletionAndHandleDelegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock { + if (!delegate) { + return completionBlock; + } + + [self addDelegate:delegate]; + __weak typeof(delegate) weakDelegate = delegate; + __weak typeof(self) weakSelf = self; + return ^void() { + [weakSelf removeDelegate:weakDelegate]; + completionBlock(); + }; +} + +// Below functions forward to all delegates. +- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { + for (id delegate in self.delegates) { + if (delegate && [delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) { + [delegate navigationController:navigationController willDisplayViewController:viewController]; + } + } +} + +- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { + for (id delegate in self.delegates) { + if (delegate && [delegate respondsToSelector:@selector(navigationController:displayedViewController:)]) { + [delegate navigationController:navigationController displayedViewController:viewController]; + } + } + // Legacy Notifier: Notify that page has changed + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:MVMCoreNotificationViewControllerChanged object:nil]; + }); +} + +- (nullable id )navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { + if (delegate && [delegate respondsToSelector:@selector(navigationController:animationControllerForOperation:fromViewController:toViewController:)]) { + id animatedTransitioning = [self.delegate navigationController:navigationController animationControllerForOperation:operation fromViewController:fromVC toViewController:toVC]; + if ([animatedTransitioning conformsToProtocol:@protocol(MVMCoreViewControllerAnimatedTransitioning)]) { + [animatedTransitioning addObserver:self forKeyPath:@"interactiveTransitionCanceled" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil]; + self.animatedTransitioning = animatedTransitioning; + } + return animatedTransitioning; + } else { + return nil; + } + +} + @end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m index 5a39b73..ff6e581 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m @@ -13,6 +13,7 @@ #import "MVMCoreLoggingHandler.h" #import "MVMCoreLoadingOverlayHandler.h" #import "MVMCoreViewControllerAnimatedTransitioning.h" +#import "MVMCoreNavigationHandler.h" @interface MVMCoreNavigationOperation () @@ -225,6 +226,8 @@ [super markAsFinished]; } +#pragma mark - Delegate + - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if (self.navigationObject.stopLoadingOverlay) { [[MVMCoreLoadingOverlayHandler sharedLoadingOverlay] stopLoading:YES]; @@ -234,6 +237,7 @@ if (self.delegate && [self.delegate respondsToSelector:@selector(navigationController:willDisplayViewController:)]) { [self.delegate navigationController:navigationController willDisplayViewController:viewController]; } + } - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {