Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Layout] Add RTL support to LayoutSpecs #1983

Merged
merged 2 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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