简体   繁体   中英

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:


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 . In my BindingView I am just using Color information of AppData . And I am expecting that my BindingView render or response for Color information changes! How ever in the fact BindingView render itself even for stringOfText Which is totally unnecessary, because that data is not used in 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. Still BindingView refresh and render itself on changes of stringOfText which it is wasting of rendering. How can I solve this issue of unnecessary rendering while using CustomType as type for my State or 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. See another one of my answers with more details about this here: 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).

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. 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). So, in a non-contrived example, if the child view were expensive to render, this would solve the issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM