ASViewController.mm 11 KB

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