我有以下可观察对象,用于跟踪自定义模式状态

import SwiftUI


@Observable class ModalObservable {
  // State
  var view = ModalViewType.Profile
  var open = false
  
  // Actions
  func show(view: ModalViewType) {
    self.open = true
    self.view = view
  }
  
  func hide() {
    self.open = false
  }
}

我不想将每个状态值作为单独的变量,而是将其设置为var state = ModalState(),其中ModalState将是一个包含每个单独值的 struct .

我找不到任何关于这会如何影响UI重新呈现逻辑的文档,主要是当此 struct 中的任何内容发生变化时还是仅当我的UI正在访问的嵌套值发生变化时才重新呈现我的UI?

例如,如果我的用户界面只使用ModalObservable.state.open,那么当view更改时,它会重新渲染吗?

推荐答案

首先,让我们弄清楚"重新渲染"是什么意思.如果您的意思是"创建一个全新的视图",那么只有在视图的identity更改时才会发生这种情况.如果您的视图标识不依赖于ModalObservable,那么您如何更改ModelObservable就无关紧要了.这可以通过实验检测到onAppear.

如果你的意思是"更新一个现有的视图",那么这取决于视图的dependencies.SwiftUI根据视图和视图使用的状态/绑定/环境等创建依赖关系图,并在任何依赖关系发生变化时更新视图.这可以通过让body产生一个副作用来实验性地检测到(尽管在第一次创建视图时也会调用body).

以下是一些代码,您可以使用它们进行实验:

struct ContentView: View {
    @State var model = ModalObservable()
    
    var body: some View {
        let _ = print("ContentView updates")
        UpdateDetector(observable: model)
        Button("Change Open") {
            model.changeOpen()
        }
        Button("Change View") {
            model.changeView()
        }
    }
}

struct UpdateDetector: View {
    let observable: ModalObservable
    
    var body: some View {
        let _ = print("UpdateDetector updates")
        Text(observable.state.open ? "Open" : "Close")
    }
}

@Observable class ModalObservable {
  var state = ModelState()
  func changeView() {
    self.state.view = UUID().uuidString
  }
  
  func changeOpen() {
    self.state.open.toggle()
  }
}

struct ModelState {    
    var view = "Foo"
    var open = false
}

在这里,SwiftUI已经确定UpdateDetector依赖于observable.state,所以每当它观察到observable.state中的变化时,即当按下任何一个按钮时,它都会更新.另一方面,ContentView不更新,因为它不依赖于任何可观察到的属性.

请注意,observable.state.openobservable.state.view不能算作两个单独的可观察属性,因为@Observable宏只将@ObservationTrackedstate相加.

这是另一个例子,这一次是Binding.

struct ContentView: View {
    @State var model = ModalObservable()
    
    var body: some View {
        let _ = print("ContentView updates")
        UpdateDetector(open: $model.state.open)
        Button("Change Open") {
            model.changeOpen()
        }
        Button("Change View") {
            model.changeView()
        }
    }
}

struct UpdateDetector: View {
    @Binding var open: Bool
    
    var body: some View {
        let _ = print("UpdateDetector updates")
        Text(open ? "Open" : "Close")
    }
}

在这里,UpdateDetector只依赖于open绑定,所以按"更改视图"不会更新它,但按"更改打开"会更新它.另一方面,ContentView现在也依赖于open绑定,因为绑定是双向的.按下"更改打开"也会更新ContentView.


如果你do希望observable.state.openobservable.state.view被分开跟踪,你可以把ModelState也变成一个@Observable类,尽管这也改变了语义.

@Observable
class ModelState {
    var view = "Foo"
    var open = false
}

Ios相关问答推荐

如何在SWIFT中处理不可发送的类型?

设置堆栈视图的所有属性

如何在wiftUI中管理导航栈?

如何从Windows PC在iPhone上安装Flutter 应用程序

如何创建装饰视图?

Swift - 缺少函数参数在编译期间不会失败,甚至会导致不同的数据类型?

Swift中是否有一种方法可以为可选属性创建一个计算(computed)属性

使用 SwiftUI 显示多个 VNRecognizedObjectObservation 边界框时偏移量错误

SwiftUI 中的描边图像边框

帧过渡动画有条件地工作

满足条件时 SwiftUI 动画背景 colored颜色 变化

如何识别 SwiftUI 中点击的按钮 ID 以使该按钮动画化?

Flutter 错误:CocoaPods not installed or not in valid state.

在 UICollectionView 上动态设置布局导致莫名其妙的 contentOffset 变化

以编程方式获取故事板 ID?

如何启用滑动以删除 TableView 中的单元格?

使用 Xcode 4 的 iPhone 临时构建

UITextView 内容插图

Xcode中的终端窗口?

我可以在 UIScrollView 中使用 UIRefreshControl 吗?