【问题标题】:How can I make my View stop unnecessary rendering with using CustomType for Binding in SwiftUI?如何在 SwiftUI 中使用 CustomType 进行绑定,使我的视图停止不必要的渲染?
【发布时间】:2021-03-16 21:24:15
【问题描述】:

我有一个名为 AppData 的 CustomType,它看起来像这样:


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

我在我的视图中使用这个 AppData 作为状态或绑定,我的项目中有 2 个视图,名为:ContentView 和另一个称为 BindingView。在我的 BindingView 中,我只是使用 AppData 的颜色信息。我期待我的 BindingView 呈现或响应颜色信息的变化!实际上 BindingView 会为stringOfText 呈现自己,这是完全没有必要的,因为视图中不使用该数据。我认为可能 BindingView 不仅考虑 colorOfText 还考虑所有携带此数据的包,即 appData 所以我决定帮助 BindingView 了解它应该何时呈现本身,我制作了那个 View Equatable,但这甚至没有帮助。仍然 BindingView 刷新并在 stringOfText 的更改上呈现自己,这是浪费渲染。在使用 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
    }

}

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    当在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每次都重新渲染(虽然成本基本为零,因为什么都不会改变),但新的子视图被跳过了,因为它的参数是可等价的(即使没有声明可等价的)。因此,在一个非人为的示例中,如果子视图的渲染成本很高,这将解决问题。

    【讨论】:

    • 感谢您的回答,我很欣赏您的解决方法,但事实上您并没有使用 CustomType 作为绑定!如果我能以你的逻辑方式解决我的问题,为什么我什至应该首先将所有包发送到 BindingView?我可以很容易地让您立即在 BindingView 中植入,它只接受颜色绑定,例如:Binding。我提出这个问题的所有想法是使用 CustomType 而不将该 CustomType 转换为另一个。看起来所有迷人的 SwiftUI 在简单的逻辑上都有问题,就像我们面临的这个问题!
    • “为什么我应该首先将所有包发送到 BindingView?”好问题。在您最初的问题中,没有理由这样做。回答者必须对 可能 发生的事情做出假设,就像我对可能存在具有昂贵渲染的子视图的想法所做的那样。这就是这些假设性问题没有实际示例的问题。但是,我不知道您在说什么“将 CustomType 转换为另一个”——我没有进行任何转换。我也不认为这是 SwiftUI 存在“简单逻辑”问题的情况。
    • "将 CustomType 转换为另一个" 我想说您正在将 CustomType 转换为使用没有问题的类型,我的想法没有转换。
    • 我想我对这个问题的答案很困惑。
    • 我想我在这个网站上的假设是,由于我们都不是 SwiftUI 团队的工程师,所以我们正试图找到使用现有框架解决实际问题的答案,因为我们无法改变当今框架的基本工作方式。
    猜你喜欢
    • 1970-01-01
    • 2020-06-04
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多