[英]How can I make my View stop unnecessary rendering with using CustomType for Binding in SwiftUI?
I have a CustomType called AppData , and it look like this:我有一个名为AppData的CustomType ,它看起来像这样:
struct AppData {
var stringOfText: String
var colorOfText: Color
}
I am using this AppData in my Views as State or Binding, I have 2 Views in my project called: ContentView and another one called BindingView .我在我的视图中使用这个AppData作为 State 或 Binding,我的项目中有2 个视图称为: ContentView和另一个称为BindingView 。 In my BindingView I am just using Color information of AppData .
在我的BindingView中,我只是使用AppData的颜色信息。 And I am expecting that my BindingView render or response for Color information changes!
我期待我的BindingView呈现或响应颜色信息的变化! How ever in the fact BindingView render itself even for
stringOfText
Which is totally unnecessary, because that data is not used in View.实际上BindingView甚至会为
stringOfText
呈现自己,这是完全没有必要的,因为 View 中没有使用该数据。 I thought that maybe BindingView not just considering for colorOfText
but also for all package that cary this data and that is appData So I decided help BindingView to understand when it should render itself, and I made that View Equatable , But that does not helped even.我认为也许BindingView不仅考虑
colorOfText
,而且还考虑所有携带这些数据的 package ,即 appData 所以我决定帮助BindingView了解它应该何时呈现自己,并且我制作了 View Equatable ,但这甚至没有帮助。 Still BindingView refresh and render itself on changes of stringOfText
which it is wasting of rendering.仍然BindingView在浪费渲染的
stringOfText
更改时刷新和渲染自身。 How can I solve this issue of unnecessary rendering while using CustomType as type for my State or Binding.在将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
}
}
When using Equatable
(and .equatable()
, EquatableView()
) on Views
, SwiftUI makes some decisions about when to apply our own ==
functions and when it is going to compare the parameters on its own.在
Views
上使用Equatable
(和.equatable()
、 EquatableView()
)时,SwiftUI 会做出一些决定,决定何时应用我们自己的==
函数以及何时自行比较参数。 See another one of my answers with more details about this here: https://stackoverflow.com/a/66617961/560942在此处查看我的另一个答案以及更多详细信息: https://stackoverflow.com/a/66617961/560942
In this case, it appears that even if Equatable
is declared, SwiftUI skips it because it must be deciding that the POD
(plain old data) in the Binding
is determined to be non-equal and therefore it's going to refresh the view (again, even though one would think that the ==
would be enough to force it not to).在这种情况下,似乎即使声明了
Equatable
, SwiftUI 也会跳过它,因为它必须确定Binding
中的POD
(普通旧数据)被确定为不相等,因此它将刷新视图(再次,即使有人会认为==
足以迫使它不要这样做)。
In the example you gave, obviously it's trivial for the system to re-render the Text
element, so it doesn't really matter if this re-render happened.在您给出的示例中,系统重新渲染
Text
元素显然是微不足道的,因此重新渲染是否发生并不重要。 But, in the even that there actually are consequences to re-rendering, you could encapsulate the non-changing parts into a separate child view:但是,即使重新渲染确实会产生后果,您也可以将不变的部分封装到单独的子视图中:
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)
}
}
In the above code, although the BindingView
is re-rendered each time (although at basically zero cost, because nothing will change), the new child view is skipped because its parameters are equatable (even without declaring Equatable).在上面的代码中,虽然每次都重新渲染
BindingView
(虽然成本基本为零,因为什么都不会改变),但是新的子视图被跳过了,因为它的参数是 equatable 的(即使没有声明 Equatable)。 So, in a non-contrived example, if the child view were expensive to render, this would solve the issue.因此,在一个非人为的示例中,如果子视图的渲染成本很高,这将解决问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.