I have a container node that implements layoutSpecThatFits, with subnodes that implement calculateSizeThatFits. While stepping through I see that my parent node's layoutSpecThatFits method is being called with an ASSizeRange that makes sense (meaning its width is bound by the parent nodes width, and the height is unbound).
What I'm seeing is that the calculateSizeThatFits method on subnode's is being called with a CGSize of what looks like CGFloat.max X CGFloat.max, I would have expected this method to be called with a size relative to the parent. The issue is that my subnodes, return a size relative to the constrainedSize passed to them in calculateSizeThatFits, is this not recommended or is there something else I might be missing here?
The issue is that eventually this leads to the following error, which I had seen previously when using the
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'subnodeLayout has an invalid position'
*** First throw call stack:
Parent node layoutSpecThatFits method: this gets called with: (ASSizeRange) $0 = (min = (width = 375, height = 0), max = (width = 375, height = 3.4028234663852886E+38))
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
var allChildStacks = [ASLayoutSpec]()
for var ii = 0; ii < choiceNodes.count; ii+=2 {
var children = [choiceNodes[ii]]
if ii + 1 < choiceNodes.count {
children.append(choiceNodes[ii + 1])
}
let aChoiceStack = ASStackLayoutSpec(direction: .Horizontal, spacing: 10, justifyContent: .Start, alignItems: .Start, children: children)
allChildStacks.append(aChoiceStack)
}
return ASStackLayoutSpec(direction: .Vertical, spacing: 10, justifyContent: .Start, alignItems: .Start, children: allChildStacks)
}
Subnodes calculateSizeThatFits method: this gets called with (CGSize) $R2 = (width = +Inf, height = +Inf)
override func calculateSizeThatFits(constrainedSize: CGSize) -> CGSize {
leftSwipeConfirmationNode.measure(constrainedSize)
rightSwipeConfirmationNode.measure(constrainedSize)
image.measure(CGSize(width: constrainedSize.width, height: constrainedSize.width))
return CGSize(width: constrainedSize.width, height: constrainedSize.width)
}
Thanks!
Jonathan
cc @levi, @Adlai-Holler, @nguyenhuy -- any tips?
@jellenbogen @appleguy This is because your subnode is inside a ASStackLayoutSpec that is horizontal (hence the Inf width) and also because that stack is inside a ASStackLayoutSpec that is vertical (hence the Inf height) these two combined results in what you are seeing. I'm pretty sure I've came across this issue, so let me see if I can dig up some code for you!
When I first started using the layout specs, I would have assumed the same behaviour that you probably assumed, which is that if you have a horizontal ASStackLayoutSpec as a subnode of a vertical ASStackLayoutSpec then the horizontal layout stack and it's children would have a width constrained to the width of the vertical stack and the children of the horizontal stack would have the height of it's parent not of the vertical stack. @appleguy This was the behaviour in ComponentKit, so maybe we can do some work to support this?
@jellenbogen What kind of layout are you trying to achieve in your subnodes calculateSizeThatFits method?
If you need the height of the parent then you can set horizontalStack.flexBasis = ASRelativeDimensionMakeWithPercent(1) (The percent range is from 0 to 1, which corresponds to 0% to 100%) which will then pass (width = Inf, height = parent's height).
If you then want to scale your subnodes relative to the parent's width (i.e not using Inf) then just set child.flexBasis = ASRelativeDimensionMakeWithPercent(0.25) on each subnode and that will give you a correct size for calculateSizeThatFits :)
@aaronschubert0 I see, yeah thats exactly what I was thinking. But it seems like regardless of where the Horizontal Stack layout, or any stack layout for that matter, lives it has an inf size in the direction of the stack?
If this is the case is there property that can limit this? Seems like there should be something equivalent to flexBasis for the direction of the stack? Since flex basis only controls the cross axis.
Or after thinking about it maybe, i'm just using the wrong combo of stack layouts. I'm trying to get my nodes to layout in a grid (see image). But I could just as well use 2 vertical stacks inside an other vertical stack where the 2 substacks have a flexBasis of 0.5. instead of rows I can work with columns...

@aaronschubert0 I ended up getting this working by simply adding the flex basis to the children of the horizontal stack layout. This makes me think that the horizontal stack layouts within the vertical stack are using the width of the vertical stack... Otherwise I'm not entirely sure what the relative dimensions on the child nodes in the horizontal stack are relative to?
Here is the code:
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
var allChildStacks = [ASLayoutSpec]()
for var ii = 0; ii < choiceNodes.count; ii+=2 {
let firstChoiceNode = choiceNodes[ii]
firstChoiceNode.flexBasis = ASRelativeDimensionMakeWithPercent(0.5)
var children = [firstChoiceNode]
if ii + 1 < choiceNodes.count {
let nextChoiceNode = choiceNodes[ii + 1]
nextChoiceNode.flexBasis = ASRelativeDimensionMakeWithPercent(0.5)
children.append(nextChoiceNode)
}
let aChoiceStack = ASStackLayoutSpec(direction: .Horizontal, spacing: 10, justifyContent: .Center, alignItems: .Center, children: children)
allChildStacks.append(aChoiceStack)
}
return ASStackLayoutSpec(direction: .Vertical, spacing: 10, justifyContent: .Start, alignItems: .Start, children: allChildStacks)
}
Most helpful comment
@jellenbogen What kind of layout are you trying to achieve in your subnodes
calculateSizeThatFitsmethod?If you need the height of the parent then you can set
horizontalStack.flexBasis = ASRelativeDimensionMakeWithPercent(1)(The percent range is from 0 to 1, which corresponds to 0% to 100%) which will then pass (width = Inf, height = parent's height).If you then want to scale your subnodes relative to the parent's width (i.e not using Inf) then just set
child.flexBasis = ASRelativeDimensionMakeWithPercent(0.25)on each subnode and that will give you a correct size forcalculateSizeThatFits:)