From b00201b1abfa8887a4ee63b40bda0ae04f11f116 Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Wed, 7 Oct 2020 15:36:01 -0400 Subject: [PATCH 1/2] MFViewController Model Dynamic Root Non Root navigation item --- .../MVMCore/Models/Extensions/Decoder.swift | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/MVMCore/MVMCore/Models/Extensions/Decoder.swift b/MVMCore/MVMCore/Models/Extensions/Decoder.swift index 04c0be7..fc54a0d 100644 --- a/MVMCore/MVMCore/Models/Extensions/Decoder.swift +++ b/MVMCore/MVMCore/Models/Extensions/Decoder.swift @@ -16,8 +16,12 @@ extension JSONDecoder: AnyDecoder {} extension PropertyListDecoder: AnyDecoder {} extension Data { - public func decode(using decoder: AnyDecoder = JSONDecoder()) throws -> T { - return try decoder.decode(T.self, from: self) + public func decode(using decoder: AnyDecoder = JSONDecoder(), delegateObject: DelegateObject? = nil) throws -> T { + if let decoder = decoder as? JSONDecoder { + return try decoder.decode(T.self, from: self, delegateObject: delegateObject) + } else { + return try decoder.decode(T.self, from: self) + } } } @@ -35,10 +39,10 @@ extension KeyedDecodingContainerProtocol { } extension Decodable { - public static func decode(jsonDict: [String: Any]) throws -> Self { + public static func decode(jsonDict: [String: Any], delegateObject: DelegateObject? = nil) throws -> Self { let jsonData = try JSONSerialization.data(withJSONObject: jsonDict) do { - return try jsonData.decode() + return try jsonData.decode(delegateObject: delegateObject) } catch { throw JSONError.other(error: error) } @@ -73,6 +77,14 @@ public extension JSONDecoder { } userInfo.updateValue(delegateObject, forKey: key) } + + /// Decodes a top-level value of the given type from the given JSON representation, and adds the delegate object if provided. + func decode(_ type: T.Type, from data: Data, delegateObject: DelegateObject?) throws -> T where T : Decodable { + if let delegateObject = delegateObject { + try add(delegateObject: delegateObject) + } + return try decode(T.self, from: data) + } } public extension Decoder { From 2243bbd644040eee7e155d4f4e87f28ed693971a Mon Sep 17 00:00:00 2001 From: "Pfeil, Scott Robert" Date: Thu, 8 Oct 2020 11:31:40 -0400 Subject: [PATCH 2/2] Added view controller getter. --- .../MVMCoreNavigationHandler.h | 3 +++ .../MVMCoreNavigationHandler.m | 21 ++++++++++++++++ .../MVMCoreNavigationOperation.h | 4 ++++ .../MVMCoreNavigationOperation.m | 24 +++++++++++++++---- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h index 2810b3c..dbd71fa 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.h @@ -116,6 +116,9 @@ // Use this to pop to the root of the stack. If no navigation controller is provided, will use the main one in app delegate. - (void)popToRootAnimated:(BOOL)animated navigationController:(nullable UINavigationController *)navigationController delegate:(nullable NSObject*)delegate completionHandler:(nullable void (^)(void))completionBlock; +/// Returns the view controllers of the navigation controller. Takes into account navigation controller animation states. +- (nullable NSArray *)getViewControllersForNavigationController:(nullable UINavigationController *)navigationController; + #pragma mark - Presentation Simple // Use this to present. diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m index 5849d9f..d156890 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationHandler.m @@ -260,6 +260,27 @@ [self startNavigationWithNavigationObject:navigationObject delegate:delegate completionHandler:completionBlock]; } +- (nullable NSArray *)getViewControllersForNavigationController:(nullable UINavigationController *)navigationController { + // Check if we are currently animating. + NSInteger index = [self.navigationQueue.operations indexOfObjectPassingTest:^BOOL(__kindof MVMCoreNavigationOperation * _Nonnull operation, NSUInteger idx, BOOL * _Nonnull stop) { + if (operation.isExecuting) { + *stop = YES; + return YES; + } else { + return NO; + } + }]; + + if (index != NSNotFound) { + MVMCoreNavigationOperation *operation = [self.navigationQueue.operations objectAtIndex:index]; + if (operation.navigationObject.navigationController == navigationController && operation.futureViewControllers) { + // Return the future state if animating. + return operation.futureViewControllers; + } + } + return navigationController.viewControllers; +} + #pragma mark - Presentation Simple - (void)presentViewController:(nonnull UIViewController *)viewController animated:(BOOL)animated { diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h index 3bcd58d..349c00c 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.h @@ -15,8 +15,12 @@ @interface MVMCoreNavigationOperation : MVMCoreOperation +@property (nonnull, strong, nonatomic) MVMCoreNavigationObject *navigationObject; @property (nullable, weak, nonatomic) NSObject *delegate; +/// Used to keep track of future view controller state because .viewControllers is not reliable during animation. +@property (nullable, strong, nonatomic) NSArray *futureViewControllers; + - (nullable instancetype)initWithNavigationObject:(nonnull MVMCoreNavigationObject *)navigationObject; @end diff --git a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m index bdd32eb..b700005 100644 --- a/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m +++ b/MVMCore/MVMCore/PresentationHandling/MVMCoreNavigationOperation.m @@ -17,7 +17,6 @@ @interface MVMCoreNavigationOperation () -@property (strong, nonatomic) MVMCoreNavigationObject *navigationObject; @property (strong, nonatomic) id animatedTransitioning; @end @@ -95,7 +94,7 @@ switch (self.navigationObject.navigationType) { case NavigationTypePush: { - [self.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + [self pushViewController]; } break; case NavigationTypeSet: @@ -114,7 +113,7 @@ } else { // Just push - [self.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + [self pushViewController]; } } break; @@ -129,13 +128,15 @@ } else { // Just push - [self.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; + [self pushViewController]; } } break; case NavigationTypePop: { if (self.navigationObject.navigationController.viewControllers.count > 1) { + // Although the post animation state is currently fine with pop, store anyway as a precaution + self.futureViewControllers = [self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, self.navigationObject.navigationController.viewControllers.count - 1)]; [self.navigationObject.navigationController popViewControllerAnimated:self.navigationObject.animated]; } else { [self markAsFinished]; @@ -179,6 +180,10 @@ } if (self.navigationObject.viewController && (self.navigationObject.viewController != self.navigationObject.navigationController.topViewController)) { + // Store post animation state. + NSInteger index = [[self.navigationObject.navigationController viewControllers] indexOfObject:self.navigationObject.viewController]; + self.futureViewControllers = [self.navigationObject.navigationController.viewControllers subarrayWithRange:NSMakeRange(0, index + 1)]; + [self.navigationObject.navigationController popToViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; } else { [self markAsFinished]; @@ -188,6 +193,9 @@ case NavigationTypePopToRoot: { if (self.navigationObject.navigationController.viewControllers.count > 1) { + // Store post animation state. + self.futureViewControllers = @[self.navigationObject.navigationController.viewControllers.firstObject]; + [self.navigationObject.navigationController popToRootViewControllerAnimated:self.navigationObject.animated]; } else { [self markAsFinished]; @@ -202,8 +210,14 @@ }]]; } +- (void)pushViewController { + // Although the post animation state is currently fine with push, store anyway as a precaution + self.futureViewControllers = [self.navigationObject.navigationController.viewControllers arrayByAddingObject:self.navigationObject.viewController]; + [self.navigationObject.navigationController pushViewController:self.navigationObject.viewController animated:self.navigationObject.animated]; +} + - (void)setViewControllers:(NSArray *)viewControllers { - + self.futureViewControllers = viewControllers; if (![self.navigationObject.navigationController.viewControllers isEqualToArray:viewControllers]) { [self.navigationObject.navigationController setViewControllers:viewControllers animated:self.navigationObject.animated]; } else {