How does react-native-reanimated handle layout updates? - react-native

When we use react-native-reanimated to animate properties like translateX, scale or opacity, react-native-reanimated runs the animation on the "native" UI thread. These properties don't affect the layout engine, so we can complete the layout on the Main Thread (JS), pass the values over the bridge to the UI thread, and then animate the stuff without involvement of the layout engine.
However, when properties like height, padding or margin are animated, this is not so clear. These properties affect the layout, and we need to update the layout when the properties change. But the layout is done by the Main Thread in Javascript. If the layout engine had to be active at every frame, because the height or the padding has changed, it would surely hit performance hard.
And indeed, I'm currently having massive performance problems when animating padding. The profiler tells me that the performance problems come from the bridge. It seems that the UI thread calls back to Javascript every frame, maybe to get the layout updated by the Main Thread, and then the layout gets passed over the bridge again to native.
However, react-native-reanimated offers the example widthandheight/index.js, which clearly animates properties like width, height and fontSize that have an effect on the layout. This example runs smoothly at 60fps.
How does react-native-reanimated handle animations which have an effect on the layout? Is there a layout process at the start and at the end of the animation, and in between it's only UI thread (this sounds like the experimental Transitions feature, but I'm not talking about Transitions). Is there some special recipe to be followed in order to get good performance?
EDIT: In the meantime, I have learned that some of these assumptions are false. The React Native layout is not done on the Javascript thread, but on the native UI thread. So, in principle, there should not be calls over the bridge when animating padding, width and margin. My performance problems maybe stem from the fact that my layout is too complex to be updated at 60 fps.

Related

Conversion of programmatic UIView subclass to Nib-based UIView shows severe performance degradation

I'm building an App, that shows a lot (100-500) of small views in some scrollpanel.
These views have several images, text labels, etc.
Now I wanted to have more declarative and intuitive designer of the views and switched to UI Builder (xib/nib).
But now I experience 4x-10x slower performance on opening the scene.
I already recognized, that if I e.g. disable Auto Layout option for the UIView, loading is accelerated at least twice. But still far slower, than what I did before programmatically.
I can just guess, that using Auto Layout with constraints is making everything to be calculated on each instantiation.
Does anyone have a tip, how to identify a bottleneck(s)?

Seeking explanation for the difference in animation performance between iOS6 and iOS7

I have been working on an iPad app that performs animations on very large images (full screen images that can be zoomed at 2x and still be retina quality). I have spent a lot of time getting smooth transitions when zooming and panning. When running the app on iOS7 however, the animations become really jerky (slow frame rate).
Further testing shows that it is the zoom animation that causes the problem (panning does not cause a problem). Interestingly, I have been able to fix it by setting the alpha of the image being scaled to 0.995 (instead of 1.0).
I have two questions
What has changed in iOS7 to make this happen?
Why does changing the opacity of the view make a difference?
Further information for the above questions:
Animation Setup
The animations are all pre-defined and are played upon user interaction. The animations are all a mix of pan and zoom. The animations are really simple:
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.frame = nextFrame;
//...
} completion:^(BOOL finished) {
//...
}];
To fix the jerky animation, I set the alpha before the animation
self.alpha = 0.99;
Some interesting points:
Setting the alpha inside of the animation works as well
Setting the alpha back to 1.0 after the animation and then doing the reverse animation with a 1.0 alpha does not give a smooth reverse animation.
Opacity fix
I have previously used the opacity fix to make animations smooth when scaling and panning multiple images. For example, I had two large images panning and scaling at different speeds with one on top of the other. When a previously un-rendered part of the lower image (the image on the bottom) became visible, the animation would become jerky (panning as well as scaling). My thought for why alpha helps in this case is, if the top image has a bit of transparency, the bottom image must always be rendered, which means it can be cached before the animation takes place. This thought is backed by doing the reverse animation and not seeing the jerky animation. (I guess I would be interested to know if anyone has different thoughts on this as well).
Having said the above, I don't know how this would have an affect when there is just one image (as in the situation I am describing in my question). Particularly when after getting the jerky animation, the reverse animation is still jerky. Another point of difference between the two situations is that it is only scaling that causes the problem in the current issue, while in the double image issue it was panning as well as scaling.
I hope the above is clear - any insights appreciated.
Look at Group Opacity. iOS 7 has that turned ON by default and this changes the way views/layers are composited:
When the UIViewGroupOpacity key is not present, the default value is
now YES. The default was previously NO.
This means that subviews of a transparent view will first be
composited onto that transparent view, then the precomposited subtree
will be drawn as a whole onto the background. A NO setting results in
less expensive, but also less accurate, compositing: each view in the
transparent subtree is composited onto what’s underneath it, according
to the parent’s opacity, in the normal painter’s algorithm order.
(source: iOS7 Release Notes)
With this setting on, compositing - also during animations - is way more expensive.
Also, have a look at the CoreGraphics Instruments tool to check if you have lots of off-screen images compositing going on.
Are you having any sort of changes going on in the view being animated? That would trigger more discarding of the rendered layer image from the backing store.

