0

Here's a simple subclass of ASDisplayNode whose content is just 2 ASTextNode stacked on top of each other. If the secondary string is not present, I want just a single centered ASTextNode in the ASDisplayNode

I can layout the elements fine if I manually set the frames, but I'd like to be using a layout stack to lay things out automatically.

layoutSpecThatFits is not being called at all. I can force it to be called if I call measure on self in the init, but the frame of the resulting primaryTextNode is 0,0,0,0...why is layoutSpecThatFits not being called at all otherwise? If I call measure with only the primary string set, I'm also not sure why it's a zero rect for the ASTextNode after layout spec is called.

class ContentNode : ASDisplayNode {

    var primaryAttributedString : NSAttributedString {
        didSet {
            primaryTextNode.attributedString = primaryAttributedString
        }
    }

    private lazy var primaryTextNode : ASTextNode = {
        let node = ASTextNode()
        node.attributedString = self.primaryAttributedString
        node.maximumNumberOfLines = 1;
        node.flexGrow = true
        //        node.frame = I need to manually set here, layout spec that fits not called
        self.addSubnode(node)
        return node
    }()

    var secondaryAttributedString : NSAttributedString? {
        didSet {
            if secondaryAttributedString != nil {
                secondaryTextNode.attributedString = secondaryAttributedString
            }
        }
    }

    private lazy var secondaryTextNode : ASTextNode = {
        let node = ASTextNode()
        node.attributedString = self.secondaryAttributedString
        node.maximumNumberOfLines = 1;
        //        node.frame = need to manually set here, layout spec that fits not called

        self.addSubnode(node)
        return node
    }()

    init(frame: CGRect, primaryText : NSAttributedString, secondaryText : NSAttributedString?) {
        self.primaryAttributedString = primaryText
        self.secondaryAttributedString = secondaryText
        super.init()
        self.frame = frame
        //        self.measure(frame.size)
        backgroundColor = UIColor.clearColor()
    }

    // THIS NEVER GETS CALLED (unless i do a self.measure call in init, and even then it does not layout the text node properly even with just the primary text node)
    override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
        var mainStackContent = [ASLayoutable]()
        mainStackContent.append(self.primaryTextNode)
        if nil != secondaryAttributedString {
            mainStackContent.append(secondaryTextNode)
        }
        let contentSpec = ASStackLayoutSpec(direction: .Vertical, spacing: 2, justifyContent: .Center, alignItems: .Center, children: mainStackContent)

        return contentSpec
    }
}
Kevin
  • 1,883
  • 16
  • 23

2 Answers2

1

Inside your ContentNode class, you will need to call measure() on each of the text nodes. You can do that at any time after the attributedString property has been set. Then in the class that is instantiating ContentNode, you may need to call measure on the instance. I am not 100% sure on the last part. It might implicitly get called when you access the .view or .layer property of your instance.

The examples for layoutSpecThatFits I have seen usually involve ASTableView or ASCollectionView. And those two containers take care of calling measure() for you.

VTSoup
  • 36
  • 4
1

How are you hosting / creating this node? Check out the NSSpain talk for details (https://www.youtube.com/watch?v=RY_X7l1g79Q), somewhere near the end discusses tables, collections, view controllers.

You can put a node in an ASTableNode, ASCollectionNode, ASPagerNode, or ASViewController to have them be automatically and asynchronously measured. It should be a rare case to need to host a node yourself, but if you do need to do this then it is necessary to call me measure method.

Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177