diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 3203623ff8..0809fbf515 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,18 @@ 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; }; 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; }; + 9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; }; + 9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; + 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; + 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -445,15 +457,15 @@ 058D09D5195D050800B7D73C /* ASControlNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASControlNode.h; sourceTree = ""; }; 058D09D6195D050800B7D73C /* ASControlNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNode.m; sourceTree = ""; }; 058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Subclasses.h"; sourceTree = ""; }; - 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; }; 058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDisplayNode.mm; sourceTree = ""; }; - 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; }; + 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeExtras.h; sourceTree = ""; }; 058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeExtras.mm; sourceTree = ""; }; 058D09DD195D050800B7D73C /* ASImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageNode.h; sourceTree = ""; }; 058D09DE195D050800B7D73C /* ASImageNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASImageNode.mm; sourceTree = ""; }; 058D09DF195D050800B7D73C /* ASTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode.h; sourceTree = ""; }; - 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; }; + 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayLayer.h; sourceTree = ""; }; 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayLayer.mm; sourceTree = ""; }; 058D09E4195D050800B7D73C /* _ASDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayView.h; sourceTree = ""; }; @@ -556,6 +568,12 @@ 4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMultidimensionalArrayUtils.h; sourceTree = ""; }; 4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMultidimensionalArrayUtils.mm; sourceTree = ""; }; 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = ""; }; + 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselinePositionedLayout.h; sourceTree = ""; }; + 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselinePositionedLayout.mm; sourceTree = ""; }; + 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutable.h; sourceTree = ""; }; + 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = ""; }; + 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -921,6 +939,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */, + 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -943,6 +963,7 @@ children = ( ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, + 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, @@ -960,11 +981,14 @@ ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */, ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */, ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */, + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */, AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1006,6 +1030,7 @@ files = ( AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */, AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */, + 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */, ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */, ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, @@ -1024,6 +1049,7 @@ 464052201A3F83C40061C0BA /* ASDataController.h in Headers */, 05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, + 9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */, 058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */, 058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1045,6 +1071,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, + 9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1071,6 +1098,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, + 9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1136,6 +1164,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, + 9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1145,6 +1174,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, + 9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1172,6 +1202,7 @@ B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, + 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */, 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */, @@ -1187,6 +1218,7 @@ 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, + 9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */, 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */, B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1409,6 +1441,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, + 9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1447,6 +1480,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, + 9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1508,8 +1542,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, + 9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 7fcfbd5999..974bbe1272 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,7 +13,7 @@ #import #import -#import +#import /** * UIView creation block. Used to create the backing view of a new display node. @@ -40,7 +40,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index fc520c1232..1b35441c81 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,7 +7,7 @@ */ #import - +#import @protocol ASTextNodeDelegate; @@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @abstract Draws interactive rich text. @discussion Backed by TextKit. */ -@interface ASTextNode : ASControlNode +@interface ASTextNode : ASControlNode /** @abstract The attributed string to show. diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 68f4f1fdab..7ca68f5177 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,6 +17,7 @@ #import #import +#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -107,6 +108,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) UILongPressGestureRecognizer *_longPressGestureRecognizer; } +@synthesize ascender = _ascender; +@synthesize descender = _descender; + #pragma mark - NSObject - (instancetype)init @@ -355,6 +359,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); + + _ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + _descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h new file mode 100644 index 0000000000..8b784a56c3 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import +#import + +typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { + /** No baseline alignment. This is only valid for a vertical stack */ + ASBaselineLayoutBaselineAlignmentNone, + /** Align all children to the first baseline. This is only valid for a horizontal stack */ + ASBaselineLayoutBaselineAlignmentFirst, + /** Align all children to the last baseline. This is useful when a text node wraps and you want to align + to the bottom baseline. This is only valid for a horizontal stack */ + ASBaselineLayoutBaselineAlignmentLast, +}; + +/** + A specialized version of a stack layout that aligns its children on a baseline. This spec only works with + ASBaselineLayoutable children. + + If the spec is created with a horizontal direction, the children will be laid on a common baseline. + If the spec is created with a vertical direction, a child's vertical spacing will be measured from its + baseline instead of from the child's bounding box. +*/ +@interface ASBaselineLayoutSpec : ASLayoutSpec + +/** Specifies the direction children are stacked in. */ +@property (nonatomic, assign) ASStackLayoutDirection direction; +/** The amount of space between each child. */ +@property (nonatomic, assign) CGFloat spacing; +/** The amount of space between each child. */ +@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent; +/** Orientation of children along cross axis */ +@property (nonatomic, assign) ASStackLayoutAlignItems alignItems; +/** The type of baseline alignment */ +@property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment; + +- (void)addChild:(id)child; +- (void)addChildren:(NSArray *)children; + +/** + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param baselineAlignment The baseline to align to + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis + @param children ASLayoutable children to be positioned. + */ ++ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + children:(NSArray *)children; + +@end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm new file mode 100644 index 0000000000..670c69c991 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASBaselineLayoutSpec.h" +#import "ASStackLayoutable.h" + +#import +#import + +#import "ASBaseDefines.h" +#import "ASInternalHelpers.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" +#import "ASStackPositionedLayout.h" +#import "ASStackUnpositionedLayout.h" +#import "ASBaselinePositionedLayout.h" +#import "ASThread.h" + + +@implementation ASBaselineLayoutSpec +{ + std::vector> _children; + ASDN::RecursiveMutex _propertyLock; +} + +@synthesize ascender = _ascender; +@synthesize descender = _descender; + +- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children +{ + if (!(self = [super init])) { + return nil; + } + + ASDisplayNodeAssert((direction == ASStackLayoutDirectionHorizontal && baselineAlignment != ASBaselineLayoutBaselineAlignmentNone) || direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + _direction = direction; + _alignItems = alignItems; + _spacing = spacing; + _justifyContent = justifyContent; + _baselineAlignment = baselineAlignment; + + _children = std::vector>(); + for (id child in children) { + _children.push_back(child); + } + return self; +} + + ++ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children +{ + return [[ASBaselineLayoutSpec alloc] initWithDirection:direction spacing:spacing baselineAlignment:baselineAlignment justifyContent:justifyContent alignItems:alignItems children:children]; +} + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; + ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, stackStyle, constrainedSize); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); + const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); + + const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); + + NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; + + ASDN::MutexLocker l(_propertyLock); + _ascender = baselinePositionedLayout.ascender; + _descender = baselinePositionedLayout.descender; + + return [ASLayout layoutWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; +} + +- (void)addChild:(id)child +{ + _children.push_back(child); +} + +- (void)addChildren:(NSArray *)children +{ + for (id child in children) { + [self addChild:child]; + } +} + +@end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h new file mode 100644 index 0000000000..5e05029964 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "ASStackLayoutable.h" + +@protocol ASBaselineLayoutable + +/** + * @abstract The distance from the top of the layoutable object to its baseline + */ +@property (nonatomic, readwrite) CGFloat ascender; + +/** + * @abstract The distance from the bottom of the layoutable object to its baseline + */ +@property (nonatomic, readwrite) CGFloat descender; + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 8edb8d8477..bb200cbfe1 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,10 +8,10 @@ * */ -#import +#import -/** A layout spec is an temporarly mutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ +@interface ASLayoutSpec : NSObject /** Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 30bf7af5e8..4ac01ee05f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -20,43 +20,6 @@ */ @protocol ASLayoutable -/** - * @abstract Additional space to place before this object in the stacking direction. - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) CGFloat spacingBefore; - -/** - * @abstract Additional space to place after this object in the stacking direction. - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) CGFloat spacingAfter; - -/** - * @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) BOOL flexGrow; - -/** - * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) BOOL flexShrink; - -/** - * @abstract Specifies the initial size in the stack dimension for this object. - * Default to ASRelativeDimensionUnconstrained. - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) ASRelativeDimension flexBasis; - -/** - * @abstract Orientation of the object along cross axis, overriding alignItems - * Used when attached to a stack layout. - */ -@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; - /** * @abstract Calculate a layout based on given size range. * diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 6663ad2ae3..d7737f7eab 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -8,6 +8,45 @@ * */ +/** The direction children are stacked in */ +typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { + /** Children are stacked vertically */ + ASStackLayoutDirectionVertical, + /** Children are stacked horizontally */ + ASStackLayoutDirectionHorizontal, +}; + +/** If no children are flexible, how should this spec justify its children in the available space? */ +typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { + /** + On overflow, children overflow out of this spec's bounds on the right/bottom side. + On underflow, children are left/top-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentStart, + /** + On overflow, children are centered and overflow on both sides. + On underflow, children are centered within this spec's bounds in the stacking direction. + */ + ASStackLayoutJustifyContentCenter, + /** + On overflow, children overflow out of this spec's bounds on the left/top side. + On underflow, children are right/bottom-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentEnd, +}; + +/** Orientation of children along cross axis */ +typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { + /** Align children to start of cross axis */ + ASStackLayoutAlignItemsStart, + /** Align children with end of cross axis */ + ASStackLayoutAlignItemsEnd, + /** Center children on cross axis */ + ASStackLayoutAlignItemsCenter, + /** Expand children to fill cross axis */ + ASStackLayoutAlignItemsStretch +}; + /** Each child may override their parent stack's cross axis alignment. @see ASStackLayoutAlignItems diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index e71de22819..7825a9b8fc 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -9,45 +9,7 @@ */ #import - -/** The direction children are stacked in */ -typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { - /** Children are stacked vertically */ - ASStackLayoutDirectionVertical, - /** Children are stacked horizontally */ - ASStackLayoutDirectionHorizontal, -}; - -/** If no children are flexible, how should this spec justify its children in the available space? */ -typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { - /** - On overflow, children overflow out of this spec's bounds on the right/bottom side. - On underflow, children are left/top-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentStart, - /** - On overflow, children are centered and overflow on both sides. - On underflow, children are centered within this spec's bounds in the stacking direction. - */ - ASStackLayoutJustifyContentCenter, - /** - On overflow, children overflow out of this spec's bounds on the left/top side. - On underflow, children are right/bottom-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentEnd, -}; - -/** Orientation of children along cross axis */ -typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { - /** Align children to start of cross axis */ - ASStackLayoutAlignItemsStart, - /** Align children with end of cross axis */ - ASStackLayoutAlignItemsEnd, - /** Center children on cross axis */ - ASStackLayoutAlignItemsCenter, - /** Expand children to fill cross axis */ - ASStackLayoutAlignItemsStretch, -}; +#import /** @@ -93,7 +55,7 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { */ + (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children; -- (void)addChild:(id)child; +- (void)addChild:(id)child; - (void)addChildren:(NSArray *)children; @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 9d02cecb8a..3149979dd6 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -20,10 +20,11 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" +#import "ASThread.h" @implementation ASStackLayoutSpec { - std::vector> _children; + std::vector> _children; } - (instancetype)init @@ -46,14 +47,14 @@ _spacing = spacing; _justifyContent = justifyContent; - _children = std::vector>(); - for (id child in children) { + _children = std::vector>(); + for (id child in children) { _children.push_back(child); } return self; } -- (void)addChild:(id)child +- (void)addChild:(id)child { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); _children.push_back(child); @@ -61,7 +62,7 @@ - (void)addChildren:(NSArray *)children { - for (id child in children) { + for (id child in children) { [self addChild:child]; } } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h new file mode 100644 index 0000000000..b23b4fbbdb --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@protocol ASStackLayoutable + +/** + * @abstract Additional space to place before this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) CGFloat spacingBefore; + +/** + * @abstract Additional space to place after this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) CGFloat spacingAfter; + +/** + * @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) BOOL flexGrow; + +/** + * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) BOOL flexShrink; + +/** + * @abstract Specifies the initial size in the stack dimension for this object. + * Default to ASRelativeDimensionUnconstrained. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; + +/** + * @abstract Orientation of the object along cross axis, overriding alignItems + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +@end diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h new file mode 100644 index 0000000000..e3857d5ed4 --- /dev/null +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASLayout.h" +#import "ASDimension.h" +#import "ASBaselineLayoutSpec.h" +#import "ASStackPositionedLayout.h" + +typedef struct { + /** Describes how the stack will be laid out */ + ASStackLayoutSpecStyle stackLayoutStyle; + + /** The type of baseline alignment */ + ASBaselineLayoutBaselineAlignment baselineAlignment; +} ASBaselineLayoutSpecStyle; + +struct ASBaselinePositionedLayout { + const std::vector sublayouts; + const CGFloat crossSize; + const CGFloat ascender; + const CGFloat descender; + + /** Given a positioned layout, computes each child position using baseline alignment. */ + static ASBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineLayoutSpecStyle &style, + const ASSizeRange &constrainedSize); +}; diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm new file mode 100644 index 0000000000..2e3d2b3bfe --- /dev/null +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASBaselinePositionedLayout.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" + +static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, + const ASLayout *layout) { + + __weak id child = (id) layout.layoutableObject; + switch (style.baselineAlignment) { + case ASBaselineLayoutBaselineAlignmentNone: + return 0; + case ASBaselineLayoutBaselineAlignmentFirst: + return child.ascender; + case ASBaselineLayoutBaselineAlignmentLast: + return layout.size.height + child.descender; + } + +} + +static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, + const ASLayout *l, + const CGFloat maxAscender, + const CGFloat maxBaseline) +{ + if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { + __weak id child = (id)l.layoutableObject; + switch (style.baselineAlignment) { + case ASBaselineLayoutBaselineAlignmentFirst: + return maxAscender - child.ascender; + case ASBaselineLayoutBaselineAlignmentLast: + return maxBaseline - baselineForItem(style, l); + case ASBaselineLayoutBaselineAlignmentNone: + return 0; + } + } + return 0; +} + +static CGFloat maxDimensionForLayout(const ASLayout *l, + const ASStackLayoutSpecStyle &style) +{ + CGFloat maxDimension = crossDimension(style.direction, l.size); + style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.position.x : maxDimension += l.position.y; + return maxDimension; +} + +ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineLayoutSpecStyle &style, + const ASSizeRange &constrainedSize) +{ + ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle; + + /* Step 1: Look at each child and determine the distance from the top of the child node it's baseline. + For example, let's say we have the following two text nodes and want to align them to the first baseline: + + Hello! Why, hello there! How + are you today? + + The first node has a font of size 14, the second a font of size 12. The first node will have a baseline offset of + the ascender of a font of size 14, the second will have a baseline of the ascender of a font of size 12. The first + baseline will be larger so we will keep that as the max baseline. + + However, if were to align from the last baseline we'd find the max baseline by taking the height of node and adding + the font's descender (its negative). In the case of the first node, which is only 1 line, this should be the same value as the ascender. + The second node, however, has a larger height and there will have a larger baseline offset. + */ + const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return baselineForItem(style, a) < baselineForItem(style, b); + }); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(style, *baselineIt); + + /* + Step 2: Find the max ascender for all of the children. + Imagine 3 nodes aligned horizontally, all with the same text but with font sizes of 12, 14, 16. Because it is has the largest + ascender node with font size of 16 will not need to move, the other two nodes will align to this node's baseline. The offset we will use + for each node is our computed maxAscender - node.ascender. If the 16pt node had an ascender of 10 and the 14pt node + had an ascender of 8, that means we will offset the 14pt node by 2 pts. + + Note: if we are alinging to the last baseline, then we don't need this value in our computation. However, we do want + our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. + */ + const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; + }); + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + + /* + Step 3: Take each child and update its layout position based on the baseline offset. + + If this is a horizontal stack, we take a positioned child and add to its y offset to align it to the maxBaseline of the children. + If this is a vertical stack, we add the child's descender to the location of the next child to position. This will ensure the + spacing between the two nodes is from the baseline, not the bounding box. + + */ + CGPoint p = CGPointZero; + BOOL first = YES; + auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ + __weak id child = (id) l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); + if (first) { + // if this is the first item use the previously computed start point + p = l.position; + } else { + // otherwise add the stack spacing + p = p + directionPoint(stackStyle.direction, stackStyle.spacing, 0); + } + first = NO; + + // 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. + l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline)); + + // 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 + // node from baselines and not bounding boxes. + CGFloat spacingAfterBaseline = 0; + if (stackStyle.direction == ASStackLayoutDirectionVertical) { + spacingAfterBaseline = child.descender; + } + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); + + return l; + }); + + /* + 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 + and another node with a font size of 12 but with multiple lines. We align these nodes to the first baseline, which will be the baseline of the node with + font size of 40 (max ascender). Now, we have to move the node with multiple lines down to the other node's baseline. This node with multiple lines will + extend below the first node farther than it did before aligning the baselines thus increasing the cross size. + + After finding the new cross size, we need to clamp it so that it fits within the constrainted size. + + */ + const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), + [&](ASLayout *a, ASLayout *b) { + return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); + }); + const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, stackStyle); + const auto minCrossSize = crossDimension(stackStyle.direction, constrainedSize.min); + const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); + const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); + + /* + Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements + ASBaselineLayoutable and needs an ascender and descender to lay itself out properly. + */ + const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ + return a.position.y + a.size.height < b.position.y + b.size.height; + }); + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id)(*descenderIt).layoutableObject).descender; + + return {stackedChildren, crossSize, maxAscender, minDescender}; +} diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 420258460f..84bbdac502 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -44,7 +44,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style const auto minCrossSize = crossDimension(style.direction, constrainedSize.min); const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - + CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ @@ -54,6 +54,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); return l.layout; }); diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 4112af8e66..93a2efcb20 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -16,7 +16,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout { const CGFloat violation; /** Given a set of children, computes the unpositioned layouts for those children. */ - static ASStackUnpositionedLayout compute(const std::vector> &children, + static ASStackUnpositionedLayout compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange); }; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 6e275c82c0..cc3be53d1c 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,7 +18,7 @@ /** Sizes the child given the parameters specified, and returns the computed layout. */ -static ASLayout *crossChildLayout(const id child, +static ASLayout *crossChildLayout(const id child, const ASStackLayoutSpecStyle style, const CGFloat stackMin, const CGFloat stackMax, @@ -186,7 +186,7 @@ static std::function isFlexibleInViolatio } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { return child.flexGrow && child.flexShrink; } @@ -195,7 +195,7 @@ ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific number then we may avoid the first "intrinsic" size calculation. */ -ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, +ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { @@ -283,7 +283,7 @@ static void flexChildrenAlongStackDimension(std::vector Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and stretched. */ -static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, +static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange, const CGSize size, @@ -292,7 +292,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); - return AS::map(children, [&](id child) -> ASStackUnpositionedItem { + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); @@ -312,7 +312,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac }); } -ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, +ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 1e08bbe8c1..293b513d1e 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -58,9 +58,7 @@ 1A943BF0259746F18D6E423F /* Frameworks */, 1AE410B73DA5C3BD087ACDD7 /* Pods */, ); - indentWidth = 2; sourceTree = ""; - tabWidth = 2; }; 05E2128219D4DB510098F589 /* Products */ = { isa = PBXGroup;