From de4f1f079ce1fe520c612298dbe4f06c0b12197e Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sun, 23 Apr 2017 13:41:46 -0700 Subject: [PATCH] [RTL] Bridge the UISemanticContentAttribute property for more convenient RTL support. Although apps could handle this before by setting the view's property in didLoad, it's useful to bridge this property for setting during off-main initialization. This change also makes RTL fully functional / automatic for Yoga layout users. --- Source/ASDisplayNode.h | 1 + Source/Details/UIView+ASConvenience.h | 1 + Source/Private/ASDisplayNode+UIViewBridge.mm | 39 ++++++++++++++++++++ Source/Private/_ASPendingState.mm | 15 ++++++++ 4 files changed, 56 insertions(+) diff --git a/Source/ASDisplayNode.h b/Source/ASDisplayNode.h index e5d215b8b..b0c215699 100644 --- a/Source/ASDisplayNode.h +++ b/Source/ASDisplayNode.h @@ -692,6 +692,7 @@ extern NSInteger const ASDefaultDrawingPriority; * contentMode for your content while it's being re-rendered. */ @property (nonatomic, assign) UIViewContentMode contentMode; // default=UIViewContentModeScaleToFill +@property (nonatomic, assign) UISemanticContentAttribute semanticContentAttribute; // default=Unspecified @property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; // default=YES (NO for layer-backed nodes) #if TARGET_OS_IOS diff --git a/Source/Details/UIView+ASConvenience.h b/Source/Details/UIView+ASConvenience.h index 9c918689e..e7480b263 100644 --- a/Source/Details/UIView+ASConvenience.h +++ b/Source/Details/UIView+ASConvenience.h @@ -60,6 +60,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) CGRect bounds; @property (nonatomic, assign) CGRect frame; // Only for use with nodes wrapping synchronous views @property (nonatomic, assign) UIViewContentMode contentMode; +@property (nonatomic, assign) UISemanticContentAttribute semanticContentAttribute; @property (nonatomic, assign, getter=isUserInteractionEnabled) BOOL userInteractionEnabled; @property (nonatomic, assign, getter=isExclusiveTouch) BOOL exclusiveTouch; @property (nonatomic, assign, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer; diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index a5139e0b7..7e394a3d7 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -773,6 +773,45 @@ - (void)setEdgeAntialiasingMask:(unsigned int)edgeAntialiasingMask _setToLayer(edgeAntialiasingMask, edgeAntialiasingMask); } +- (UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_read; + if (AS_AT_LEAST_IOS9) { + return _getFromViewOnly(semanticContentAttribute); + } + return UISemanticContentAttributeUnspecified; +} + +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)semanticContentAttribute +{ + _bridge_prologue_write; + if (AS_AT_LEAST_IOS9) { +#if YOGA + YGDirection effectiveDirection = YGDirectionInherit; + // NOTE: It's not clear that this is more or less thread-safe than just calling the + // preferred +[UIView userInterfaceLayoutDirectionForSemanticContentAttribute:] method. + switch ([UIApplication sharedApplication].userInterfaceLayoutDirection) { + case UIUserInterfaceLayoutDirectionLeftToRight: + if (semanticContentAttribute == UISemanticContentAttributeForceRightToLeft) { + effectiveDirection = YGDirectionRTL; + } else { + effectiveDirection = YGDirectionLTR; + } + break; + case UIUserInterfaceLayoutDirectionRightToLeft: + if (semanticContentAttribute != UISemanticContentAttributeForceLeftToRight && + semanticContentAttribute != UISemanticContentAttributePlayback && + semanticContentAttribute != UISemanticContentAttributeSpatial) { + effectiveDirection = YGDirectionRTL; + } + break; + } + self.style.direction = effectiveDirection; +#endif + _setToViewOnly(semanticContentAttribute, semanticContentAttribute); + } +} + @end diff --git a/Source/Private/_ASPendingState.mm b/Source/Private/_ASPendingState.mm index 0baad1129..c10c2d55d 100644 --- a/Source/Private/_ASPendingState.mm +++ b/Source/Private/_ASPendingState.mm @@ -75,6 +75,7 @@ int setAccessibilityHeaderElements:1; int setAccessibilityActivationPoint:1; int setAccessibilityPath:1; + int setSemanticContentAttribute:1; } ASPendingStateFlags; @implementation _ASPendingState @@ -118,6 +119,7 @@ @implementation _ASPendingState NSArray *accessibilityHeaderElements; CGPoint accessibilityActivationPoint; UIBezierPath *accessibilityPath; + UISemanticContentAttribute semanticContentAttribute; ASPendingStateFlags _flags; } @@ -179,6 +181,7 @@ ASDISPLAYNODE_INLINE void ASPendingStateApplyMetricsToLayer(_ASPendingState *sta @synthesize borderWidth=borderWidth; @synthesize borderColor=borderColor; @synthesize asyncdisplaykit_asyncTransactionContainer=asyncTransactionContainer; +@synthesize semanticContentAttribute=semanticContentAttribute; static CGColorRef blackColorRef = NULL; @@ -259,6 +262,7 @@ - (instancetype)init accessibilityActivationPoint = CGPointZero; accessibilityPath = nil; edgeAntialiasingMask = (kCALayerLeftEdge | kCALayerRightEdge | kCALayerTopEdge | kCALayerBottomEdge); + semanticContentAttribute = UISemanticContentAttributeUnspecified; return self; } @@ -512,6 +516,11 @@ - (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)flag _flags.setAsyncTransactionContainer = YES; } +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)attribute { + semanticContentAttribute = attribute; + _flags.setSemanticContentAttribute = YES; +} + - (BOOL)isAccessibilityElement { return isAccessibilityElement; @@ -904,6 +913,10 @@ - (void)applyToView:(UIView *)view withSpecialPropertiesHandling:(BOOL)specialPr if (flags.setOpaque) ASDisplayNodeAssert(view.layer.opaque == opaque, @"Didn't set opaque as desired"); + if (flags.setSemanticContentAttribute) { + view.semanticContentAttribute = semanticContentAttribute; + } + if (flags.setIsAccessibilityElement) view.isAccessibilityElement = isAccessibilityElement; @@ -1045,6 +1058,7 @@ + (_ASPendingState *)pendingViewStateFromView:(UIView *)view pendingState.allowsGroupOpacity = layer.allowsGroupOpacity; pendingState.allowsEdgeAntialiasing = layer.allowsEdgeAntialiasing; pendingState.edgeAntialiasingMask = layer.edgeAntialiasingMask; + pendingState.semanticContentAttribute = view.semanticContentAttribute; pendingState.isAccessibilityElement = view.isAccessibilityElement; pendingState.accessibilityLabel = view.accessibilityLabel; pendingState.accessibilityHint = view.accessibilityHint; @@ -1119,6 +1133,7 @@ - (BOOL)hasChanges || flags.needsLayout || flags.setAsyncTransactionContainer || flags.setOpaque + || flags.setSemanticContentAttribute || flags.setIsAccessibilityElement || flags.setAccessibilityLabel || flags.setAccessibilityHint