Skip to content

Commit

Permalink
[Layout] Add RTL support to LayoutSpecs (TextureGroup#1983)
Browse files Browse the repository at this point in the history
* [Layout] Add RTL support to LayoutSpecs

This is largely a slight update for TextureGroup#1805. If RTL is enabled, `calculateLayoutLayoutSpec:` will flip the origin of all sublayouts.

The new part of the diff is that ASBatchFetching now supports proper fetching on RTL horizontal scrollViews.

* Fix build and add RTL batch fetching tests
  • Loading branch information
rcancro authored and OhKanghoon committed Mar 22, 2024
1 parent afc1261 commit a59dacd
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 21 deletions.
21 changes: 21 additions & 0 deletions Source/ASDisplayNode+LayoutSpec.mm
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,27 @@ - (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize
}
layout = [layout filteredNodeLayoutTree];

// Flip layout if layout should be rendered right-to-left
BOOL shouldRenderRTLLayout = [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:_semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
if (shouldRenderRTLLayout) {
for (ASLayout *sublayout in layout.sublayouts) {
switch (_semanticContentAttribute) {
case UISemanticContentAttributeUnspecified:
case UISemanticContentAttributeForceRightToLeft: {
// Flip
CGPoint flippedPosition = CGPointMake(layout.size.width - CGRectGetWidth(sublayout.frame) - sublayout.position.x, sublayout.position.y);
sublayout.position = flippedPosition;
}
case UISemanticContentAttributePlayback:
case UISemanticContentAttributeForceLeftToRight:
case UISemanticContentAttributeSpatial:
// Don't flip
break;
}
}
}


return layout;
}

Expand Down
15 changes: 15 additions & 0 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,21 @@ - (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType
});
}

- (void)updateSemanticContentAttributeWithAttribute:(UISemanticContentAttribute)attribute
{
__instanceLock__.lock();
UISemanticContentAttribute oldAttribute = _semanticContentAttribute;
_semanticContentAttribute = attribute;
__instanceLock__.unlock();

ASPerformBlockOnMainThread(^{
// If the value has changed we should attempt to relayout.
if (attribute != oldAttribute) {
[self setNeedsLayout];
}
});
}

- (void)recursivelySetDisplaySuspended:(BOOL)flag
{
_recursivelySetDisplaySuspended(self, nil, flag);
Expand Down
1 change: 1 addition & 0 deletions Source/Private/ASBatchFetching.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ ASDK_EXTERN BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
CGPoint targetOffset,
CGFloat leadingScreens,
BOOL visible,
BOOL shouldRenderRTLLayout,
CGPoint velocity,
_Nullable id<ASBatchFetchingDelegate> delegate);

Expand Down
11 changes: 9 additions & 2 deletions Source/Private/ASBatchFetching.mm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ BOOL ASDisplayShouldFetchBatchForScrollView(UIScrollView<ASBatchFetchingScrollVi
CGFloat leadingScreens = scrollView.leadingScreensForBatching;
id<ASBatchFetchingDelegate> delegate = scrollView.batchFetchingDelegate;
BOOL visible = (scrollView.window != nil);
return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible, velocity, delegate);
BOOL shouldRenderRTLLayout = [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:scrollView.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
return ASDisplayShouldFetchBatchForContext(context, scrollDirection, scrollableDirections, bounds, contentSize, contentOffset, leadingScreens, visible, shouldRenderRTLLayout, velocity, delegate);
}

BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
Expand All @@ -40,6 +41,7 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
CGPoint targetOffset,
CGFloat leadingScreens,
BOOL visible,
BOOL shouldRenderRTLLayout,
CGPoint velocity,
id<ASBatchFetchingDelegate> delegate)
{
Expand Down Expand Up @@ -79,13 +81,18 @@ BOOL ASDisplayShouldFetchBatchForContext(ASBatchContext *context,
}

// If they are scrolling toward the head of content, don't batch fetch.
BOOL isScrollingTowardHead = (ASScrollDirectionContainsUp(scrollDirection) || ASScrollDirectionContainsLeft(scrollDirection));
BOOL isScrollingTowardHead = (ASScrollDirectionContainsUp(scrollDirection) || (shouldRenderRTLLayout ? ASScrollDirectionContainsRight(scrollDirection) : ASScrollDirectionContainsLeft(scrollDirection)));
if (isScrollingTowardHead) {
return NO;
}

CGFloat triggerDistance = viewLength * leadingScreens;
CGFloat remainingDistance = contentLength - viewLength - offset;
if (shouldRenderRTLLayout && ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) {
remainingDistance = offset;
} else {
remainingDistance = contentLength - viewLength - offset;
}
BOOL result = remainingDistance <= triggerDistance;

if (delegate != nil && velocityLength > 0.0) {
Expand Down
7 changes: 4 additions & 3 deletions Source/Private/ASDisplayNode+UIViewBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -944,14 +944,15 @@ - (void)setEdgeAntialiasingMask:(CAEdgeAntialiasingMask)edgeAntialiasingMask

- (UISemanticContentAttribute)semanticContentAttribute
{
_bridge_prologue_read;
return _getFromViewOnly(semanticContentAttribute);
AS::MutexLocker l(__instanceLock__);
return _semanticContentAttribute;
}

- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute
{
_bridge_prologue_write;
AS::MutexLocker l(__instanceLock__);
_setToViewOnly(semanticContentAttribute, semanticContentAttribute);
_semanticContentAttribute = semanticContentAttribute;
#if YOGA
[self semanticContentAttributeDidChange:semanticContentAttribute];
#endif
Expand Down
6 changes: 5 additions & 1 deletion Source/Private/ASDisplayNodeInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ static constexpr CACornerMask kASCACornerAllCorners =
// These properties are used on iOS 10 and lower, where safe area is not supported by UIKit.
UIEdgeInsets _fallbackSafeAreaInsets;


// Right-to-Left layout support
UISemanticContentAttribute _semanticContentAttribute;

#pragma mark - ASDisplayNode (Debugging)
ASLayout *_unflattenedLayout;
Expand Down Expand Up @@ -332,6 +333,9 @@ static constexpr CACornerMask kASCACornerAllCorners =
cornerRadius:(CGFloat)newCornerRadius
maskedCorners:(CACornerMask)newMaskedCorners;

/// Update the Semantic Content Attribute. Trigger layout if this value has changed.
- (void)updateSemanticContentAttributeWithAttribute:(UISemanticContentAttribute)attribute;

/// Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses.
- (instancetype)initWithViewClass:(Class)viewClass;

Expand Down
Loading

0 comments on commit a59dacd

Please sign in to comment.