Swift Readjust / Resize layout (ASDisplayNode) on show hide of the children node

1.1k Views Asked by At

I new to AsyncDisplayKit. So, I create a new app to learn AsyncDisplayKit animationTransition based on my real project code. The show / hide animation works perfect, but i dont know why the parent node (ASDisplayNode) is not readjusting the layout when the children's hidden (Sorry if my english is bad)

i already tried to put setNeedsLayout() on transitionLayout measurementCompletion, but nothing change

import AsyncDisplayKit

class HomeView: ASDisplayNode {
    let topWrapperNode: TopWrapperNode
    let loginButtonNode: LoginButtonNode

    override required init() {
        self.topWrapperNode = TopWrapperNode()
        self.loginButtonNode = LoginButtonNode()

        super.init()

        self.automaticallyManagesSubnodes = true
        self.automaticallyRelayoutOnSafeAreaChanges = true
        self.insetsLayoutMarginsFromSafeArea = true
    }

    override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
        let verticalStackSpec = ASStackLayoutSpec.vertical()

        verticalStackSpec.children = [
            self.topWrapperNode,
            self.loginButtonNode
        ]
        verticalStackSpec.alignItems = .stretch
        verticalStackSpec.justifyContent = .spaceBetween

        let displayInset = ASInsetLayoutSpec(
            insets: UIEdgeInsets(top: 0, left: 32, bottom: 16, right: 32),
            child: verticalStackSpec
        )

        return ASInsetLayoutSpec(insets: safeAreaInsets, child: displayInset)
    }

    func keyboardShowUpdateLayout(keyboardHeight: CGFloat) {
//        self.topWrapperNode.hideWelcomeLabelNode()
    }

    func keyboardHideUpdateLayout() {
//        self.topWrapperNode.showWelcomeLabelNode()
    }
}

// MARK - TopWrapperNode
class TopWrapperNode: ASDisplayNode {
    let welcomeLabelNode: WelcomeLabelNode
//    let textFieldNode: TextFieldNode

    override required init() {
        welcomeLabelNode = WelcomeLabelNode()
//        textFieldNode = TextFieldNode()

        super.init()

        self.automaticallyManagesSubnodes = true
        self.autoresizesSubviews = true
    }

    override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
        let verticalStackSpec = ASStackLayoutSpec.vertical()

        verticalStackSpec.children = [
            self.logoImage,
            self.welcomeLabelNode,
//            self.textFieldNode,
        ]
        verticalStackSpec.alignItems = .stretch
        verticalStackSpec.justifyContent = .spaceBetween

        self.backgroundColor = .yellow

        let displayInset = ASInsetLayoutSpec(
            insets: UIEdgeInsets(top: 24, left: 0, bottom: 0, right: 0),
            child: verticalStackSpec
        )

        return ASInsetLayoutSpec(insets: safeAreaInsets, child: displayInset)
    }

    private let logoImage: ASImageNode = {
        let imageNode = ASImageNode()

        imageNode.image = UIImage(named: "logo")
        imageNode.frame.size = CGSize(
            width: CGFloat(SizeScaler().moderateScale(size: 98)),
            height: CGFloat(SizeScaler().moderateScale(size: 48))
        )
        imageNode.contentMode = .scaleAspectFill
        imageNode.style.alignSelf = .center

        return imageNode
    }()

    func hideWelcomeLabelNode() {
        self.welcomeLabelNode.setHide(visibility: true)
        self.welcomeLabelNode.transitionLayout(withAnimation: true, shouldMeasureAsync: false)
    }

    func showWelcomeLabelNode() {
        self.welcomeLabelNode.setHide(visibility: false)
        self.welcomeLabelNode.transitionLayout(withAnimation: true, shouldMeasureAsync: false)
    }
}

// MARK: - WelcomeLabel
class WelcomeLabelNode: ASDisplayNode {
    var isHide: Bool = false

    override required init() {
        super.init()

        self.automaticallyManagesSubnodes = true
        self.autoresizesSubviews = true
        self.shouldAnimateSizeChanges = true
    }

