简体   繁体   English

当视图可以在 SwiftUI 中使用时,为什么我不能在我的视图中使用 Equatable function?

[英]Why I can not use Equatable function in my View when the View can use it in SwiftUI?

I am trying use EquatableView for my View but SwiftUI does not use my == function, it seems I done everything right, but does not work, need help.我正在尝试将 EquatableView 用于我的视图,但 SwiftUI 不使用我的 == function,看来我做的一切都正确,但不起作用,需要帮助。 I want stop SwiftUI from unnecessary rendering also using my == function, but it renders every time and does not use the given function, why?我想停止 SwiftUI 进行不必要的渲染,也使用我的 == function,但它每次都渲染并且不使用给定的 function,为什么?

import SwiftUI

extension Int { var isEven: Bool { return self % 2 == 0 } }

struct ContentView: View {
    
    @State private var number = 3
    
    var body: some View {
        VStack {
            
            // EquatableView ( content: NumberView(number: number) )  // <<: Here
            // NumberView(number: number).equatable()   // <<: Or Here
            

            Button("New Random Number") {
                number = Int.random(in: 1...100)
            }
            
            Text(number.description).bold().padding()
        }
    }
}

struct NumberView: View, Equatable {
    
    let number: Int
    
    init(number: Int) {
        
        print("initializing")
        
        self.number = number
    }

    var body: some View {
        
        print("rendering")

        return VStack {
            
            if number.isEven {
                Text("EVEN").bold().foregroundColor(Color.green)
            } else {
                Text("ODD").bold().foregroundColor(Color.red)
            }
            
        }
        .padding()
  
    }

    static func == (lhs: NumberView, rhs: NumberView) -> Bool {
        
        print("Equatable used!")
        
        return lhs.number.isEven == rhs.number.isEven
    }
  
}

(Xcode 12.4, macOS 11.2.3) (Xcode 12.4、macOS 11.2.3)

The mechanics of equatable() are a little bit opaque. equatable()的机制有点不透明。 In fact, all the headers have is this (certainly not detailed) definition:事实上,所有标题都有这个(当然不是详细的)定义:

Prevents the view from updating its child view when its new value is the same as its old value.当新值与旧值相同时,防止视图更新其子视图。

For example, one would assume that with your original example, that your == would be called to evaluate if the views are equal.例如,人们会假设对于您的原始示例,您的==将被调用以评估视图是否相等。 But, clearly that doesn't happen.但是,显然这不会发生。

It can be forced to happen in a number of (sometimes suprising) ways.它可以以多种(有时令人惊讶的)方式被迫发生。 For example (this one is the un-surprising one), if you add an additional property on NumberView that is a @State property, your == is called each time:例如(这个并不令人惊讶),如果您在NumberView上添加一个附加属性,即@State属性,则您的==每次都会被调用:


struct NumberView: View, Equatable {
    
    var number: Int = 0
    @State var additionalProp: Int = 0
    
    var body: some View {
        print("rendering \(Date()): \(number)")
        return VStack {
            Text("\(number)")
            if number.isEven {
                Text("EVEN").bold().foregroundColor(Color.green)
            } else {
                Text("ODD").bold().foregroundColor(Color.red)
            }
        }
        .padding()
    }

    static func == (lhs: NumberView, rhs: NumberView) -> Bool {
        print("Equatable used!")
        return lhs.number.isEven == rhs.number.isEven
    }
}

which behaves differently than the following (in which == is not called):它的行为与以下不同(其中==未被调用):

var number: Int = 0
var additionalProp: Int = 0

From this, I think we can infer a couple of things:由此,我认为我们可以推断出以下几点:

  1. With a view without other properties, SwiftUI will do an equality check of the props and if they're not equal, it won't even both calling your equatable function在没有其他属性的情况下,SwiftUI 将对道具进行平等检查,如果它们不相等,它甚至不会同时调用您的equatable
  2. If there are @State properties, SwiftUI will call your equatable function.如果有@State 属性,SwiftUI调用您的等价 function。

Here's the more interesting case to me:这是对我来说更有趣的案例:

struct NumberView: View, Equatable {
    
    var number: Int = 0
    var additionalProp: String = ""
    
    var body: some View {
        print("rendering \(Date()): \(number)")
        return VStack {
            Text("\(number)")
            if number.isEven {
                Text("EVEN").bold().foregroundColor(Color.green)
            } else {
                Text("ODD").bold().foregroundColor(Color.red)
            }
        }
        .padding()
    }

    static func == (lhs: NumberView, rhs: NumberView) -> Bool {
        print("Equatable used!")
        return lhs.number.isEven == rhs.number.isEven
    }
}

In the above case, the equatable function will get used again, even though String is a value type (and Equatable) in Swift and not using @State .在上述情况下,即使String是 Swift 中的值类型(和 Equatable)并且不使用 @State ,也会再次使用可相等的@State I don't have a great explanation for that part.我对那部分没有很好的解释。


This page (https://swiftui-lab.com/equatableview/) has a quote from an Apple engineer about when `==` is used: 此页面 (https://swiftui-lab.com/equatableview/) 引用了一位 Apple 工程师关于何时使用 `==` 的引用:

SwiftUI assumes any Equatable.== is a true equality check, so for POD views it compares each field directly instead (via reflection). SwiftUI 假定任何 Equatable.== 是真正的相等检查,因此对于 POD 视图,它直接比较每个字段(通过反射)。 For non-POD views it prefers the view's == but falls back to its own field compare if no ==.对于非 POD 视图,它更喜欢视图的 ==,但如果没有 ==,则回退到其自己的字段比较。 EqView is a way to force the use of ==. EqView 是一种强制使用 == 的方法。 When it does the per-field comparison the same rules are applied recursively to each field (to choose direct comparison or == if defined).当它对每个字段进行比较时,相同的规则会递归地应用于每个字段(选择直接比较或 == 如果已定义)。 (POD = plain data, see Swift's _isPOD() function.) (POD = 纯数据,参见 Swift 的 _isPOD() function。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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