您的位置 首页 > 腾讯云社区

SwiftUI:启用高性能 Metal 渲染---韦弦zhy

SwiftUI默认使用Core Animation进行渲染,提供了出色的开箱即用性能。但是,对于复杂的渲染,您可能会发现代码开始变慢——任何低于每秒60帧(FPS)的问题都是一个问题,但实际上您应该将目标更高,因为许多iOS设备现在以120fps的速度渲染。

为了说明这一点,让我们看一些示例代码。我们将创建一个颜色循环视图,以各种颜色呈现同心圆。结果看起来像是一个径向渐变,但我们将添加两个属性以使其更具可定制性:一个用于控制应绘制多少个圆,另一个用于控制颜色循环——它将能够移动渐变周围的开始和结束颜色。

我们可以通过使用Color(hue:saturation:brightness:)初始值设定项来获得颜色循环效果:hue 是从0到1的值,控制着我们所看到的颜色的种类——红色是0到1,所有其他色相都介于两者之间。为了弄清楚特定圆圈的色相,我们可以取圆圈数(例如25),除以圆圈数(例如100),然后加上颜色循环量(例如0.5)。因此,如果我们是100的第25圈,循环量为0.5,则我们的色相将为0.75。

这里的一个小复杂性是,色相在达到1.0后不会自动换行,这意味着1.0的色相等于0.0的色相,但是1.2的色相不等于0.2的色相。因此,我们将手工包裹色相:如果色相超过1.0,我们将减去1.0,以确保它始终位于0.0到1.0的范围内。

这是代码:

struct ColorCyclingCircle: View { var amount = 0.0 var steps = 100 var body: some View { ZStack { ForEach(0..<steps) { value in Circle() .inset(by: CGFloat(value)) .strokeBorder(self.color(for: value, brightness: 1), lineWidth: 2) } } } func color(for value: Int, brightness: Double) -> Color { var targetHue = Double(value) / Double(self.steps) + self.amount if targetHue > 1 { targetHue -= 1 } return Color(hue: targetHue, saturation: 1, brightness: brightness) } }

现在,我们可以在布局中使用它,将其颜色循环绑定到由滑块控制的局部属性:

struct ContentView: View { @State private var colorCycle = 0.0 var body: some View { VStack { ColorCyclingCircle(amount: self.colorCycle) .frame(width: 300, height: 300) Slider(value: $colorCycle) } } }

如果您运行该应用程序,将会看到我们有一个整齐的彩色波浪效果,完全可以通过在滑块上拖动来控制,并且效果非常流畅。

简单视图.gif

您现在所看到的是由Core Animation驱动的,这意味着它将把我们的100个圆变成在屏幕上绘制的100个独立视图。这在计算上是昂贵的,但是如您所见,它运行良好——我们获得了平稳的性能。

但是,如果稍微增加复杂度,我们会发现事情并不是那么乐观。用以下一个替换现有的strokeBorder()修饰符:

.strokeBorder(LinearGradient(gradient: Gradient(colors: [ self.color(for: value, brightness: 1), self.color(for: value, brightness: 0.5) ]), startPoint: .top, endPoint: .bottom), lineWidth: 2)

现在,这将呈现一个柔和的渐变,在圆的顶部显示明亮的颜色,在底部显示较暗的颜色。现在,当您运行该应用程序时,您会发现它运行起来要慢得多——SwiftUI正在努力为100个单独视图渲染100个渐变。

卡顿.gif

我们可以通过应用一个称为drawingGroup()的新修改器来解决此问题。这告诉SwiftUI,在将视图内容作为单个呈现的输出放回到屏幕上之前,应将视图的内容呈现到屏幕外的图像中(离屏渲染),这要快得多。在幕后,该功能由Metal提供支持,Metal是Apple的框架,可直接与GPU协同工作以实现极快的图形。

因此,将ColorCyclingCircle主体修改为此:

var body: some View { ZStack { // existing code… } .drawingGroup() }

现在,再次运行它——仅需添加一点点,就可以正确渲染所有内容,即使使用渐变色,我们也可以全速返回。

Metal 渲染.gif

重要提示:drawingGroup()修饰符有助于了解和保留您的武器库,这是解决性能问题的一种方法,但是您不应该经常使用它。添加屏幕外渲染过程可能会降低SwiftUI进行简单绘图的速度,因此,在尝试引入drawingGroup()之前,应等到遇到实际性能问题后再进行操作。

译自 Enabling high-performance Metal rendering with drawingGroup()

---来自腾讯云社区的---韦弦zhy

关于作者: 瞎采新闻

这里可以显示个人介绍!这里可以显示个人介绍!

热门文章

留言与评论(共有 0 条评论)
   
验证码: