I have a problem with two ASViewControllers that I want to transition between using UIViewControllerAnimatedTransitioning. It's a context transition where an image view from the first view controller animates its position and size to an image node in the second view controller. The problem is that the second image node is contained in a cell node which is loaded by the vc's node (ASTableNode).
Before I start the transition, the second vc has to load its cells and lay them out so I know the target image node's frame. But this frame is always .zero (size and origin). I tried calling setNeedsLayout and layoutIfNeeded on the node's view, but that didn't help.
How can I force the view controller or its view to layout its node and the cells and get a callback once the layout is finished? Do I have to call visibility functions on the node?
This problem involves many parts of my app since I have animated transitions all over the place but to make them work with ASDK, the target frames need to be valid before I start the transitions.
I'm having exactly the same problem. I'm pushing a view controller onto the stack, and it needs to be completely laid out before the transition begins. Is there any way to force this to happen?
Still haven't found a proper solution :( I recalculate the frame now manually, which feels SO wrong. It also doesn't matter how long I wait until I start the transition because the layout really just starts when the node is added to the containerView, which is too late in my case. I tried adding it as a subnode to the mainWindow so the setNeedsLayout and layoutIfNeeded would have a container frame to calculate their layout for and then executed the transition in a DispatchQueue.main.async somehow hoping it would wait for the next layout pass to finish with valid frame, but all that didn't help
Sorry for this late response. This is indeed a bit tricky. You essentially have to trigger an initial data load (i.e reloadData) on the table/collection node, block main while the node is preparing its data in background and then force a layout pass on the table/collection to make sure it has an up-to-date layout. The code should look similar to this:
# Inside your "to" view controller, called by the VC transition
- (void)ensureDisplay
{
[self.view layoutIfNeeded] // `reloadData` will be called on tableNode as an initial data load, assuming it was already added to the view
[self.tableNode waitUntilAllUpdatesAreCommitted]; // Block main while the node prepares its data
[self.tableNode.view layoutIfNeeded]; // Make sure the table view's layout is updated
}
Let me know if it doesn't work for you!
Wow @nguyenhuy it works 馃帀. Just started a Hero transition and the frame is correct. 馃榿
But I really have to know HOW this can work, when I call this on a view controller that's not yet added to the container view/ window (I checked again, view.superview is nil by the time ensureDisplay is called. I'm curious, because if I don't have a fullscreen presentation it might be wrong to use the window size for the initial layout calculation.
@fruitcoder One interesting titbit is that when you access the view of a UIViewController for the first time, -[UIViewController loadViewIfRequired] is called which in turn calls -[UIViewController loadView]. After loadView, the view has a full screen size even though it's not yet attached to a window. Most of the time, this size is one your view will end up having, so you're fine.
That being said, you're right that if your view isn't supposed to be fullscreen, you possibly have to time your ensureDisplay call to make sure it occurs after the view has the expected size.
@nguyenhuy I have a related problem with a UINavigationController that doesn't use the whole screen. Before I push a controller onto the stack, I'm pulling out some views and adding them to the container view. Because loadView is giving the full screen size, the frames of those views are slightly off.
Forcing the "to" controller to layout doesn't seem to work in this case, because it needs to be added to the navigation stack to determine its layout. Is there a way to get the correct final frames for those views before the transition begins?
Seems there was a simple solution. Before using any views for my transition I could just do this:
toNode.view.frame = transitionContext.containerView.frame
toNode.view.layoutIfNeeded()
And it works fine 馃憤
Cool, thanks for sharing!
Most helpful comment
Sorry for this late response. This is indeed a bit tricky. You essentially have to trigger an initial data load (i.e
reloadData) on the table/collection node, block main while the node is preparing its data in background and then force a layout pass on the table/collection to make sure it has an up-to-date layout. The code should look similar to this:Let me know if it doesn't work for you!