Hello,
My use case is simple:

I have a feed card with nodes in a vertical stack layout. The x Likes text node should be hidden when there are 0 likes. When user presses the like icon, the text node should expand using animation on the height (using pop for this). Any tips on how to initially hide the text node (e.g. set its height to 0, its frame/bounds to CGRectZero, hidden prop true) and later expand it using simple expand animation?
@simoami: Have you tried the Layout Transition API?
@hannahmbanana cool, taking a look. Coincidentally, I was experimenting with animateLayoutTransition which I found in one of the examples.
@simoami: even without the Layout Transition API, you can do a simple animation by simply including or excluding the component in the layoutSpecThatFits:. Check out the ASDKgram example in which the comments "animate" in when they are loaded.
@hannahmbanana Can you point me to the Animate code in question? Btw, the way I'm approaching this right now is:
liked boolean flaglikes countsetNeedsLayout()As a result, the entire cell animates to fit the new content, but the "x Likes" text node just appears immediately.
preview: https://cl.ly/0B333v1p2G0u
@simoami: is this with or without the Layout Transition API? could you share what you are looking for specifically with the reveal animation?
With Layout Transition API. Here's the Footer View code, which combines both the "x Likes" row and the actions bar below it:
FooterView.swift
import AsyncDisplayKit
class FooterView: ASDisplayNode {
...
func favoriteButtonPressed() {
liked = !liked
likes = max(0, likes + (liked ? 1 : -1))
post?.counts["likes"] = likes
transitionLayoutWithAnimation(true, shouldMeasureAsync: true) {
print("done animating. notify the parent cell node")
self.setNeedsLayout()
}
}
override init() {
super.init()
usesImplicitHierarchyManagement = true
// addSubnode(likesCommentsView)
// addSubnode(dividerLineView)
// addSubnode(favoriteButton)
// addSubnode(commentButton)
// addSubnode(shareButton)
// addSubnode(moreButton)
favoriteButton.addTarget(self, action: #selector(self.favoriteButtonPressed), forControlEvents: .TouchUpInside)
}
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
let hasLikesOrComments = comments > 0 || likes > 0
let spacer = ASLayoutSpec()
spacer.flexShrink = true
spacer.flexBasis = ASRelativeDimension(type: .Percent, value: 100)
let likesCommentsInset = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 12, left: 4, bottom: 12, right: 4), child: likesCommentsView)
dividerLineView.preferredFrameSize = CGSize(width: CGFloat.max, height: 0.4)
let dividerInset = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4), child: dividerLineView)
let hStack = ASStackLayoutSpec.horizontalStackLayoutSpec()
hStack.spacing = 4
hStack.setChildren([favoriteButton, commentButton, shareButton, spacer, moreButton])
let vStack = ASStackLayoutSpec.verticalStackLayoutSpec()
var children: [ASLayoutable] = []
if hasLikesOrComments {
children.append(likesCommentsInset)
}
children.append(dividerInset)
children.append(hStack)
vStack.setChildren(children)
let inset = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 0, left: 4, bottom: 0, right: 4), child: vStack)
return inset
}
override func animateLayoutTransition(context: ASContextTransitioning) {
if liked {
var hiddenFrame = context.finalFrameForNode(self.likesCommentsView)
hiddenFrame.size.height = 0
likesCommentsView.alpha = 0
likesCommentsView.frame = hiddenFrame
UIView.animateWithDuration(0.4, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.5, options: .BeginFromCurrentState, animations: { () -> Void in
self.likesCommentsView.alpha = 1
self.likesCommentsView.frame = context.finalFrameForNode(self.likesCommentsView);
}, completion: {
context.completeTransition($0)
})
} else {
var frame = context.finalFrameForNode(self.likesCommentsView)
likesCommentsView.alpha = 1
likesCommentsView.frame = frame
frame.size.height = 0
UIView.animateWithDuration(0.4, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.5, options: .BeginFromCurrentState, animations: { () -> Void in
self.likesCommentsView.alpha = 0
self.likesCommentsView.frame = frame;
}, completion: {
context.completeTransition($0)
})
}
}
CellNode.swift:
import AsyncDisplayKit
class CellNode: ASCellNode {
let headerView = HeaderView()
let footerView = FooterView()
let statusImageView = ASNetworkImageNode()
init(post: Post) {
super.init()
addSubnode(headerView)
addSubnode(statusImageView)
addSubnode(footerView)
}
override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
let stack = ASStackLayoutSpec.verticalStackLayoutSpec()
stack.justifyContent = .Start
stack.alignItems = .Stretch
var children: [ASLayoutable] = [headerView]
let image = ASRatioLayoutSpec(ratio: 9 / 16, child: statusImageView)
children.append(image)
children.append(footerView)
stack.setChildren(children)
return stack
}
}
@simoami: there are several different ways to animate in the X Likes text node. Do you want it to fade in? Do you want it to reveal with clips to bounds? What final look are you going for?
@hannahmbanana My goal, which I guess the illustration above doesn't explain well, is to expand the "x Likes" vertically with clips to bound. and have the entire cell expand at the same time to accommodate the new height of the entire vertical stack. Think of an accordion animation.
@hannahmbanana I switched to using ASDK 2 to overcome an issue and while everything works as before, Layout Transition API seems to be deprecated. Thoughts?
Hi, I have been experiencing this same issue; here is crux of my animation:
override func animateLayoutTransition(context: ASContextTransitioning) {
let final = context.finalFrameForNode(self.contentContainer)
let initial = context.initialFrameForNode(self.contentContainer)
if self.detailVisible == true {
self.contentContainer.frame = initial
self.someContent.alpha = 0
UIView.animateWithDuration(4, animations: {
self.contentContainer.frame = final
self.someContent.alpha = 1
self.setNeedsLayout()
}, completion: {
finished in
context.completeTransition(true)
})
} else {
self.contentContainer.frame = initial
self.someContent.alpha = 1
UIView.animateWithDuration(4, animations: {
self.contentContainer.frame = final
self.someContent.alpha = 0
self.setNeedsLayout()
}, completion: {
finished in
context.completeTransition(true)
})
}
}
I find that the cell node animates however at its own speed (Note: I have set the duration to 4s intentionally).
@simoami did the animations at the very least work as expected?
@renegare The animations are not in sync. The content below the cell animates but the cell itself doesn't animate. it just jumps to the new height.
@hannahmbanana Here's an illustration of the current behavior and desired effect (taken from another app):
Current:

Desired:

@simoami correct. There seems to be no idiomatic way to do this.
I had to go back to using Auto Layout constraints (albeit using SnapKit) :( but still very much interested in seeing what the problem (or better the solution) to this is.
@simoami @renegare Hey, would it be possible to extract some code out of your projects to let us dive into that issue a bit more?
@simoami It seems like in your first example you call setNeedsLayout() in the completion handler and this is the reason you see a snapping of the cell. You have to animate the cell node itself too ... but as said would be awesome to have an example project to play with.
Going to close this out since it's community debugging and hasn't been commented on in a while.
Most helpful comment
@renegare The animations are not in sync. The content below the cell animates but the cell itself doesn't animate. it just jumps to the new height.
@hannahmbanana Here's an illustration of the current behavior and desired effect (taken from another app):
Current:

Desired:
