| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- //
- // ASViewController.mm
- // AsyncDisplayKit
- //
- // Created by Huy Nguyen on 16/09/15.
- //
- // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under the BSD-style license found in the
- // LICENSE file in the root directory of this source tree. An additional grant
- // of patent rights can be found in the PATENTS file in the same directory.
- //
- #import <AsyncDisplayKit/ASViewController.h>
- #import <AsyncDisplayKit/ASAssert.h>
- #import <AsyncDisplayKit/ASAvailability.h>
- #import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
- #import <AsyncDisplayKit/ASLayout.h>
- #import <AsyncDisplayKit/ASTraitCollection.h>
- #import <AsyncDisplayKit/ASRangeControllerUpdateRangeProtocol+Beta.h>
- #import <AsyncDisplayKit/ASInternalHelpers.h>
- #define AS_LOG_VISIBILITY_CHANGES 0
- @implementation ASViewController
- {
- BOOL _ensureDisplayed;
- BOOL _automaticallyAdjustRangeModeBasedOnViewEvents;
- BOOL _parentManagesVisibilityDepth;
- NSInteger _visibilityDepth;
- BOOL _selfConformsToRangeModeProtocol;
- BOOL _nodeConformsToRangeModeProtocol;
- }
- - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
- {
- if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
- return nil;
- }
-
- [self _initializeInstance];
-
- return self;
- }
- - (instancetype)initWithCoder:(NSCoder *)aDecoder
- {
- if (!(self = [super initWithCoder:aDecoder])) {
- return nil;
- }
-
- [self _initializeInstance];
-
- return self;
- }
- - (instancetype)initWithNode:(ASDisplayNode *)node
- {
- if (!(self = [super initWithNibName:nil bundle:nil])) {
- return nil;
- }
-
- _node = node;
- [self _initializeInstance];
- return self;
- }
- - (void)_initializeInstance
- {
- if (_node == nil) {
- return;
- }
-
- _selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
- _nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
- _automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol;
-
- // In case the node will get loaded
- if (_node.nodeLoaded) {
- // Node already loaded the view
- [self view];
- } else {
- // If the node didn't load yet add ourselves as on did load observer to load the view in case the node gets loaded
- // before the view controller
- __weak __typeof__(self) weakSelf = self;
- [_node onDidLoad:^(__kindof ASDisplayNode * _Nonnull node) {
- if ([weakSelf isViewLoaded] == NO) {
- [weakSelf view];
- }
- }];
- }
- }
- - (void)dealloc
- {
- ASPerformBackgroundDeallocation(_node);
- }
- - (void)loadView
- {
- // Apple applies a frame and autoresizing masks we need. Allocating a view is not
- // nearly as expensive as adding and removing it from a hierarchy, and fortunately
- // we can avoid that here. Enabling layerBacking on a single node in the hierarchy
- // will have a greater performance benefit than the impact of this transient view.
- [super loadView];
-
- if (_node == nil) {
- return;
- }
-
- ASDisplayNodeAssertTrue(!_node.layerBacked);
-
- UIView *view = self.view;
- CGRect frame = view.frame;
- UIViewAutoresizing autoresizingMask = view.autoresizingMask;
-
- // We have what we need, so now create and assign the view we actually want.
- view = _node.view;
- _node.frame = frame;
- _node.autoresizingMask = autoresizingMask;
- self.view = view;
-
- // ensure that self.node has a valid trait collection before a subclass's implementation of viewDidLoad.
- // Any subnodes added in viewDidLoad will then inherit the proper environment.
- ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
- [self propagateNewTraitCollection:traitCollection];
- }
- - (void)viewWillLayoutSubviews
- {
- [super viewWillLayoutSubviews];
-
- // Before layout, make sure that our trait collection containerSize actually matches the size of our bounds.
- // If not, we need to update the traits and propagate them.
- CGSize boundsSize = self.view.bounds.size;
- if (CGSizeEqualToSize(self.node.primitiveTraitCollection.containerSize, boundsSize) == NO) {
- [UIView performWithoutAnimation:^{
- ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
- traitCollection.containerSize = boundsSize;
-
- // this method will call measure
- [self propagateNewTraitCollection:traitCollection];
- }];
- } else {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // Call layoutThatFits: to let the node prepare for a layout that will happen shortly in the layout pass of the view.
- // If the node's constrained size didn't change between the last layout pass it's a no-op
- [_node layoutThatFits:[self nodeConstrainedSize]];
- #pragma clang diagnostic pop
- }
- }
- - (void)viewDidLayoutSubviews
- {
- if (_ensureDisplayed && self.neverShowPlaceholders) {
- _ensureDisplayed = NO;
- [_node recursivelyEnsureDisplaySynchronously:YES];
- }
- [super viewDidLayoutSubviews];
- }
- ASVisibilityDidMoveToParentViewController;
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
- _ensureDisplayed = YES;
- // A layout pass is forced this early to get nodes like ASCollectionNode, ASTableNode etc.
- // into the hierarchy before UIKit applies the scroll view inset adjustments, if automatic subnode management
- // is enabled. Otherwise the insets would not be applied.
- [_node.view layoutIfNeeded];
-
- if (_parentManagesVisibilityDepth == NO) {
- [self setVisibilityDepth:0];
- }
- }
- ASVisibilitySetVisibilityDepth;
- ASVisibilityViewDidDisappearImplementation;
- ASVisibilityDepthImplementation;
- - (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
- - (BOOL)automaticallyAdjustRangeModeBasedOnViewEvents
- {
- return _automaticallyAdjustRangeModeBasedOnViewEvents;
- }
- - (void)setAutomaticallyAdjustRangeModeBasedOnViewEvents:(BOOL)automaticallyAdjustRangeModeBasedOnViewEvents
- {
- if (automaticallyAdjustRangeModeBasedOnViewEvents != _automaticallyAdjustRangeModeBasedOnViewEvents) {
- if (automaticallyAdjustRangeModeBasedOnViewEvents && _selfConformsToRangeModeProtocol == NO && _nodeConformsToRangeModeProtocol == NO) {
- NSLog(@"Warning: automaticallyAdjustRangeModeBasedOnViewEvents set to YES in %@, but range mode updating is not possible because neither view controller nor node %@ conform to ASRangeControllerUpdateRangeProtocol.", self, _node);
- }
- _automaticallyAdjustRangeModeBasedOnViewEvents = automaticallyAdjustRangeModeBasedOnViewEvents;
- }
- }
- - (void)updateCurrentRangeModeWithModeIfPossible:(ASLayoutRangeMode)rangeMode
- {
- if (!_automaticallyAdjustRangeModeBasedOnViewEvents) {
- return;
- }
-
- if (_selfConformsToRangeModeProtocol) {
- id<ASRangeControllerUpdateRangeProtocol> rangeUpdater = (id<ASRangeControllerUpdateRangeProtocol>)self;
- [rangeUpdater updateCurrentRangeWithMode:rangeMode];
- }
-
- if (_nodeConformsToRangeModeProtocol) {
- id<ASRangeControllerUpdateRangeProtocol> rangeUpdater = (id<ASRangeControllerUpdateRangeProtocol>)_node;
- [rangeUpdater updateCurrentRangeWithMode:rangeMode];
- }
- }
- #pragma mark - Layout Helpers
- - (ASSizeRange)nodeConstrainedSize
- {
- return ASSizeRangeMake(self.view.bounds.size);
- }
- - (ASInterfaceState)interfaceState
- {
- return _node.interfaceState;
- }
- #pragma mark - ASTraitEnvironment
- - (ASPrimitiveTraitCollection)primitiveTraitCollectionForUITraitCollection:(UITraitCollection *)traitCollection
- {
- if (self.overrideDisplayTraitsWithTraitCollection) {
- ASTraitCollection *asyncTraitCollection = self.overrideDisplayTraitsWithTraitCollection(traitCollection);
- return [asyncTraitCollection primitiveTraitCollection];
- }
-
- ASDisplayNodeAssertMainThread();
- ASPrimitiveTraitCollection asyncTraitCollection = ASPrimitiveTraitCollectionFromUITraitCollection(traitCollection);
- asyncTraitCollection.containerSize = self.view.frame.size;
- return asyncTraitCollection;
- }
- - (void)propagateNewTraitCollection:(ASPrimitiveTraitCollection)traitCollection
- {
- ASPrimitiveTraitCollection oldTraitCollection = self.node.primitiveTraitCollection;
-
- if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, oldTraitCollection) == NO) {
- self.node.primitiveTraitCollection = traitCollection;
-
- NSArray<id<ASLayoutElement>> *children = [self.node sublayoutElements];
- for (id<ASLayoutElement> child in children) {
- ASTraitCollectionPropagateDown(child, traitCollection);
- }
-
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wdeprecated-declarations"
- // Once we've propagated all the traits, layout this node.
- // Remeasure the node with the latest constrained size – old constrained size may be incorrect.
- [_node layoutThatFits:[self nodeConstrainedSize]];
- #pragma clang diagnostic pop
- }
- }
- - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
- {
- [super traitCollectionDidChange:previousTraitCollection];
-
- ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
- traitCollection.containerSize = self.view.bounds.size;
- [self propagateNewTraitCollection:traitCollection];
- }
- - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
- {
- [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
-
- ASPrimitiveTraitCollection traitCollection = _node.primitiveTraitCollection;
- traitCollection.containerSize = self.view.bounds.size;
- [self propagateNewTraitCollection:traitCollection];
- }
- @end
|