(SwiftUI更改检测)这段代码有什么问题?

(SwiftUI更改检测)这段代码有什么问题?,swiftui,Swiftui,在调试我正在使用的应用程序的问题时,我设法将其缩小到以下最小示例: class RadioModel: ObservableObject { @Published var selected: Int = 0 } struct RadioButton: View { let idx: Int @EnvironmentObject var radioModel: RadioModel var body: some View { Button(actio

在调试我正在使用的应用程序的问题时,我设法将其缩小到以下最小示例:

class RadioModel: ObservableObject {
    @Published var selected: Int = 0
}
struct RadioButton: View {
    let idx: Int
    @EnvironmentObject var radioModel: RadioModel
    var body: some View {
        Button(action: {
            self.radioModel.selected = self.idx
        }, label: {
            if radioModel.selected == idx {
                Text("Button \(idx)").background(Color.yellow)
            } else {
                Text("Button \(idx)")
            }
        })
    }
}
struct RadioListTest: View {
    @ObservedObject var radioModel = RadioModel()
    var body: some View {
        return VStack {
            Text("You selected: \(radioModel.selected)")
            RadioButton(idx: 0)
            RadioButton(idx: 1)
            RadioButton(idx: 2)
        }.environmentObject(radioModel)
    }
}
struct ContentView: View {
    @State var refreshDate = Date()
    func refresh() {
        print("Refreshing...")
        self.refreshDate = Date()
    }
    var body: some View {
        VStack {
            Text("\(refreshDate)")
            HStack {
                Button(action: {
                    self.refresh()
                }, label: {
                    Text("Refresh")
                })
                RadioListTest()
            }
        }
    }
}
这段代码在我看来相当合理,尽管它显示了一个特殊的bug:当我点击
刷新
按钮时,单选按钮停止工作。单选按钮不会刷新,并保留对旧的
RadioModel
实例的引用,因此当我单击它们时,它们会更新该实例,而不是
Refresh
之后创建的新按钮会导致构建新的
RadioListTest
。我怀疑我使用
EnvironmentObject
s的方式有问题,但我没有发现任何参考资料表明我所做的是错误的。我知道我可以通过各种方式解决这个问题,强制刷新单选按钮,但我希望能够了解哪些情况需要刷新强制破解,我不能仅仅因为“安全比抱歉更好”就在代码中散布这些,如果每次修改都要重新绘制所有内容,那么性能会非常糟糕


编辑:澄清。在我看来,奇怪的事情是:为什么在
refresh
上重新创建
radioisttest
(连同一个新的
RadioModel
)并重新评估它的
body
,但是创建了
RadioButton
,而不评估
body
属性,但是使用了前面的
正文
。它们都只有一个视图模型作为状态,实际上是相同的视图模型,但一个视图模型作为
ObservedObject
,另一个视图模型作为
EnvironmentObject
。我怀疑这是我对
环境对象的误用,但我找不到任何关于它为什么是错误的参考资料这是有效的:(是的,我知道,你知道如何解决它,但我认为这是“正确”的方法

问题是这一行:

struct RadioListTest: View {
    @ObservedObject var radioModel = RadioModel(). <<< problem
struct RadioListTest:视图{
@ObservedObject var radioModel=radioModel()。这是有效的:(是的,我知道,你知道如何解决它,但我认为这是“正确”的方法

问题是这一行:

struct RadioListTest: View {
    @ObservedObject var radioModel = RadioModel(). <<< problem
struct RadioListTest:视图{
@ObservedObject var radioModel=radioModel()。
这段代码有什么问题

您的
RadioListTest
子视图不会在
refresh()
上更新,因为它不依赖于已更改的参数(
refreshDate
,在本例中),所以SwiftUI渲染引擎假定它与先前创建的相同,并且不做任何处理:

HStack {
    Button(action: {
        self.refresh()
    }, label: {
        Text("Refresh")
    })
    RadioListTest()     // << here !!
}
这段代码有什么问题

您的
RadioListTest
子视图不会在
refresh()
上更新,因为它不依赖于已更改的参数(
refreshDate
,在本例中),所以SwiftUI渲染引擎假定它与先前创建的相同,并且不做任何处理:

HStack {
    Button(action: {
        self.refresh()
    }, label: {
        Text("Refresh")
    })
    RadioListTest()     // << here !!
}

请再次检查代码好吗?它不可编译…错误:无法编辑泛型参数“Content”inferred@Chrisyeh抱歉,必须删除该参数,不再使用它。请再次检查您的代码好吗?它不可编译…错误:无法编辑通用参数“内容”inferred@Chris是的,对不起,护理人员er必须被删除,它不再被使用。如果所有状态都是全局的和单实例的,那么双实例就没有问题。但这对于更大的应用程序来说是不可扩展的。如果有一个复杂的应用程序具有导航功能,并且可能有大量的放射科医生,我无法在根视图中初始化所有内容,然后传递它关闭后,我需要能够装入子视图状态。例如,考虑表示文件系统导航,每个单选按钮都是您可以选择导航到的目录(有点像MacOS“打开文件”对话框)。我已经通过您的第一个注释理解了这一点;)我正在考虑其他解决方案……但这不是唯一的解决方案“第一个问题”不再是……这是第二个问题;)要清楚:每次父对象更改时都可以重新创建模型(并重置其状态),这不是我要调查的问题。我希望在更改与其关联的
环境对象
实例时重新评估按钮的
主体
(当刷新日期发生变化时会发生这种情况),但问题不是“我如何才能使它工作”,而是“我做错了什么?”".yeh如果所有状态都是全局和单实例,那么双实例就没有问题。但对于更大的应用程序来说,这并不能扩展。如果有一个具有导航功能的复杂应用程序,并且可能有大量的放射科医生,我无法在根视图中初始化所有内容,然后将其向下传递,我需要能够合并子视图状态。以表示文件系统导航为例,每个单选按钮都是您可以选择导航到的目录(有点像MacOS的“打开文件”对话框)。我已经通过您的第一条评论理解了这一点;)我正在考虑其他解决方案……但这不是“第一个问题”还有…这是第二个;)要清楚:每次父对象更改时都可以重新创建模型(并重置其状态),这不是我要调查的问题。我希望在更改与其关联的
环境对象
实例时重新评估按钮的
主体
(当刷新日期发生变化时会发生这种情况),但问题并不是“我如何才能使它工作”,而是“我做错了什么”.RadioListTest
会更新,并且每次刷新父视图时都会调用它的
主体
。这也是
RadioModel
的两个实例的原因。您可以通过方法
init
主体
上的断点来检查。但它的子视图不会更新,这就是导致错误的原因不添加显式
id
会使SwiftUI识别
单选按钮