diff --git a/Source/ASDisplayNode+Beta.h b/Source/ASDisplayNode+Beta.h index da26bb9f3..f735db18c 100644 --- a/Source/ASDisplayNode+Beta.h +++ b/Source/ASDisplayNode+Beta.h @@ -169,6 +169,7 @@ extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable // These methods should not normally be called directly. - (void)invalidateCalculatedYogaLayout; - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize; +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute; @end diff --git a/Source/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm index 18447b4a6..8fbb5c515 100644 --- a/Source/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -328,6 +328,18 @@ - (void)invalidateCalculatedYogaLayout } } +- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute; +{ + YGDirection effectiveDirection = YGDirectionInherit; + if (attribute != UISemanticContentAttributeUnspecified && AS_AT_LEAST_IOS9) { + UIUserInterfaceLayoutDirection attributeDirection = + [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:attribute]; + effectiveDirection = (attributeDirection == UIUserInterfaceLayoutDirectionLeftToRight + ? YGDirectionLTR : YGDirectionRTL); + } + self.style.direction = effectiveDirection; +} + - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize { if (self.yogaParent) { @@ -357,14 +369,14 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize ASLayoutElementStyle *style = node.style; YGNodeRef yogaNode = node.yogaNode; - YGNodeStyleSetDirection (yogaNode, YGDirectionInherit); + YGNodeStyleSetDirection (yogaNode, style.direction); YGNodeStyleSetFlexWrap (yogaNode, style.flexWrap); YGNodeStyleSetFlexGrow (yogaNode, style.flexGrow); YGNodeStyleSetFlexShrink (yogaNode, style.flexShrink); YGNODE_STYLE_SET_DIMENSION (yogaNode, FlexBasis, style.flexBasis); - YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.direction)); + YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.flexDirection)); YGNodeStyleSetJustifyContent(yogaNode, yogaJustifyContent(style.justifyContent)); YGNodeStyleSetAlignSelf (yogaNode, yogaAlignSelf(style.alignSelf)); ASStackLayoutAlignItems alignItems = style.alignItems; @@ -379,7 +391,7 @@ - (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize ASEdgeInsets border = style.border; YGEdge edge = YGEdgeLeft; - for (int i = 0; i < YGEdgeAll + 1; i++) { + for (int i = 0; i < YGEdgeAll + 1; ++i) { YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Position, dimensionForEdgeWithEdgeInsets(edge, position), edge); YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Margin, dimensionForEdgeWithEdgeInsets(edge, margin), edge); YGNODE_STYLE_SET_DIMENSION_WITH_EDGE(yogaNode, Padding, dimensionForEdgeWithEdgeInsets(edge, padding), edge); 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..a8a74e2a1 100644 --- a/Source/Details/UIView+ASConvenience.h +++ b/Source/Details/UIView+ASConvenience.h @@ -51,15 +51,16 @@ NS_ASSUME_NONNULL_BEGIN */ @protocol ASDisplayNodeViewProperties -@property (nonatomic, assign) BOOL clipsToBounds; -@property (nonatomic, getter=isHidden) BOOL hidden; -@property (nonatomic, assign) BOOL autoresizesSubviews; -@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; -@property (nonatomic, strong, null_resettable) UIColor *tintColor; -@property (nonatomic, assign) CGFloat alpha; -@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) BOOL clipsToBounds; +@property (nonatomic, getter=isHidden) BOOL hidden; +@property (nonatomic, assign) BOOL autoresizesSubviews; +@property (nonatomic, assign) UIViewAutoresizing autoresizingMask; +@property (nonatomic, strong, null_resettable) UIColor *tintColor; +@property (nonatomic, assign) CGFloat alpha; +@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+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h index e533b200a..be1aa5079 100644 --- a/Source/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -214,6 +214,14 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat */ - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; +/** + * @abstract Informs the root node that the intrinsic size of the receiver is no longer valid. + * + * @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know + * that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen. + */ +- (void)setNeedsLayoutFromAbove; + /** * @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes * size is invalidated and may need to result in a different size as the current calculated size. diff --git a/Source/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm index a5139e0b7..78012a86e 100644 --- a/Source/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -773,6 +773,26 @@ - (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 + [self semanticContentAttributeDidChange:semanticContentAttribute]; +#endif + _setToViewOnly(semanticContentAttribute, semanticContentAttribute); + } +} + @end diff --git a/Source/Private/_ASPendingState.mm b/Source/Private/_ASPendingState.mm index 0baad1129..666325aa6 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,12 @@ - (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)flag _flags.setAsyncTransactionContainer = YES; } +- (void)setSemanticContentAttribute:(UISemanticContentAttribute)attribute +{ + semanticContentAttribute = attribute; + _flags.setSemanticContentAttribute = YES; +} + - (BOOL)isAccessibilityElement { return isAccessibilityElement; @@ -904,6 +914,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 +1059,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 +1134,7 @@ - (BOOL)hasChanges || flags.needsLayout || flags.setAsyncTransactionContainer || flags.setOpaque + || flags.setSemanticContentAttribute || flags.setIsAccessibilityElement || flags.setAccessibilityLabel || flags.setAccessibilityHint