facebookarchive / AsyncDisplayKit

Smooth asynchronous user interfaces for iOS apps.
http://asyncdisplaykit.org
Other
13.4k stars 2.2k forks source link

Unable to layout a vertical line the full height of the cell #2958

Closed christianselig closed 7 years ago

christianselig commented 7 years ago

Using the latest version of AsyncDisplayKit (2.0.2), I'm attempting to layout a 3px wide vertical line the full height of the cell on the left side, as shown here:

screen shot 2017-01-31 at 3 29 02 pm

ASTableNode set up is very straightforward:

class ViewController: ASViewController<ASTableNode> {
    let tableNode = ASTableNode()

    init() {
        super.init(node: tableNode)

        tableNode.dataSource = self
        tableNode.delegate = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension ViewController: ASTableDataSource {
    func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int {
        return 50
    }

    func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock {
        return {
            return TestNode()
        }
    }
}

And the ASCellNode subclass is set up as follows:

class TestNode: ASCellNode {
    let lineNode = ASDisplayNode()

    let nameNode = ASTextNode()
    let usernameNode = ASTextNode()
    let ageNode = ASTextNode()
    let bodyNode = ASTextNode()

    override init() {
        super.init()

        automaticallyManagesSubnodes = true

        lineNode.backgroundColor = UIColor.magenta
        lineNode.style.width = ASDimension(unit: .points, value: 3.0)
        lineNode.style.height = ASDimension(unit: .fraction, value: 1.0)

        nameNode.attributedText = NSAttributedString(string: "John Smith", attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14.0)])
        usernameNode.attributedText = NSAttributedString(string: "@johnsmith", attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14.0)])
        ageNode.attributedText = NSAttributedString(string: "12w", attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 14.0)])
        bodyNode.attributedText = NSAttributedString(string: "Lorem ipsum dolor sit amet.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Sed lacinia, ex ut placerat lobortis, nunc metus.", attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 16.0)])
    }

    override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
        let horizontalStackSpec = ASStackLayoutSpec(direction: .horizontal, spacing: 10.0, justifyContent: .start, alignItems: .center, children: [nameNode, usernameNode, ageNode])
        horizontalStackSpec.style.alignSelf = .stretch

        let verticalStackSpec = ASStackLayoutSpec(direction: .vertical, spacing: 8.0, justifyContent: .start, alignItems: .stretch, children: [horizontalStackSpec, bodyNode])
        verticalStackSpec.style.flexShrink = 1.0

        let insetSpec = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2), child: lineNode)

        let horizontalLayoutSpec2 = ASStackLayoutSpec(direction: .horizontal, spacing: 0.0, justifyContent: .start, alignItems: .start, children: [insetSpec, verticalStackSpec])
        return horizontalLayoutSpec2
    }
}

The layoutSpecThatFits method is obviously the root of the issue, but I cannot for the life of me figure out how to simply position the line so it hugs the left side and its height spans the full height of the cell. If I simply return insetSpec in the above block, it works perfectly, but the addition of the line really messes everything up, and I'm at a loss as to how to fix it.

This code doesn't show the lineNode and logs this issue many times:

Cannot calculate size of node: constrainedSize is infinite and node does not override -calculateSizeThatFits: or specify a preferredSize. Try setting style.preferredSize. Node: <<ASDisplayNode: 0x15de25130> alpha:1.00 isLayerBacked:0 frame:{{0, 0}, {0, 0}} (null)>

If I do as it suggests and add lineNode.preferredSize = CGSize(width: 3.0, height: 20.0) it "works", but the height is hardcoded and as the layout is dynamic it doesn't really work.

Sample Project: LineNodeTest.zip

Adlai-Holler commented 7 years ago

@christianselig In horizontalSpec2 you might want to set alignItems: .stretch so that the insetSpec will always fill the height of the horizontal stack.

christianselig commented 7 years ago

@Adlai-Holler Oh wow, good call, thank you so much!

That worked 95%! It still yells at me about constrainedSize being infinite, however:

2017-01-31 17:19:14.309337 AsyncDisplayKitTest[1977:514552] Cannot calculate size of node: constrainedSize is infinite and node does not override -calculateSizeThatFits: or specify a preferredSize. Try setting style.preferredSize. Node: <<ASDisplayNode: 0x143e529a0> alpha:1.00 isLayerBacked:0 frame:{{0, 0}, {0, 0}} (null)>

christianselig commented 7 years ago

@rewcraig in Slack solved the last step, the key was:

try lineNode.style.preferredSize = CGSize(width: 3.0, height: 0) rather than setting lineNode.style.width & lineNode.style.height

Which worked delightfully! I'm admittedly a little confused why (any documentation on this by chance?) but it's not the craziest thing and works well.

christianselig commented 7 years ago

Argh, while it visually works, the provided solution does not work alongside ASCellNode being selected (lineNode disappears).

rewandy commented 7 years ago

Try enabling isLayerBacked on the lineNode. It might help prevent UIKit from trying to be smart and automatically changing your views' background colors during selection.

christianselig commented 7 years ago

@rewandy Bingo!