Implement Fan View - Cocoa

I am trying to implement a Fan view like the ones in the Mac OS X dock, such as the Downloads and Documents folders, using Cocoa.
I am currently adding a button on a transparent window's content view and animating the button's frame using NSViewAnimation (group animation). But the animation is not as smooth as expected.
Is there any other optimized way for implementing this?
You should be using Core Animation for this. You should create a transparent view/window that's large enough to contain your whole animation. You should then use CALayer objects to perform the actual animation.
Core Animation layers are essentially high-level lightweight wrappers around OpenGL surfaces and the rendering of the layers is done by the GPU, giving much better performance than CPU-managed animation such as NSViewAnimation.
Bear in mind that because Core Animation layers are lightweight, they don't have any event-handling built into them, so you'll need to do all the mouse tracking in your view/view controller.
Your other option is using layer-backed views (which have their own CALayer) and animating the buttons' positions using the animator proxy. This may be enough to achieve what you want, and because the buttons are still full NSButton objects they still have all the NSView event handling.
You should probably read the Animation Overview to give you a better idea of how all these technologies work.

How to manage memory in scolling components?

Are there known and proven ways to manage memory with scrolling components like tables or grids other than recycling cells as is used in Cocoa? The sequence of calculations and datasource/delegation calls needed to make this way of laying out views works but also makes coordinating complex animations with the cells and a scroll view error prone as you have to pay careful attention to the sequence of calls as it reloads data, scrolls to an offset and other mechanisms of the layout that affect the target frame of your animations. I am looking for a more declarative approach to providing content to the scroll view and having it figure out a smart way to manage it's memory as is done by a browser when you load the DOM with a long vertical layout of pictures.
I found it easier to create my own custom layout classes that only do layout on my views and not to impose an elaborate protocol such as NSTableViewDataSource and the like that makes animation difficult to program. I like to know exactly where my views are at all times, the complete hierarchy of each view and I don't like to keep a model in sync with my views so I store data on the views themselves. In my mind the objects on screen are the one and only objects I like to orchestrate as a programmer. I want direct declarative control over them kind of like a game programer. By subclassing a scroll view directly and following very simple layout rules outside of the normal layoutSubviews methods of Cocoa to avoid surprise layouts, I was able to control my animations better and do more complex and fluid animations. Hope this inspires someone to do the same.

maskToBounds:YES affecting scroll performance

I have several UIButtons on a UIScrollView. I want the buttons to have rounded corners, so I call maskToBounds: on each of them. When I do this and run on the device, the scrolling framerate is pretty bad (it works fine on the simulator). Any ideas on a workaround for this problem?
You're causing the view to be composited offscreen with that call to masksToBounds:, which slows things down quite a bit. Are you rendering custom button images? If so use UIImage -stretchableImageWithLeftCapWidth:topCapHeight: with an image which is the minimum width to encompass it's rounded edges. This allows the GPU to handle stretching the image in the most efficient way possible, while still giving you a button made out of an image. There is a session in the WWDC 2011 videos on Drawing in UIKit - watch that, as it addresses exactly this problem, and a few others you're likely to have.
A few alternative methods:
Tweeties implementation of fast scrolling, by drawing everything manually
Matt Gallaghers implementation of custom drawing. This is the method I use, as it's easy to maintain