每次@State
被“重新分配”时,我想重置一些@ObservedObject
。如何做到这一点?
class Selection: ObservableObject {
let id = UUID()
}
struct ContentView: View {
@State var selectedIndex: Int = 0
var selections: [Selection] = [Selection(), Selection(), Selection()]
var body: some View {
VStack {
// Assume the user can select something so selectedIndex changes all the time
// SwiftUI will just keep updating the same presentation layer with new data
Button("Next") {
self.selectedIndex += 1
if self.selectedIndex >= self.selections.count {
self.selectedIndex = 0
}
}
SelectionDisplayer(selection: self.selections[self.selectedIndex])
}
}
}
struct SelectionDisplayer: View {
@ObservedObject var selection: Selection {
didSet { // Wish there were something *like* this
print("will never happen")
self.tabIndex = 0
}
}
@State var tapCount: Int = 0 // Reset this to 0 when `selection` changes from one object to another
var body: some View {
Text(self.selection.id.description)
.onReceive(self.selection.objectWillChange) {
print("will never happen")
}
Button("Tap Count: \(self.tapCount)") {
self.tapCount += 1
}
}
}
我知道onReceive
,但我不是想响应objectWillChange
来修改状态,而是当对象本身被关闭时。在具有引用类型的UIKit中,我将使用didSet
,但这在这里不起作用。
我确实尝试为此使用PreferenceKey
(gist),但这似乎太过分了。
答案 0 :(得分:1)
为我完成此操作的唯一方法是通过暂时隐藏子视图来强制父视图重画该子视图。这是一种hack,但是替代方法是传入$tapCount
,因为情况更糟,因为这样父级不仅必须知道必须重绘它,而且还必须知道其内部状态。
这可能可以重构为它自己的视图,从而使其不那么肮脏。
import Foundation
import SwiftUI
import Combine
class Selection {
let id = UUID()
}
struct RedirectStateChangeView: View {
@State var selectedIndex: Int = 0
@State var isDisabled = false
var selections: [Selection] = [Selection(), Selection(), Selection()]
var body: some View {
VStack {
// Assume the user can select something so selectedIndex changes all the time
// SwiftUI will just keep updating the same presentation layer with new data
Button("Next") {
self.selectedIndex += 1
if self.selectedIndex >= self.selections.count {
self.selectedIndex = 0
}
self.isDisabled = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.005) {
self.isDisabled = false
}
}
if !isDisabled {
SelectionDisplayer(selection: selections[selectedIndex])
}
}
}
}
struct SelectionDisplayer: View {
var selection: Selection
@State var tapCount: Int = 0 // Reset this to 0 when `selection` changes from one object to another
var body: some View {
VStack{
Text(selection.id.description)
Button("Tap Count: \(self.tapCount)") {
self.tapCount += 1
}
}
}
}
答案 1 :(得分:1)
当前(测试版5),最好的方法可能是对数据更改时要重置的项目使用构造函数和通用ObservableObject
。这样可以保留一些@State
,而有些则被重置。
在下面的示例中,tapCount
每次更改都会重置selection
,而allTimeTaps
不会更改。
class StateHolder<Value>: ObservableObject {
@Published var value: Value
init(value: Value) {
self.value = value
}
}
struct ContentView: View {
@State var selectedIndex: Int = 0
var selections: [Selection] = [Selection(), Selection(), Selection()]
var body: some View {
VStack {
// Assume the user can select something so selectedIndex changes all the time
// SwiftUI will just keep updating the same presentation though
Button("Next") {
self.selectedIndex += 1
if self.selectedIndex >= self.selections.count {
self.selectedIndex = 0
}
}
SelectionDisplayer(selection: self.selections[self.selectedIndex])
}
}
}
struct SelectionDisplayer: View {
struct SelectionState {
var tapCount: Int = 0
}
@ObservedObject var selection: Selection
@ObservedObject var stateHolder: StateHolder<SelectionState>
@State var allTimeTaps: Int = 0
init(selection: Selection) {
let state = SelectionState()
self.stateHolder = StateHolder(value: state)
self.selection = selection
}
var body: some View {
VStack {
Text(self.selection.id.description)
Text("All Time Taps: \(self.allTimeTaps)")
Text("Tap Count: \(self.stateHolder.value.tapCount)")
Button("Tap") {
self.stateHolder.value.tapCount += 1
self.allTimeTaps += 1
}
}
}
}
在寻找解决方案时,我很感兴趣地发现您无法在@State
中初始化init
变量。编译器会抱怨在访问self之前尚未设置所有属性。
答案 2 :(得分:0)
这就是您想要的,我相信:
class Selection: ObservableObject { let id = UUID() }
struct ContentView: View {
@State var selectedIndex: Int = 0
var selections: [Selection] = [Selection(), Selection(), Selection()]
@State var count = 0
var body: some View {
VStack {
Button("Next") {
self.count = 0
self.selectedIndex += 1
if self.selectedIndex >= self.selections.count {
self.selectedIndex = 0
}
}
SelectionDisplayer(selection: self.selections[self.selectedIndex], count: $count)
}
}
}
struct SelectionDisplayer: View {
@ObservedObject var selection: Selection
@Binding var count: Int
var body: some View {
VStack {
Text("\(self.selection.id)")
Button("Tap Count: \(self.count)") { self.count += 1 }
}
}
}
我的Xcode不喜欢您的代码,因此我需要做一些其他更改,而不仅仅是将计数移到父级