Skip to content

Commit 6971422

Browse files
committed
Merge pull request facebookarchive#719 from rcancro/baselineFixes
Fixes to baseline stack alignment
2 parents e41f7c5 + 89a216b commit 6971422

File tree

5 files changed

+56
-42
lines changed

5 files changed

+56
-42
lines changed

AsyncDisplayKit/ASTextNode.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <AsyncDisplayKit/ASTextNodeTextKitHelpers.h>
1818
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
1919

20+
#import "ASInternalHelpers.h"
2021
#import "ASTextNodeRenderer.h"
2122
#import "ASTextNodeShadower.h"
2223
#import "ASEqualityHelpers.h"
@@ -344,6 +345,12 @@ - (void)setAttributedString:(NSAttributedString *)attributedString {
344345
self.isAccessibilityElement = YES;
345346
}
346347
});
348+
349+
if (attributedString.length > 0) {
350+
CGFloat screenScale = ASScreenScale();
351+
self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale;
352+
self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale;
353+
}
347354
}
348355

349356
#pragma mark - Text Layout

AsyncDisplayKit/Layout/ASStackLayoutDefines.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
4545
ASStackLayoutAlignItemsCenter,
4646
/** Expand children to fill cross axis */
4747
ASStackLayoutAlignItemsStretch,
48-
/** Children align to their first baseline. Only available for horizontal stack nodes */
48+
/** Children align to their first baseline. Only available for horizontal stack spec */
4949
ASStackLayoutAlignItemsBaselineFirst,
50-
/** Children align to their last baseline. Only available for horizontal stack nodes */
50+
/** Children align to their last baseline. Only available for horizontal stack spec */
5151
ASStackLayoutAlignItemsBaselineLast,
5252
};
5353

@@ -66,8 +66,4 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
6666
ASStackLayoutAlignSelfCenter,
6767
/** Expand to fill cross axis */
6868
ASStackLayoutAlignSelfStretch,
69-
/** Children align to their first baseline. Only available for horizontal stack nodes */
70-
ASStackLayoutAlignSelfBaselineFirst,
71-
/** Children align to their last baseline. Only available for horizontal stack nodes */
72-
ASStackLayoutAlignSelfBaselineLast,
7369
};

AsyncDisplayKit/Layout/ASStackLayoutSpec.mm

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,28 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
101101
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
102102
for (id<ASLayoutable> child in self.children) {
103103
stackChildren.push_back(child);
104-
needsBaselinePass |= child.alignSelf == ASStackLayoutAlignSelfBaselineFirst || child.alignSelf == ASStackLayoutAlignSelfBaselineLast;
105104
}
106105

107106
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
108107
const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize);
109108

110109
CGSize finalSize = CGSizeZero;
111110
NSArray *sublayouts = nil;
112-
if (needsBaselinePass) {
113-
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
111+
112+
// regardless of whether or not this stack aligns to baseline, we should let ASStackBaselinePositionedLayout::compute find the max ascender
113+
// and min descender in case this spec is a child in another spec that wants to align to a baseline.
114+
const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize);
115+
if (self.direction == ASStackLayoutDirectionVertical) {
116+
ASDN::MutexLocker l(_propertyLock);
117+
self.ascender = [[self.children firstObject] ascender];
118+
self.descender = [[self.children lastObject] descender];
119+
} else {
114120
ASDN::MutexLocker l(_propertyLock);
115121
self.ascender = baselinePositionedLayout.ascender;
116122
self.descender = baselinePositionedLayout.descender;
117-
123+
}
124+
125+
if (needsBaselinePass) {
118126
finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize);
119127
sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
120128
} else {

AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ static CGFloat maxDimensionForLayout(const ASLayout *l,
9393
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
9494
return a.layoutableObject.ascender < b.layoutableObject.ascender;
9595
});
96-
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
96+
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
9797

9898
/*
9999
Step 3: Take each child and update its layout position based on the baseline offset.
@@ -103,33 +103,40 @@ static CGFloat maxDimensionForLayout(const ASLayout *l,
103103
spacing between the two nodes is from the baseline, not the bounding box.
104104
105105
*/
106-
CGPoint p = CGPointZero;
107-
BOOL first = YES;
108-
auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
109-
__weak id<ASLayoutable> child = l.layoutableObject;
110-
p = p + directionPoint(style.direction, child.spacingBefore, 0);
111-
if (first) {
112-
// if this is the first item use the previously computed start point
113-
p = l.position;
114-
} else {
115-
// otherwise add the stack spacing
116-
p = p + directionPoint(style.direction, style.spacing, 0);
117-
}
118-
first = NO;
119-
120-
// Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position.
121-
l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline));
122-
123-
// If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing
124-
// node from baselines and not bounding boxes.
125-
CGFloat spacingAfterBaseline = 0;
126-
if (style.direction == ASStackLayoutDirectionVertical) {
127-
spacingAfterBaseline = child.descender;
128-
}
129-
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
130-
131-
return l;
132-
});
106+
std::vector<ASLayout *> stackedChildren;
107+
// Only change positions of layouts this stackSpec is aligning to a baseline. Otherwise we are only here to
108+
// compute the min/max descender/ascender for this stack spec.
109+
if (style.baselineRelativeArrangement || style.alignItems == ASStackLayoutAlignItemsBaselineFirst || style.alignItems == ASStackLayoutAlignItemsBaselineLast) {
110+
CGPoint p = CGPointZero;
111+
BOOL first = YES;
112+
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
113+
__weak id<ASLayoutable> child = l.layoutableObject;
114+
p = p + directionPoint(style.direction, child.spacingBefore, 0);
115+
if (first) {
116+
// if this is the first item use the previously computed start point
117+
p = l.position;
118+
} else {
119+
// otherwise add the stack spacing
120+
p = p + directionPoint(style.direction, style.spacing, 0);
121+
}
122+
first = NO;
123+
124+
// Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position.
125+
l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline));
126+
127+
// If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing
128+
// node from baselines and not bounding boxes.
129+
CGFloat spacingAfterBaseline = 0;
130+
if (style.direction == ASStackLayoutDirectionVertical) {
131+
spacingAfterBaseline = child.descender;
132+
}
133+
p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
134+
135+
return l;
136+
});
137+
} else {
138+
stackedChildren = positionedLayout.sublayouts;
139+
}
133140

134141
/*
135142
Step 4: Since we have been mucking with positions, there is a chance that our cross size has changed. Imagine a node with a font size of 40

AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment,
6363
return ASStackLayoutAlignItemsStart;
6464
case ASStackLayoutAlignSelfStretch:
6565
return ASStackLayoutAlignItemsStretch;
66-
case ASStackLayoutAlignSelfBaselineFirst:
67-
return ASStackLayoutAlignItemsBaselineFirst;
68-
case ASStackLayoutAlignSelfBaselineLast:
69-
return ASStackLayoutAlignItemsBaselineLast;
7066
case ASStackLayoutAlignSelfAuto:
7167
default:
7268
return stackAlignment;

0 commit comments

Comments
 (0)