ASViewController.mm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //
  2. // ASViewController.mm
  3. // AsyncDisplayKit
  4. //
  5. // Created by Huy Nguyen on 16/09/15.
  6. //
  7. // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
  8. // This source code is licensed under the BSD-style license found in the
  9. // LICENSE file in the root directory of this source tree. An additional grant
  10. // of patent rights can be found in the PATENTS file in the same directory.
  11. //
  12. #import <AsyncDisplayKit/ASViewController.h>
  13. #import <AsyncDisplayKit/ASAssert.h>
  14. #import <AsyncDisplayKit/ASAvailability.h>
  15. #import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
  16. #import <AsyncDisplayKit/ASLayout.h>
  17. #import <AsyncDisplayKit/ASTraitCollection.h>
  18. #import <AsyncDisplayKit/ASRangeControllerUpdateRangeProtocol+Beta.h>
  19. #import <AsyncDisplayKit/ASInternalHelpers.h>
  20. #define AS_LOG_VISIBILITY_CHANGES 0
  21. @implementation ASViewController
  22. {
  23. BOOL _ensureDisplayed;
  24. BOOL _automaticallyAdjustRangeModeBasedOnViewEvents;
  25. BOOL _parentManagesVisibilityDepth;
  26. NSInteger _visibilityDepth;
  27. BOOL _selfConformsToRangeModeProtocol;
  28. BOOL _nodeConformsToRangeModeProtocol;
  29. }
  30. - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  31. {
  32. if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
  33. return nil;
  34. }
  35. [self _initializeInstance];
  36. return self;
  37. }
  38. - (instancetype)initWithCoder:(NSCoder *)aDecoder
  39. {
  40. if (!(self = [super initWithCoder:aDecoder])) {
  41. return nil;
  42. }
  43. [self _initializeInstance];
  44. return self;
  45. }
  46. - (instancetype)initWithNode:(ASDisplayNode *)node
  47. {
  48. if (!(self = [super initWithNibName:nil bundle:nil])) {
  49. return nil;
  50. }
  51. _node = node;
  52. [self _initializeInstance];
  53. return self;
  54. }
  55. - (void)_initializeInstance
  56. {
  57. if (_node == nil) {
  58. return;
  59. }
  60. _selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
  61. _nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)];
  62. _automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol;
  63. // In case the node will get loaded
  64. if (_node.nodeLoaded) {
  65. // Node already loaded the view
  66. [self view];
  67. } else {
  68. // If the node didn't load yet add ourselves as on did load observer to load the view in case the node gets loaded
  69. // before the view controller
  70. __weak __typeof__(self) weakSelf = self;
  71. [_node onDidLoad:^(__kindof ASDisplayNode * _Nonnull node) {
  72. if ([weakSelf isViewLoaded] == NO) {
  73. [weakSelf view];
  74. }
  75. }];
  76. }
  77. }
  78. - (void)dealloc
  79. {
  80. ASPerformBackgroundDeallocation(_node);
  81. }
  82. - (void)loadView
  83. {
  84. // Apple applies a frame and autoresizing masks we need. Allocating a view is not
  85. // nearly as expensive as adding and removing it from a hierarchy, and fortunately
  86. // we can avoid that here. Enabling layerBacking on a single node in the hierarchy
  87. // will have a greater performance benefit than the impact of this transient view.
  88. [super loadView];
  89. if (_node == nil) {
  90. return;
  91. }
  92. ASDisplayNodeAssertTrue(!_node.layerBacked);
  93. UIView *view = self.view;
  94. CGRect frame = view.frame;
  95. UIViewAutoresizing autoresizingMask = view.autoresizingMask;
  96. // We have what we need, so now create and assign the view we actually want.
  97. view = _node.view;
  98. _node.frame = frame;
  99. _node.autoresizingMask = autoresizingMask;
  100. self.view = view;
  101. // ensure that self.node has a valid trait collection before a subclass's implementation of viewDidLoad.
  102. // Any subnodes added in viewDidLoad will then inherit the proper environment.
  103. ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
  104. [self propagateNewTraitCollection:traitCollection];
  105. }
  106. - (void)viewWillLayoutSubviews
  107. {
  108. [super viewWillLayoutSubviews];
  109. // Before layout, make sure that our trait collection containerSize actually matches the size of our bounds.
  110. // If not, we need to update the traits and propagate them.
  111. CGSize boundsSize = self.view.bounds.size;
  112. if (CGSizeEqualToSize(self.node.primitiveTraitCollection.containerSize, boundsSize) == NO) {
  113. [UIView performWithoutAnimation:^{
  114. ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
  115. traitCollection.containerSize = boundsSize;
  116. // this method will call measure
  117. [self propagateNewTraitCollection:traitCollection];
  118. }];
  119. } else {
  120. #pragma clang diagnostic push
  121. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  122. // Call layoutThatFits: to let the node prepare for a layout that will happen shortly in the layout pass of the view.
  123. // If the node's constrained size didn't change between the last layout pass it's a no-op
  124. [_node layoutThatFits:[self nodeConstrainedSize]];
  125. #pragma clang diagnostic pop
  126. }
  127. }
  128. - (void)viewDidLayoutSubviews
  129. {
  130. if (_ensureDisplayed && self.neverShowPlaceholders) {
  131. _ensureDisplayed = NO;
  132. [_node recursivelyEnsureDisplaySynchronously:YES];
  133. }
  134. [super viewDidLayoutSubviews];
  135. }
  136. ASVisibilityDidMoveToParentViewController;
  137. - (void)viewWillAppear:(BOOL)animated
  138. {
  139. [super viewWillAppear:animated];
  140. _ensureDisplayed = YES;
  141. // A layout pass is forced this early to get nodes like ASCollectionNode, ASTableNode etc.
  142. // into the hierarchy before UIKit applies the scroll view inset adjustments, if automatic subnode management
  143. // is enabled. Otherwise the insets would not be applied.
  144. [_node.view layoutIfNeeded];
  145. if (_parentManagesVisibilityDepth == NO) {
  146. [self setVisibilityDepth:0];
  147. }
  148. }
  149. ASVisibilitySetVisibilityDepth;
  150. ASVisibilityViewDidDisappearImplementation;
  151. ASVisibilityDepthImplementation;
  152. - (void)visibilityDepthDidChange
  153. {
  154. ASLayoutRangeMode rangeMode = ASLayoutRangeModeForVisibilityDepth(self.visibilityDepth);
  155. #if AS_LOG_VISIBILITY_CHANGES
  156. NSString *rangeModeString;
  157. switch (rangeMode) {
  158. case ASLayoutRangeModeMinimum:
  159. rangeModeString = @"Minimum";
  160. break;
  161. case ASLayoutRangeModeFull:
  162. rangeModeString = @"Full";
  163. break;
  164. case ASLayoutRangeModeVisibleOnly:
  165. rangeModeString = @"Visible Only";
  166. break;
  167. case ASLayoutRangeModeLowMemory:
  168. rangeModeString = @"Low Memory";
  169. break;
  170. default:
  171. break;
  172. }
  173. NSLog(@"Updating visibility of:%@ to: %@ (visibility depth: %d)", self, rangeModeString, self.visibilityDepth);
  174. #endif
  175. [self updateCurrentRangeModeWithModeIfPossible:rangeMode];
  176. }
  177. #pragma mark - Automatic range mode
  178. - (BOOL)automaticallyAdjustRangeModeBasedOnViewEvents
  179. {
  180. return _automaticallyAdjustRangeModeBasedOnViewEvents;
  181. }
  182. - (void)setAutomaticallyAdjustRangeModeBasedOnViewEvents:(BOOL)automaticallyAdjustRangeModeBasedOnViewEvents
  183. {
  184. if (automaticallyAdjustRangeModeBasedOnViewEvents != _automaticallyAdjustRangeModeBasedOnViewEvents) {
  185. if (automaticallyAdjustRangeModeBasedOnViewEvents && _selfConformsToRangeModeProtocol == NO && _nodeConformsToRangeModeProtocol == NO) {
  186. 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);
  187. }
  188. _automaticallyAdjustRangeModeBasedOnViewEvents = automaticallyAdjustRangeModeBasedOnViewEvents;
  189. }
  190. }
  191. - (void)updateCurrentRangeModeWithModeIfPossible:(ASLayoutRangeMode)rangeMode
  192. {
  193. if (!_automaticallyAdjustRangeModeBasedOnViewEvents) {
  194. return;
  195. }
  196. if (_selfConformsToRangeModeProtocol) {
  197. id<ASRangeControllerUpdateRangeProtocol> rangeUpdater = (id<ASRangeControllerUpdateRangeProtocol>)self;
  198. [rangeUpdater updateCurrentRangeWithMode:rangeMode];
  199. }
  200. if (_nodeConformsToRangeModeProtocol) {
  201. id<ASRangeControllerUpdateRangeProtocol> rangeUpdater = (id<ASRangeControllerUpdateRangeProtocol>)_node;
  202. [rangeUpdater updateCurrentRangeWithMode:rangeMode];
  203. }
  204. }
  205. #pragma mark - Layout Helpers
  206. - (ASSizeRange)nodeConstrainedSize
  207. {
  208. return ASSizeRangeMake(self.view.bounds.size);
  209. }
  210. - (ASInterfaceState)interfaceState
  211. {
  212. return _node.interfaceState;
  213. }
  214. #pragma mark - ASTraitEnvironment
  215. - (ASPrimitiveTraitCollection)primitiveTraitCollectionForUITraitCollection:(UITraitCollection *)traitCollection
  216. {
  217. if (self.overrideDisplayTraitsWithTraitCollection) {
  218. ASTraitCollection *asyncTraitCollection = self.overrideDisplayTraitsWithTraitCollection(traitCollection);
  219. return [asyncTraitCollection primitiveTraitCollection];
  220. }
  221. ASDisplayNodeAssertMainThread();
  222. ASPrimitiveTraitCollection asyncTraitCollection = ASPrimitiveTraitCollectionFromUITraitCollection(traitCollection);
  223. asyncTraitCollection.containerSize = self.view.frame.size;
  224. return asyncTraitCollection;
  225. }
  226. - (void)propagateNewTraitCollection:(ASPrimitiveTraitCollection)traitCollection
  227. {
  228. ASPrimitiveTraitCollection oldTraitCollection = self.node.primitiveTraitCollection;
  229. if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, oldTraitCollection) == NO) {
  230. self.node.primitiveTraitCollection = traitCollection;
  231. NSArray<id<ASLayoutElement>> *children = [self.node sublayoutElements];
  232. for (id<ASLayoutElement> child in children) {
  233. ASTraitCollectionPropagateDown(child, traitCollection);
  234. }
  235. #pragma clang diagnostic push
  236. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  237. // Once we've propagated all the traits, layout this node.
  238. // Remeasure the node with the latest constrained size – old constrained size may be incorrect.
  239. [_node layoutThatFits:[self nodeConstrainedSize]];
  240. #pragma clang diagnostic pop
  241. }
  242. }
  243. - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
  244. {
  245. [super traitCollectionDidChange:previousTraitCollection];
  246. ASPrimitiveTraitCollection traitCollection = [self primitiveTraitCollectionForUITraitCollection:self.traitCollection];
  247. traitCollection.containerSize = self.view.bounds.size;
  248. [self propagateNewTraitCollection:traitCollection];
  249. }
  250. - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
  251. {
  252. [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
  253. ASPrimitiveTraitCollection traitCollection = _node.primitiveTraitCollection;
  254. traitCollection.containerSize = self.view.bounds.size;
  255. [self propagateNewTraitCollection:traitCollection];
  256. }
  257. @end