代码之家  ›  专栏  ›  技术社区  ›  LittleExp

SwiftUI:为什么当@Observable数据的相关属性发生变化时,SwiftUI会重建视图

  •  1
  • LittleExp  · 技术社区  · 11 月前

    我是SwiftUI的新手。如果这是一个愚蠢的问题,我很抱歉。

    @Observable
    class Book {
        var title = "A Sample Book"
        var pageOne = Page()
    }
    
    class Page {
        var pageTitle = "pageTitle"
        var isAvailable = true
    }
    
    struct ContentView: View {
        @State private var book = Book()
        
        var body: some View {
            VStack{
                ChangeView(book: book)
                DisplayView(book: book)
            }
        }
    }
    
    struct ChangeView: View{
        @Bindable var book: Book
        
        var body: some View{
            Toggle(book.pageOne.isAvailable ? "page available" : "page not available",
                   isOn: $book.pageOne.isAvailable
            )
        }
    }
    
    struct DisplayView: View {
        var book: Book
    
        var body: some View {
            Text(book.pageOne.pageTitle)
        }
    }
    

    这个 ChangeView 正在阅读和观看 book.pageOne.isAvailable 。每次我单击切换时 更改视图 的身体将被重建。这是有道理的。 但我发现 DisplayView 每次我单击切换时,即使它不关心 isAvailable 完全。所以我的问题是为什么要重建SwiftUI 显示视图 当一个变量不关心变化时?这似乎与文件中所说的不同。是因为 isAvaialble 是可观测数据的一个子属性吗?

    1 回复  |  直到 11 月前
        1
  •  2
  •   Sweeper    11 月前

    首先,你应该 Page @Observable 。否则,SwiftUI无法正确跟踪其更改。

    @Observable
    class Page {
        var pageTitle = "pageTitle"
        var isAvailable = true
    }
    

    然而,仅凭这一点不足以解决问题。要解决此问题,您可以制作 Bindable 第页 (在这种情况下 book 无需如此 @Bindable ),

    @Bindable var page = book.pageOne
    Toggle(book.pageOne.isAvailable ? "page available" : "page not available",
           isOn: $page.isAvailable
    )
    

    或执行以下操作:

    Toggle(book.pageOne.isAvailable ? "page available" : "page not available",
           isOn: $book[dynamicMember: \.pageOne.isAvailable]
    )
    

    说明:

    当你这样做的时候 $book.pageOne.isAvailable ,它被降低(即等效)为:

    $book[dynamicMember: \.pageOne][dynamicMember: \.isAvailable]
    

    第一对 [] 是一个电话 Bindable.subscript 。这创建了一个 Binding<Page> 第二对 [] 是一个电话 Binding.subscript ,创建a Bindable<Bool> .

    如果你不知道这是怎么回事,看看 SE-0195 SE-0252 .

    问题是这个中间体 装订<页面> 当决赛 Binding<Bool> 通过切换、设置 isAvailable 显然,它被称为,但根据我的观察,它是 pageOne 打电话。

    As 第页 是引用类型,此setter调用是不必要的。也许这是因为 装订<Bool> 装订<页面> . Binding s最初设计用于处理结构体,如果 第页 如果是一个结构体,那么调用setter是绝对必要的 第一页 .

    无论如何,因为setter 第一页 当被调用时 is可用 SwiftUI认为 第一页 也发生了变化。自从 DisplayView.body 呼叫 吸气剂 属于 第一页 ,SwiftUI将调用 DisplayView.body 再次当 is可用 变化。

    在这两种解决方案中,我都消除了中间体 装订<页面> ,所以设置 第一页 不会被呼叫。