    override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {
        let verticalStackSpec = ASStackLayoutSpec.vertical()

        verticalStackSpec.children = [self.welcomeTitleLabel, self.welcomeDescLabel]
        verticalStackSpec.alignItems = .start

        self.backgroundColor = .green

        return ASInsetLayoutSpec(
            insets: UIEdgeInsets(top: 0, left: 0, bottom: 40, right: 0),
            child: verticalStackSpec
        )
    }

    override func animateLayoutTransition(_ context: ASContextTransitioning) {
        if (self.isHide) {
            let initialTitle = context.initialFrame(for: self.welcomeTitleLabel)
            let initialDesc = context.initialFrame(for: self.welcomeDescLabel)

            self.welcomeTitleLabel.alpha = 1
            self.welcomeTitleLabel.frame = initialTitle
            self.welcomeDescLabel.alpha = 1
            self.welcomeDescLabel.frame = initialDesc

            var finalTitle = context.finalFrame(for: self.welcomeTitleLabel)
            finalTitle.origin.y -= 50
            var finalDesc = context.finalFrame(for: self.welcomeDescLabel)
            finalDesc.origin.y -= 50

            UIView.animate(withDuration: 0.4, animations: {
                self.welcomeTitleLabel.alpha = 0
                self.welcomeTitleLabel.frame = finalTitle
                self.welcomeDescLabel.alpha = 0
                self.welcomeDescLabel.frame = finalDesc
            }, completion: { finished in
                context.completeTransition(finished)
            })
        } else {
            var finalTitle = context.finalFrame(for: self.welcomeTitleLabel)
            finalTitle.origin.y -= 50
            var finalDesc = context.finalFrame(for: self.welcomeDescLabel)
            finalDesc.origin.y -= 50

            self.welcomeTitleLabel.alpha = 0
            self.welcomeTitleLabel.frame = finalTitle
            self.welcomeDescLabel.alpha = 0
            self.welcomeDescLabel.frame = finalDesc

            let initialTitle = context.initialFrame(for: self.welcomeTitleLabel)
            let initialDesc = context.initialFrame(for: self.welcomeDescLabel)

            UIView.animate(withDuration: 0.4, animations: {
                self.welcomeTitleLabel.alpha = 1
                self.welcomeTitleLabel.frame = initialTitle
                self.welcomeDescLabel.alpha = 1
                self.welcomeDescLabel.frame = initialDesc
            }, completion: { finished in
                context.completeTransition(finished)
            })
        }
    }

    let welcomeTitleLabel: QlueWorkLabel = {
        let label = QlueWorkLabel()

        label.setFont34(text: "Selamat datang!", fontType: "medium")
        label.textContainerInset = UIEdgeInsets(top: 32, left: 0, bottom: 8, right: 0)
        label.style.flexGrow = 1
        label.style.flexShrink = 1
        label.backgroundColor = .cyan

        return label
    }()

    let welcomeDescLabel: QlueWorkLabel = {
        let label = QlueWorkLabel()

        label.setFont16or20(
            text: "Pantau pekerjaanmu lebih mudah dengan QlueWork",
            fontType: "regular"
        )
        label.style.flexGrow = 1
        label.style.flexShrink = 1
        label.backgroundColor = .blue

        return label
    }()

    func setHide(visibility: Bool) {
        self.isHide = visibility
    }
}

i expect the parent node readjusting layout when the children is hide / show like the flexBox should be. Can anyone help me or tell me why did i do wrong?

1

There are 1 best solutions below

0
Wendy Liga On BEST ANSWER

after rendering complete, you cannot expect parentNode to adjust itself with changing its child dimension

but you can do work arround with ask parentNode to re-render itself like this

DispatchQueue.main.async{
     parentNode.transitionLayout(withAnimation: false,
                            shouldMeasureAsync: true, 
                            measurementCompletion: nil)           
}

make sure to run transitionLayout on main thread

Happy Texturing