Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In SwiftUI, how to draw shadows with high performance?

I use the .shadow(color:, radius:, x:, y:) to draw shadows in my application. This is the only way I know of drawing apps in SwiftUI. I use the .sheet(isPresented:, content:) method to pop up a view, which contains a lot of shadows, and when I debug view hierarchy, I saw these warnings:

enter image description here

But I don't know how to setting shadowPath, or pre-rendering the shadow into an image and putting it under the layer in SwiftUI, please help me.

like image 313
Kinnouka Bokudo Avatar asked Sep 26 '20 12:09

Kinnouka Bokudo


1 Answers

This warning is not caused because your code is inherently bad, but as a way of telling you that there are much more performant ways of rendering shadows.

As your UI elements are currently written, SwiftUI is drawing the shadows around your view objects dynamically (at Runtime) based on wherever their positions and bounds are at the time, and that rendering will follow the view throughout it's lifecycle.

It's a math intensive process and involves many draw-calls to the GPU in the best of cases, and CPU bottlenecking as well in the worst of cases.

There are several different ways of rendering shadows in Swift. Most of them utilize frameworks OUTSIDE of SwiftUI (UIKit and CoreGraphics, usually, though Metal, Core Animation, and Core Image have been important in various applications.)

This warning is probably not a big deal if you're not seeing performance problems in the UI layer on target hardware, but if you're very motivated to solve the problem, there are some options:


Option 1

The absolute easiest thing to do if you just want to make the error go away would be to force a GPU call rasterization for the view + shadow by adding

.drawingGroup()

somewhere after the .shadow view. Be advised, this will likely look like crap compared to dynamic shadows. If you're familiar with UIKit, this is similar to the layer.shouldRasterize property on UIView.


Option 2

Speaking of UIKit, an alternative would be to head over there and use either a UIViewRepresentable of your SwiftUI drawing logic, or a completely separate UIView. Either way:

myView = UIView()
myView.layer.shadowPath = UIBezierPath(rect: myView.bounds.cgPath)

should get you started... the other shadow properties and stuff will help.


Option 3

You could render the shadow as an image, either programatically (hard) or in an imaging editing application (annoying) and load is as an image at a lower Z index than your view, and scale them to give the illusion of depth.

This is the kind of hacky work around that game developers used to do when they had crappy hardware but still wanted things to look good.


In the end... for MOST SwiftUI views this warning likely can be ignored. If you load the code in Instruments, you'll likely see that the dynamic rendering of drop shadows under a View is probably not impacting your view rendering performance significantly. This warning is only usually visible inside a UI Debug session.

Hope this helps set you on the path to a solution.

like image 156
Alex Moore Avatar answered Nov 27 '22 16:11

Alex Moore