在 SwiftUI 中,如何以高性能绘制阴影?

Jak*_* To 7 shadow swift swiftui

我使用.shadow(color:, radius:, x:, y:)来在我的应用程序中绘制阴影。这是我所知道的在 SwiftUI 中绘制应用程序的唯一方法。我使用该.sheet(isPresented:, content:)方法弹出一个视图,其中包含很多阴影,在调试视图层次结构时,我看到了这些警告:

在此处输入图片说明

但是我不知道如何shadowPath在SwiftUI中设置或将阴影预渲染为图像并将其放在图层下,请帮助我。

小智 14

这个警告不是因为你的代码本质上是不好的,而是为了告诉你有更多的渲染阴影的高效方法。

由于您的 UI 元素当前正在编写,SwiftUI 正在根据视图对象当时的位置和边界动态(在运行时)在视图对象周围绘制阴影,并且该渲染将在视图的整个生命周期中跟随视图。

这是一个数学密集型过程,在最好的情况下涉及对 GPU 的许多绘图调用,在最坏的情况下也涉及 CPU 瓶颈。

在 Swift 中有几种不同的渲染阴影的方法。他们中的大多数使用 SwiftUI 之外的框架(UIKit 和 CoreGraphics,通常,尽管 Metal、Core Animation 和 Core Image 在各种应用程序中都很重要。)

如果您没有在目标硬件上的 UI 层中看到性能问题,则此警告可能不是什么大问题,但是如果您非常有动力解决问题,则有一些选择:


选项1

如果您只想让错误消失,最简单的方法是通过添加强制 GPU 调用光栅化视图 + 阴影

.drawingGroup()
Run Code Online (Sandbox Code Playgroud)

在 .shadow 视图之后的某个地方。请注意,与动态阴影相比,这可能看起来很糟糕。如果您熟悉 UIKit,这类似于 UIView 上的 layer.shouldRasterize 属性。


选项 2

说到 UIKit,另一种方法是使用 SwiftUI 绘图逻辑的 UIViewRepresentable 或完全独立的 UIView。无论哪种方式:

myView = UIView()
myView.layer.shadowPath = UIBezierPath(rect: myView.bounds.cgPath)
Run Code Online (Sandbox Code Playgroud)

应该让你开始......其他阴影属性和东西会有所帮助。


选项 3

您可以以编程方式(硬)或在成像编辑应用程序中(烦人)将阴影渲染为图像,并加载为 Z 索引低于您的视图的图像,并缩放它们以产生深度错觉。

这是游戏开发人员在拥有蹩脚硬件但仍希望事情看起来不错时所做的那种hacky 工作。


最后......对于大多数 SwiftUI 视图,此警告可能可以忽略。如果您在 Instruments 中加载代码,您可能会看到 View 下阴影的动态渲染可能不会显着影响您的视图渲染性能。此警告通常仅在 UI 调试会话中可见。

希望这有助于您走上解决方案的道路。


jas*_*ori 7

这周我遇到了这个。我想我找到了一种与 UIKit 中的 ShadowPath 选项一样高性能的方法。我认为当你使用Shape().shadow(...)它时,它会根据路径绘制有效的阴影。因此,如果您知道要为其添加阴影的物体的形状,则可以这样做:

content
  // Use background so the shadow is the same size as the content
  .background(
    // I'm assuming rectangle but it can be anything like with rounded corners too
    Rectangle()
      // Add efficient shadow
      .shadow()
      // Add an inset so you don't see the ugly inner edge of the shadow, it will be under your content
      .padding(1)
  )
Run Code Online (Sandbox Code Playgroud)

这比我之前的速度快多了!