如何在SwiftUI中使用CustomType进行绑定,使视图停止不必要的渲染



我有一个名为AppDataCustomType,它看起来像这样:


struct AppData {
var stringOfText: String
var colorOfText: Color
}

我在视图中使用此AppData作为State或Binding,我的项目中有2视图,名为:ContentView,另一个视图名为BindingViewindingView中,我只是使用AppData的颜色信息。我希望我的BindingView呈现或对颜色信息的响应发生更改!事实上,BindingView甚至为stringOfText呈现自己,这是完全不必要的,因为视图中没有使用该数据。我认为,也许BindingView不仅考虑colorOfText,还考虑所有包含此数据的包,即appData。因此,我决定帮助绑定视图了解它何时应该呈现自己,并使视图Equatable,但这甚至于事无补。仍然BindingViewstringOfText的更改时刷新和渲染自己,这是在浪费渲染时间。在使用CustomType作为State或Binding的类型时,如何解决不必要的渲染问题。


struct ContentView: View {
@State private var appData: AppData = AppData(stringOfText: "Hello, world!", colorOfText: Color.purple)
var body: some View {
print("rendering ContentView")
return VStack(spacing: 20) {
Spacer()
EquatableView(content: BindingView(appData: $appData))
//BindingView(appData: $appData).equatable()
Spacer()
Button("update stringOfText from ContentView") { appData.stringOfText += " updated"}
Button("update colorOfText from ContentView") { appData.colorOfText = Color.red }
Spacer()
}
}
}

struct BindingView: View, Equatable {
@Binding var appData: AppData
var body: some View {
print("rendering BindingView")
return Text("123")
.bold()
.foregroundColor(appData.colorOfText)
}
static func == (lhs: BindingView, rhs: BindingView) -> Bool {
print("Equatable function used!")
return lhs.appData.colorOfText == rhs.appData.colorOfText
}
}

Views上使用Equatable(以及.equatable()EquatableView()(时,SwiftUI会决定何时应用我们自己的==函数,以及何时单独比较参数。请在此处查看我的另一个答案,了解更多详细信息:https://stackoverflow.com/a/66617961/560942

在这种情况下,似乎即使声明了Equatable,SwiftUI也会跳过它,因为它必须决定Binding中的POD(纯旧数据(被确定为不相等,因此它将刷新视图(再次,尽管人们会认为==足以迫使它不刷新(。

在您给出的示例中,很明显,系统重新渲染Text元素是微不足道的,因此,是否发生这种重新渲染并不重要。但是,即使重新渲染确实会产生后果,您也可以将不更改的部分封装到一个单独的子视图中:

struct BindingView: View {
@Binding var appData: AppData
var body: some View {
print("rendering BindingView")
return BindingChildView(color: appData.colorOfText)
}
//no point in declaring == since it won't get called (at least with the current parameters
}
struct BindingChildView : View {
var color: Color

var body: some View {
print("rendering BindingChildView")
return Text("123")
.bold()
.foregroundColor(color)
}
}

在上面的代码中,尽管BindingView每次都会被重新渲染(尽管基本上是零成本的,因为什么都不会改变(,但新的子视图会被跳过,因为它的参数是相等的(即使没有声明equatable(。因此,在一个不做作的例子中,如果子视图的渲染成本很高,这将解决问题。

最新更新