如何为 SwiftUI 设计高效的视图模型?

问题描述 投票:0回答:1

我有以下视图模型:

class ViewModel: ObservableObject {
    @Published var brightnessValue: Double = 0.0
    @Published var saturationValue: Double = 0.0
    @Published var contrastValue:   Double = 0.0
}

在 UI 层,我有 3 个滑块,实现为 SwiftUI 视图,每个都绑定到来自上述视图模型的

Double
。每当滑块更改属性时,所有 3 个滑块都会重新绘制。这是因为对
Published
中的
ObservableObject
属性的更改会重绘所有引用它的视图。

这就是 SwiftUI 视图的样子:

struct RootView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
            AdjustmentSlider(name: "brightness", sliderValue: $viewModel.brightnessValue)
            AdjustmentSlider(name: "saturation", sliderValue: $viewModel.saturationValue)
            AdjustmentSlider(name: "contrast",   sliderValue: $viewModel.contrastValue)            
        }
        .background(.random)
    }
}

struct AdjustmentSlider: View {
    let name: String
    @Binding var sliderValue: Double

    var body: some View {
        Slider(value: $sliderValue)
            .background(.randomColor) // I use this to visualize the redrawing.
    }
}

最后,这是整体(示例)应用程序设计:

这将是一个非常复杂的应用程序的一部分,有几十个滑块,所以我担心随着视图模型变得更加复杂,这种不断的重绘可能会成为一个重要的性能瓶颈。

有什么方法可以为 SwiftUI 设计更 高效 视图模型(即更少的重绘)?而且,更广泛地说,在使用大约 iOS 16 的 SwiftUI 时,这甚至是一个有效的问题吗?

swift model-view-controller swiftui viewmodel
1个回答
1
投票

在 SwiftUI 中,

View
结构是一个视图模型并保存视图数据。所以使用一个对象代替它已经是低效的并且会导致一致性错误。我们应该使用
let
来表示不变的数据,
@State
来表示确实发生变化的数据,以及
@State var
结构来将相关的变量组合在一起,您可以使用
mutating func
来表示您想要的任何逻辑独立测试。使用 computed var 在将数据传递到
body
中的子视图初始化时转换数据,例如
MySubView(myComputedVar)
,这就是
View
结构层次结构的要点——在向下的过程中从丰富的模型类型转换为简单类型。在你的情况下应该是这样的:

struct MyColor {
    var brightnessValue: Double = 0.0
    var saturationValue: Double = 0.0
    var contrastValue:   Double = 0.0
}

struct RootView: View {
    @State var color = MyColor()

    var body: some View {
        VStack {
            AdjustmentSlider(name: "brightness", sliderValue: $color.brightnessValue)
            AdjustmentSlider(name: "saturation", sliderValue: $color.saturationValue)
            AdjustmentSlider(name: "contrast",   sliderValue: $color.contrastValue)            
        }
        .background(.random)
    }
}

View 结构不“绘制”任何东西。这些是超快速的轻量级值类型,如

int x = 3
。生成
View
结构基本上可以忽略不计,因为它存储在内存堆栈而不是堆上。 SwiftUI 重新计算
View
层次结构的部分,它检测到数据依赖性更改(它记录视图调用
@State
getter 的内容),它与上次比较层次结构,并使用其结果来添加/删除/更新
 UIViewController
UIView
对象自动为我们服务。所以基本上我们不需要担心 View init 和 body 经常被调用,但是我们需要有效地使用像
@State
@Binding
这样的值类型并且尽量不要使用对象并且永远不要在 View 结构中初始化对象,由于该结构不断地被重新创建,它不能挂在一个对象上——因此这样做本质上是一个内存泄漏,并且会不断地做无意义的堆分配来减慢它的速度。

为了使 SwiftUI 更高效,只需将所有内容分解为尽可能小的视图,并且

body
仅使用在该视图中定义的 lets/vars。这被称为具有“更严格范围内的失效”(SwiftUI WWDC 2020 中的数据要点 @ 12:21

© www.soinside.com 2019 - 2024. All rights reserved.