Home asdisplaynode-layout
Post
Cancel

asdisplaynode-layout

ASDisplayNode.mm

canLayoutAsynchronous

1
2
3
4
- (BOOL)canLayoutAsynchronous
{
  return !self.isNodeLoaded;
}

__setNeedsLayout

1
2
3
4
- (void)__setNeedsLayout
{
  [self invalidateCalculatedLayout];
}

invalidateCalculatedLayout

1
2
3
4
5
6
7
8
9
10
11
12
- (void)invalidateCalculatedLayout
{
  MutexLocker l(__instanceLock__);
  
  _layoutVersion++;
  
  _unflattenedLayout = nil;

#if YOGA
  [self invalidateCalculatedYogaLayout];
#endif
}

__layout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
- (void)__layout
{
  ASDisplayNodeAssertThreadAffinity(self);
  // ASAssertUnlocked(__instanceLock__);
  
  BOOL loaded = NO;
  {
    AS::UniqueLock l(__instanceLock__);
    loaded = [self _locked_isNodeLoaded];
    CGRect bounds = _threadSafeBounds;
    
    if (CGRectEqualToRect(bounds, CGRectZero)) {
      // Performing layout on a zero-bounds view often results in frame calculations
      // with negative sizes after applying margins, which will cause
      // layoutThatFits: on subnodes to assert.
      as_log_debug(OS_LOG_DISABLED, "Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
      return;
    }
    
    // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as
    // this is supposed to happen within the layout transition process
    if (_transitionID != ASLayoutElementContextInvalidTransitionID) {
      return;
    }

    as_activity_create_for_scope("-[ASDisplayNode __layout]");

    // This method will confirm that the layout is up to date (and update if needed).
    // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
    l.unlock();
    [self _u_measureNodeWithBoundsIfNecessary:bounds];
    l.lock();
    
    [self _locked_layoutPlaceholderIfNecessary];
  }
  
  [self _layoutSublayouts];
  
  // Per API contract, `-layout` and `-layoutDidFinish` are called only if the node is loaded. 
  if (loaded) {
    ASPerformBlockOnMainThread(^{
      [self layout];
      [self _layoutClipCornersIfNeeded];
      [self _layoutDidFinish];
    });
  }

  [self _fallbackUpdateSafeAreaOnChildren];
}

_layoutDidFinish

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- (void)_layoutDidFinish
{
  ASDisplayNodeAssertMainThread();
  // ASAssertUnlocked(__instanceLock__);
  ASDisplayNodeAssertTrue(self.isNodeLoaded);
  [self layoutDidFinish];
}

#pragma mark Calculation

- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
                     restrictedToSize:(ASLayoutElementSize)size
                 relativeToParentSize:(CGSize)parentSize
{
  as_activity_scope_verbose(as_activity_create("Calculate node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
  as_log_verbose(ASLayoutLog(), "Calculating layout for %@ sizeRange %@", self, NSStringFromASSizeRange(constrainedSize));

#if AS_KDEBUG_ENABLE
  // We only want one calculateLayout signpost interval per thread.
  // Currently there is no fallback for profiling i386, since it's not useful.
  static _Thread_local NSInteger tls_callDepth;
  if (tls_callDepth++ == 0) {
    ASSignpostStart(ASSignpostCalculateLayout);
  }
#endif

  ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
  const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
  ASLayout *result = [self calculateLayoutThatFits:resolvedRange];
  as_log_verbose(ASLayoutLog(), "Calculated layout %@", result);

#if AS_KDEBUG_ENABLE
  if (--tls_callDepth == 0) {
    ASSignpostEnd(ASSignpostCalculateLayout);
  }
#endif
  
  return result;
}

calculateLayoutThatFits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
  __ASDisplayNodeCheckForLayoutMethodOverrides;

  switch (self.layoutEngineType) {
    case ASLayoutEngineTypeLayoutSpec:
      return [self calculateLayoutLayoutSpec:constrainedSize];
#if YOGA
    case ASLayoutEngineTypeYoga:
      return [self calculateLayoutYoga:constrainedSize];
#endif
      // If YOGA is not defined but for some reason the layout type engine is Yoga
      // we explicitly fallthrough here
    default:
      break;
  }

  // If this case is reached a layout type engine was defined for a node that is currently
  // not supported.
  ASDisplayNodeAssert(NO, @"No layout type determined");
  return nil;
}

layout

1
2
3
4
5
6
7
8
9
10
- (void)layout
{
  // Hook for subclasses
  ASDisplayNodeAssertMainThread();
  // ASAssertUnlocked(__instanceLock__);
  ASDisplayNodeAssertTrue(self.isNodeLoaded);
  [self enumerateInterfaceStateDelegates:^(id<ASInterfaceStateDelegate> del) {
    [del nodeDidLayout];
  }];
}

_layoutTransitionMeasurementDidFinish

1
2
3
4
- (void)_layoutTransitionMeasurementDidFinish
{
  // Hook for subclasses - No-Op in ASDisplayNode
}
1
2
3
4
5
6
7
8
9
10
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
{
  ASDisplayNodeAssertMainThread();

  [self didCompleteLayoutTransition:context];
  
  _pendingLayoutTransitionContext = nil;

  [self _pendingLayoutTransitionDidComplete];
}
This post is licensed under CC BY 4.0 by the author.