From af7a2f09d782a0c8acbc5339409407227d99e436 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Mon, 9 May 2016 13:48:22 -0700 Subject: [PATCH] Add support for visibility depth This adds support for the concept of visibility depth. Visibility essentially defines the number of user actions it would take a user to have a view controller visible. Knowing a view controllers visibility depth allows view controllers to take action such as clearing out memory that can be restored at a later date. This patch also add two new view controller subclasses which adopt the ASManagesChildVisibilityDepth protocol. Any view controller that has child view controllers can adopt this protocol to indicate to the child what they're visibility is. For example, ASNavigationController will return a visibility depth of it's own visibilityDepth + 1 for a view controller that would be revealed by tapping the back button. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 34 ++++++ AsyncDisplayKit/ASNavigationController.h | 15 +++ AsyncDisplayKit/ASNavigationController.m | 121 ++++++++++++++++++++++ AsyncDisplayKit/ASTabBarController.h | 15 +++ AsyncDisplayKit/ASTabBarController.m | 98 ++++++++++++++++++ AsyncDisplayKit/ASViewController.h | 3 +- AsyncDisplayKit/ASViewController.mm | 68 +++++++++++- AsyncDisplayKit/ASVisibilityProtocols.h | 45 ++++++++ AsyncDisplayKit/ASVisibilityProtocols.m | 23 ++++ AsyncDisplayKit/AsyncDisplayKit.h | 3 + 10 files changed, 420 insertions(+), 5 deletions(-) create mode 100644 AsyncDisplayKit/ASNavigationController.h create mode 100644 AsyncDisplayKit/ASNavigationController.m create mode 100644 AsyncDisplayKit/ASTabBarController.h create mode 100644 AsyncDisplayKit/ASTabBarController.m create mode 100644 AsyncDisplayKit/ASVisibilityProtocols.h create mode 100644 AsyncDisplayKit/ASVisibilityProtocols.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 631fc6d0..722bae06 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -272,6 +272,17 @@ 68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */; }; 68EE0DBF1C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */; }; 68EE0DC01C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */; }; + 68FC85DE1CE29AB700EDD713 /* ASNavigationController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */; }; + 68FC85DF1CE29AB700EDD713 /* ASNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85DD1CE29AB700EDD713 /* ASNavigationController.m */; }; + 68FC85E21CE29B7E00EDD713 /* ASTabBarController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85E01CE29B7E00EDD713 /* ASTabBarController.h */; }; + 68FC85E31CE29B7E00EDD713 /* ASTabBarController.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85E01CE29B7E00EDD713 /* ASTabBarController.h */; }; + 68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */; }; + 68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */; }; + 68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85DD1CE29AB700EDD713 /* ASNavigationController.m */; }; + 68FC85E91CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */; }; + 68FC85EA1CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */; }; + 68FC85EB1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */; }; + 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */ = {isa = PBXBuildFile; fileRef = 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */; }; 698548631CA9E025008A345F /* ASEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 698548611CA9E025008A345F /* ASEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; }; 698548641CA9E025008A345F /* ASEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = 698548611CA9E025008A345F /* ASEnvironment.h */; settings = {ATTRIBUTES = (Public, ); }; }; 698C8B611CAB49FC0052DC3F /* ASLayoutableExtensibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 698C8B601CAB49FC0052DC3F /* ASLayoutableExtensibility.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -787,6 +798,12 @@ 68B8A4E01CBDB958007E4543 /* ASWeakProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASWeakProxy.m; sourceTree = ""; }; 68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMainSerialQueue.h; sourceTree = ""; }; 68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMainSerialQueue.mm; sourceTree = ""; }; + 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASNavigationController.h; sourceTree = ""; }; + 68FC85DD1CE29AB700EDD713 /* ASNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASNavigationController.m; sourceTree = ""; }; + 68FC85E01CE29B7E00EDD713 /* ASTabBarController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTabBarController.h; sourceTree = ""; }; + 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTabBarController.m; sourceTree = ""; }; + 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASVisibilityProtocols.h; sourceTree = ""; }; + 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = ""; }; 698548611CA9E025008A345F /* ASEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEnvironment.h; sourceTree = ""; }; 698C8B601CAB49FC0052DC3F /* ASLayoutableExtensibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutableExtensibility.h; path = AsyncDisplayKit/Layout/ASLayoutableExtensibility.h; sourceTree = ""; }; 69CB62A91CB8165900024920 /* _ASDisplayViewAccessiblity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayViewAccessiblity.h; sourceTree = ""; }; @@ -1075,6 +1092,8 @@ 68355B2E1CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm */, 0516FA3E1A1563D200B4EBED /* ASMultiplexImageNode.h */, 0516FA3F1A1563D200B4EBED /* ASMultiplexImageNode.mm */, + 68FC85DC1CE29AB700EDD713 /* ASNavigationController.h */, + 68FC85DD1CE29AB700EDD713 /* ASNavigationController.m */, 055B9FA61A1C154B00035D6D /* ASNetworkImageNode.h */, 055B9FA71A1C154B00035D6D /* ASNetworkImageNode.mm */, 25E327541C16819500A2170C /* ASPagerNode.h */, @@ -1083,6 +1102,8 @@ A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */, D785F6601A74327E00291744 /* ASScrollNode.h */, D785F6611A74327E00291744 /* ASScrollNode.m */, + 68FC85E01CE29B7E00EDD713 /* ASTabBarController.h */, + 68FC85E11CE29B7E00EDD713 /* ASTabBarController.m */, B0F880581BEAEC7500D17647 /* ASTableNode.h */, 9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */, 055F1A3219ABD3E3004DAFF1 /* ASTableView.h */, @@ -1098,6 +1119,8 @@ 764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */, 764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */, DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */, + 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */, + 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */, 92074A5E1CC8B9DD00918F75 /* tvOS */, 058D09E1195D050800B7D73C /* Details */, 058D0A01195D050800B7D73C /* Private */, @@ -1493,6 +1516,7 @@ 058D0A82195D060300B7D73C /* ASAssert.h in Headers */, 0516FA3C1A15563400B4EBED /* ASAvailability.h in Headers */, AEB7B01A1C5962EA00662EF4 /* ASDefaultPlayButton.h in Headers */, + 68FC85E21CE29B7E00EDD713 /* ASTabBarController.h in Headers */, ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */, 058D0A83195D060300B7D73C /* ASBaseDefines.h in Headers */, 054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */, @@ -1508,6 +1532,7 @@ AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */, AEEC47E11C20C2DD00EC1693 /* ASVideoNode.h in Headers */, DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */, + 68FC85DE1CE29AB700EDD713 /* ASNavigationController.h in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1533,6 +1558,7 @@ 0587F9BD1A7309ED00AFF0BA /* ASEditableTextNode.h in Headers */, DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */, 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */, + 68FC85E91CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */, 257754A81BEE44CD00737CA5 /* ASTextKitContext.h in Headers */, DB55C2611C6408D6004EDCF5 /* _ASTransitionContext.h in Headers */, 464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */, @@ -1667,6 +1693,7 @@ CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */, 92074A621CC8BA1900918F75 /* ASImageNode+tvOS.h in Headers */, B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */, + 68FC85E31CE29B7E00EDD713 /* ASTabBarController.h in Headers */, DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */, 9C70F20F1CDBE9FF007D6C76 /* ASLayoutManager.h in Headers */, B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, @@ -1674,6 +1701,7 @@ B35062171B010EFD0018CF92 /* ASDataController.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, 34EFC75B1B701BAF00AD841F /* ASDimension.h in Headers */, + 68FC85EA1CE29C7D00EDD713 /* ASVisibilityProtocols.h in Headers */, A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */, DBABFAFC1C6A8D2F0039EA4A /* _ASTransitionContext.h in Headers */, 9C70F2061CDA4F0C007D6C76 /* ASTraitCollection.h in Headers */, @@ -1975,6 +2003,7 @@ files = ( 058D0A22195D050800B7D73C /* _ASAsyncTransaction.mm in Sources */, E55D86321CA8A14000A0C26F /* ASLayoutable.mm in Sources */, + 68FC85E41CE29B7E00EDD713 /* ASTabBarController.m in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 68355B3A1CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m in Sources */, @@ -2004,6 +2033,7 @@ AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */, 9CFFC6C21CCAC768006A6476 /* ASTableNode.mm in Sources */, 205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */, + 68FC85EB1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */, 058D0A13195D050800B7D73C /* ASControlNode.mm in Sources */, 464052211A3F83C40061C0BA /* ASDataController.mm in Sources */, B30BF6531C5964B0004FCD53 /* ASLayoutManager.m in Sources */, @@ -2028,6 +2058,7 @@ 430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */, ACF6ED231B17843500DA7C62 /* ASInsetLayoutSpec.mm in Sources */, ACF6ED4C1B17847A00DA7C62 /* ASInternalHelpers.mm in Sources */, + 68FC85DF1CE29AB700EDD713 /* ASNavigationController.m in Sources */, ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */, DB55C2631C6408D6004EDCF5 /* _ASTransitionContext.m in Sources */, 92074A631CC8BA1900918F75 /* ASImageNode+tvOS.m in Sources */, @@ -2161,6 +2192,7 @@ 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */, E55D86331CA8A14000A0C26F /* ASLayoutable.mm in Sources */, + 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */, 68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */, 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */, 69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */, @@ -2209,6 +2241,7 @@ 044285101BAA64EC00D16268 /* ASMultidimensionalArrayUtils.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, 0442850A1BAA63FE00D16268 /* ASBatchFetching.m in Sources */, + 68FC85E61CE29B9400EDD713 /* ASNavigationController.m in Sources */, 34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */, 254C6B8B1BF94F8A003EC431 /* ASTextKitShadower.mm in Sources */, 34EFC7661B701CD200AD841F /* ASRelativeSize.mm in Sources */, @@ -2223,6 +2256,7 @@ 9C70F2051CDA4F06007D6C76 /* ASTraitCollection.m in Sources */, 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */, DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */, + 68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */, AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */, 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, 92074A6A1CC8BADA00918F75 /* ASControlNode+tvOS.m in Sources */, diff --git a/AsyncDisplayKit/ASNavigationController.h b/AsyncDisplayKit/ASNavigationController.h new file mode 100644 index 00000000..c849f708 --- /dev/null +++ b/AsyncDisplayKit/ASNavigationController.h @@ -0,0 +1,15 @@ +// +// ASNavigationController.h +// Pods +// +// Created by Garrett Moon on 4/27/16. +// +// + +#import + +#import "ASVisibilityProtocols.h" + +@interface ASNavigationController : UINavigationController + +@end diff --git a/AsyncDisplayKit/ASNavigationController.m b/AsyncDisplayKit/ASNavigationController.m new file mode 100644 index 00000000..7f5af99d --- /dev/null +++ b/AsyncDisplayKit/ASNavigationController.m @@ -0,0 +1,121 @@ +// +// ASNavigationController.m +// Pods +// +// Created by Garrett Moon on 4/27/16. +// +// + +#import "ASNavigationController.h" + +@implementation ASNavigationController +{ + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; +} + +- (void)didMoveToParentViewController:(UIViewController *)parent +{ + [super didMoveToParentViewController:parent]; + [self visibilityDepthDidChange]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 0; + [self visibilityDepthDidChange]; + } +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 1; + [self visibilityDepthDidChange]; + } +} + +- (NSInteger)visibilityDepth +{ + if (self.parentViewController && _parentManagesVisibilityDepth == NO) { + _parentManagesVisibilityDepth = [self.parentViewController conformsToProtocol:@protocol(ASManagesChildVisibilityDepth)]; + } + + if (_parentManagesVisibilityDepth) { + return [(id )self.parentViewController visibilityDepthOfChildViewController:self]; + } + return _visibilityDepth; +} + +- (void)visibilityDepthDidChange +{ + for (UIViewController *viewController in self.viewControllers) { + if ([viewController conformsToProtocol:@protocol(ASVisibilityDepth)]) { + [(id )viewController visibilityDepthDidChange]; + } + } +} + +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController +{ + NSUInteger viewControllerIndex = [self.viewControllers indexOfObject:childViewController]; + NSAssert(viewControllerIndex != NSNotFound, @"childViewController is not in the navigation stack."); + + if (viewControllerIndex == self.viewControllers.count - 1) { + //view controller is at the top + return [self visibilityDepth] + 0; + } else if (viewControllerIndex == 0) { + //view controller is the root view controller. Can be accessed by holding the back button. + return [self visibilityDepth] + 1; + } + + return [self visibilityDepth] + self.viewControllers.count - 1 - viewControllerIndex; +} + +#pragma mark - UIKit overrides + +- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + NSArray *viewControllers = [super popToViewController:viewController animated:animated]; + [self visibilityDepthDidChange]; + return viewControllers; +} + +- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated +{ + NSArray *viewControllers = [super popToRootViewControllerAnimated:animated]; + [self visibilityDepthDidChange]; + return viewControllers; +} + +- (void)setViewControllers:(NSArray *)viewControllers +{ + [super setViewControllers:viewControllers]; + [self visibilityDepthDidChange]; +} + +- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated +{ + [super setViewControllers:viewControllers animated:animated]; + [self visibilityDepthDidChange]; +} + +- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated +{ + [super pushViewController:viewController animated:animated]; + [self visibilityDepthDidChange]; +} + +- (UIViewController *)popViewControllerAnimated:(BOOL)animated +{ + UIViewController *viewController = [super popViewControllerAnimated:animated]; + [self visibilityDepthDidChange]; + return viewController; +} + +@end diff --git a/AsyncDisplayKit/ASTabBarController.h b/AsyncDisplayKit/ASTabBarController.h new file mode 100644 index 00000000..6c633a8a --- /dev/null +++ b/AsyncDisplayKit/ASTabBarController.h @@ -0,0 +1,15 @@ +// +// ASTabBarController.h +// AsyncDisplayKit +// +// Created by Garrett Moon on 5/10/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import + +#import "ASVisibilityProtocols.h" + +@interface ASTabBarController : UITabBarController + +@end diff --git a/AsyncDisplayKit/ASTabBarController.m b/AsyncDisplayKit/ASTabBarController.m new file mode 100644 index 00000000..b083dba7 --- /dev/null +++ b/AsyncDisplayKit/ASTabBarController.m @@ -0,0 +1,98 @@ +// +// ASTabBarController.m +// AsyncDisplayKit +// +// Created by Garrett Moon on 5/10/16. +// Copyright © 2016 Facebook. All rights reserved. +// + +#import "ASTabBarController.h" + +@implementation ASTabBarController +{ + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; +} + +- (void)didMoveToParentViewController:(UIViewController *)parent +{ + [super didMoveToParentViewController:parent]; + [self visibilityDepthDidChange]; +} + +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 0; + [self visibilityDepthDidChange]; + } +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 1; + [self visibilityDepthDidChange]; + } +} + +- (NSInteger)visibilityDepth +{ + if (self.parentViewController && _parentManagesVisibilityDepth == NO) { + _parentManagesVisibilityDepth = [self.parentViewController conformsToProtocol:@protocol(ASManagesChildVisibilityDepth)]; + } + + if (_parentManagesVisibilityDepth) { + return [(id )self.parentViewController visibilityDepthOfChildViewController:self]; + } + return _visibilityDepth; +} + +- (void)visibilityDepthDidChange +{ + for (UIViewController *viewController in self.viewControllers) { + if ([viewController conformsToProtocol:@protocol(ASVisibilityDepth)]) { + [(id )viewController visibilityDepthDidChange]; + } + } +} + +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController +{ + if (self.selectedViewController == childViewController) { + return [self visibilityDepth]; + } + return [self visibilityDepth] + 1; +} + +#pragma mark - UIKit overrides + +- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers +{ + [super setViewControllers:viewControllers]; + [self visibilityDepthDidChange]; +} + +- (void)setViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers animated:(BOOL)animated +{ + [super setViewControllers:viewControllers animated:animated]; + [self visibilityDepthDidChange]; +} + +- (void)setSelectedIndex:(NSUInteger)selectedIndex +{ + [super setSelectedIndex:selectedIndex]; + [self visibilityDepthDidChange]; +} + +- (void)setSelectedViewController:(__kindof UIViewController *)selectedViewController +{ + [super setSelectedViewController:selectedViewController]; + [self visibilityDepthDidChange]; +} + +@end diff --git a/AsyncDisplayKit/ASViewController.h b/AsyncDisplayKit/ASViewController.h index 4102ee80..71a7e4a7 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/AsyncDisplayKit/ASViewController.h @@ -8,6 +8,7 @@ #import #import +#import @class ASTraitCollection; @@ -16,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitCollectionBlock)(UITraitCollection *traitCollection); typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(CGSize windowSize); -@interface ASViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController +@interface ASViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController - (instancetype)initWithNode:(DisplayNodeType)node NS_DESIGNATED_INITIALIZER; diff --git a/AsyncDisplayKit/ASViewController.mm b/AsyncDisplayKit/ASViewController.mm index db5b1e5b..80d8d2ec 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/AsyncDisplayKit/ASViewController.mm @@ -13,10 +13,14 @@ #import "ASDisplayNode+Beta.h" #import "ASRangeControllerUpdateRangeProtocol+Beta.h" +#define AS_LOG_VISIBILITY_CHANGES 1 + @implementation ASViewController { BOOL _ensureDisplayed; BOOL _automaticallyAdjustRangeModeBasedOnViewEvents; + BOOL _parentManagesVisibilityDepth; + NSInteger _visibilityDepth; } - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil @@ -81,21 +85,75 @@ [super viewDidLayoutSubviews]; } +- (void)didMoveToParentViewController:(UIViewController *)parent +{ + [super didMoveToParentViewController:parent]; + [self visibilityDepthDidChange]; +} + - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _ensureDisplayed = YES; [_node measureWithSizeRange:[self nodeConstrainedSize]]; [_node recursivelyFetchData]; - - [self updateCurrentRangeModeWithModeIfPossible:ASLayoutRangeModeFull]; + + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 0; + [self visibilityDepthDidChange]; + } } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - [self updateCurrentRangeModeWithModeIfPossible:ASLayoutRangeModeMinimum]; + if (_parentManagesVisibilityDepth == NO) { + _visibilityDepth = 1; + [self visibilityDepthDidChange]; + } +} + +- (NSInteger)visibilityDepth +{ + if (self.parentViewController && _parentManagesVisibilityDepth == NO) { + _parentManagesVisibilityDepth = [self.parentViewController conformsToProtocol:@protocol(ASManagesChildVisibilityDepth)]; + } + + if (_parentManagesVisibilityDepth) { + return [(id )self.parentViewController visibilityDepthOfChildViewController:self]; + } + return _visibilityDepth; +} + +- (void)visibilityDepthDidChange +{ + ASLayoutRangeMode rangeMode = ASLayoutRangeModeForVisibilityDepth(self.visibilityDepth); +#if AS_LOG_VISIBILITY_CHANGES + NSString *rangeModeString; + switch (rangeMode) { + case ASLayoutRangeModeMinimum: + rangeModeString = @"Minimum"; + break; + + case ASLayoutRangeModeFull: + rangeModeString = @"Full"; + break; + + case ASLayoutRangeModeVisibleOnly: + rangeModeString = @"Visible Only"; + break; + + case ASLayoutRangeModeLowMemory: + rangeModeString = @"Low Memory"; + break; + + default: + break; + } + NSLog(@"Updating visibility of:%@ to: %@ (visibility depth: %d)", self, rangeModeString, self.visibilityDepth); +#endif + [self updateCurrentRangeModeWithModeIfPossible:rangeMode]; } #pragma mark - Automatic range mode @@ -113,7 +171,9 @@ - (void)updateCurrentRangeModeWithModeIfPossible:(ASLayoutRangeMode)rangeMode { if (!_automaticallyAdjustRangeModeBasedOnViewEvents) { return; } - if (![_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]) { return; } + if (![_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]) { + return; + } id updateRangeNode = (id)_node; [updateRangeNode updateCurrentRangeWithMode:rangeMode]; diff --git a/AsyncDisplayKit/ASVisibilityProtocols.h b/AsyncDisplayKit/ASVisibilityProtocols.h new file mode 100644 index 00000000..39eb0d53 --- /dev/null +++ b/AsyncDisplayKit/ASVisibilityProtocols.h @@ -0,0 +1,45 @@ +// +// ASVisibilityProtocols.h +// Pods +// +// Created by Garrett Moon on 4/27/16. +// +// + +#import "ASLayoutRangeType.h" + +ASLayoutRangeMode ASLayoutRangeModeForVisibilityDepth(NSUInteger visibilityDepth); + +@protocol ASVisibilityDepth + +/** + * @abstract Represents the number of user actions necessary to reach the view controller. An increased visibility + * depth indicates a higher number of user interactions for the view controller to be visible again. For example, + * an onscreen navigation controller's top view controller should have a visibility depth of 0. The view controller + * one from the top should have a visibility deptch of 1 as should the root view controller in the stack (because + * the user can hold the back button to pop to the root view controller). + * + * Visibility depth is used to automatically adjust ranges on range controllers (and thus free up memory) and can + * be used to reduce memory usage of other items as well. + */ +- (NSInteger)visibilityDepth; + + +- (void)visibilityDepthDidChange; + +@end + +/** + * @abstract Container view controllers should adopt this protocol to indicate that they will manage their child's + * visibilityDepth. For example, ASNavigationController adopts this protocol and manages its childrens visibility + * depth. + * + * If you adopt this protocol, you *must* also emit visibilityDepthDidChange messages to child view controllers. + * + * @param childViewController Expected to return the visibility depth of the child view controller. + */ +@protocol ASManagesChildVisibilityDepth + +- (NSInteger)visibilityDepthOfChildViewController:(UIViewController *)childViewController; + +@end diff --git a/AsyncDisplayKit/ASVisibilityProtocols.m b/AsyncDisplayKit/ASVisibilityProtocols.m new file mode 100644 index 00000000..a739fd01 --- /dev/null +++ b/AsyncDisplayKit/ASVisibilityProtocols.m @@ -0,0 +1,23 @@ +// +// ASVisibilityProtocols.m +// Pods +// +// Created by Garrett Moon on 4/28/16. +// +// + +#import + +#import "ASVisibilityProtocols.h" + +ASLayoutRangeMode ASLayoutRangeModeForVisibilityDepth(NSUInteger visibilityDepth) +{ + if (visibilityDepth == 0) { + return ASLayoutRangeModeFull; + } else if (visibilityDepth == 1) { + return ASLayoutRangeModeMinimum; + } else if (visibilityDepth == 2) { + return ASLayoutRangeModeVisibleOnly; + } + return ASLayoutRangeModeLowMemory; +} \ No newline at end of file diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index 42a3baa5..7de390bb 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -34,6 +34,8 @@ #import #import +#import +#import #import #import @@ -78,6 +80,7 @@ #import #import #import +#import #import