首页 > 解决方案 > @State 初始值未在 init() 上重置变量

问题描述

我有一个分段控件,我将其用作我的应用程序工具栏中的选项卡,链接到 selectedTab 变量。当我切换选项卡时,帐户列表会更改,但不会重置行列表。我尝试在所选对象上使用 initialValue 以确保它重置为 0,但这并没有影响它。我尝试在 init 中打印以确保在 init 之后 selected 的值为 0。每次都是,但它仍然没有刷新 foreach 列表的行。

我错过了什么?

import SwiftUI
import SQLite3

struct ContentView:View {

    @EnvironmentObject var shared:SharedObject

    var body: some View {
        VStack {
            if shared.selectedTab == 0 {
                LedgerView(ledger: .Accounts)
            } else if shared.selectedTab == 1 {
                LedgerView(ledger: .Budgets)
            } else if shared.selectedTab == 2 {
                ReportsView()
            }
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }

}

struct LedgerView:View {

    @EnvironmentObject var shared:SharedObject

    let ledger:LedgerType
    @State var selected:Int = 0

    init(ledger:LedgerType) {
        self.ledger = ledger
        self._selected = State(initialValue: 0)
    }

    var body:some View {
        HStack {
            VStack(alignment: HorizontalAlignment.leading) {
                ForEach(shared.accounts.filter({$0.ledger == ledger})) { account in
                    Text(account.name)
                        .background(account.id == self.selected ? Color.accentColor : Color.clear)
                        .onTapGesture {self.selected = account.id}
                }
            }
            Divider()
            VStack(alignment: HorizontalAlignment.leading) {
                ForEach(shared.journalLines.filter({$0.accountID == selected})) { line in
                    Text("Line#\(line.id)")
                }
            }
        }
    }

}

struct ReportsView:View {
    var body:some View {
        Text("Under Construction ...")
    }
}

class SharedObject:ObservableObject {

    @Published var accounts:[Account] = []
    @Published var journalLines:[JournalLine] = []
    @Published var selectedTab:Int = 0

    init() {
        loadData()    
    }

}

enum LedgerType:Int {
    case Accounts=0,Budgets=1
    var name:String {
        switch(self) {
        case .Accounts: return "Accounts"
        case .Budgets: return "Budgets"
        }
    }
}
struct Account:Identifiable {
    var id:Int
    var name:String
    var ledger:LedgerType
}
struct Payee:Identifiable {
    var id:Int
    var name:String
}
struct Journal:Identifiable {
    var id:Int
    var date:Date
    var payeeID:Int
    var memo:String?
}
struct JournalLine:Identifiable {
    var id:Int
    var journalID:Int
    var accountID:Int
    var amount:Double
}

编辑删节的演示代码以尝试隔离问题

import SwiftUI

struct ContentView: View {

    @EnvironmentObject var shared:SharedObject

    var body: some View {
        VStack {
            Picker(selection: $shared.selectedTab, label: Text("")) {
                Text("Accounts").tag(0)
                Text("Budgets").tag(1)
            }.pickerStyle(SegmentedPickerStyle())
            Divider()
            if shared.selectedTab == 0 || shared.selectedTab == 1 {
                LedgerView()
            }
            Spacer()
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }

}

struct LedgerView:View {

    @State var selected:Int = 0

    init() {
        self._selected = State(initialValue: 0)
        print("LedgerView.init()")
    }

    var body:some View {
        VStack(alignment: HorizontalAlignment.leading) {
            Text("Selected: \(selected)")
            Picker(selection: $selected, label: Text("")) {
                Text("Account#1").tag(1)
                Text("Account#2").tag(2)
                Text("Account#3").tag(3)
            }
        }
    }

}

class SharedObject: ObservableObject {
    @Published var selectedTab:Int = 0
}

标签: swiftui

解决方案


根据您最近的评论,您需要的是@Binding 而不是@State。解释一下:结构 LedgerView 只是选择器在单独视图中的摘录。当您调用 LedgerView() 时,它不会实例化一个新对象。它只是在那个地方添加了选择器视图。因此,当您需要在切换选项卡时重置选择器时,您需要使用绑定来重置选择器。这是工作代码。希望能帮助到你。

struct ContentView: View {

    @EnvironmentObject var shared:SharedObject
    @State var selected: Int = 0

    var body: some View {
        VStack {
            Picker(selection: $shared.selectedTab, label: Text("")) {
                Text("Accounts").tag(0)
                Text("Budgets").tag(1)
            }.pickerStyle(SegmentedPickerStyle())
            Divider()
            if shared.selectedTab == 0 || shared.selectedTab == 1 {
                LedgerView(selected: $selected)
            }
            Spacer()
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .onReceive(shared.$selectedTab) { newValue in
            self.selected = 0
        }
    }
}

struct LedgerView:View {

    @Binding var selected: Int

    var body:some View {
        VStack(alignment: HorizontalAlignment.leading) {
            Text("Selected: \(selected)")
            Picker(selection: $selected, label: Text("")) {
                Text("Account#1").tag(1)
                Text("Account#2").tag(2)
                Text("Account#3").tag(3)
            }
        }
    }
}

[包含替代解决方案的早期答案]

我已经修改了您的代码以使这些行正常工作。我添加了示例数据以使代码正常工作。我怀疑问题是否出在您的数据上。我还修改了枚举 LedgerType 以使其可迭代。这是工作代码。

我在代码中修改了以下内容:

  1. 删除了将 ledgerType 传递给 LedgerView 作为事实来源

    @EnvironmentObject var 共享:SharedObject

  2. 添加了切换标签时默认选择第一个帐户的代码。这会在选项卡之间切换时刷新行。请参阅 .onReceive 中的代码

希望这可以帮助。如果你需要什么,请告诉我。

struct ContentView:View {

    @EnvironmentObject var shared: SharedObject

    var body: some View {
        VStack {
            Picker(selection: $shared.selectedTab, label: Text("")) {
                ForEach(0 ..< LedgerType.allCases.count) { index in
                    Text(LedgerType.allCases[index].rawValue).tag(index)
                }
            }
            .pickerStyle(SegmentedPickerStyle())

            if shared.selectedTab == 0 || shared.selectedTab == 1 {
                LedgerView()
            } else if shared.selectedTab == 2 {
                ReportsView()
            }
            Spacer()
        }.frame(maxWidth: .infinity, maxHeight: .infinity)
    }

}

struct LedgerView:View {
    @EnvironmentObject var shared:SharedObject
    @State var selected: Int = 0

    var body:some View {
        HStack {
            VStack(alignment: HorizontalAlignment.leading) {
                ForEach(shared.accounts.filter({ $0.ledger == LedgerType.allCases[shared.selectedTab] })) { account in
                    Text(account.name)
                        .background(account.id == self.selected ? Color.accentColor : Color.clear)
                        .onTapGesture {self.selected = account.id}
                }
            }
            Divider()
            VStack(alignment: HorizontalAlignment.leading) {
                ForEach(shared.journalLines.filter({$0.accountID == selected})) { line in
                    Text("Line#\(line.id)")
                }
            }
        }
        .onReceive(shared.$selectedTab) { newValue in
            if let id = self.shared.getInitialAccountId(tabIndex: newValue) {
                self.selected = id
            }
        }
    }
}

struct ReportsView:View {
    var body:some View {
        Text("Under Construction ...")
    }
}

class SharedObject:ObservableObject {

    @Published var accounts:[Account] = []
    @Published var journalLines:[JournalLine] = []
    @Published var selectedTab:Int = 0

    func getInitialAccountId(tabIndex: Int) -> Int? {
        if tabIndex == 0 {
            return accounts.filter({
                $0.ledger == LedgerType.Accounts
            }).first?.id
        }
        else if tabIndex == 1 {
            return accounts.filter({
                $0.ledger == LedgerType.Budgets
            }).first?.id
        }
        else {
            return accounts.filter({
                $0.ledger == LedgerType.Reports
            }).first?.id
        }
    }

    init() {
        accounts = [
            Account(id: 1, name: "Sales", ledger: .Accounts),
            Account(id: 2, name: "Purchase", ledger: .Accounts),
            Account(id: 3, name: "Forecast", ledger: .Budgets)
        ]
        journalLines = [
            // Line for sales
            JournalLine(id: 1, journalID: 10, accountID: 1, amount: 200),
            JournalLine(id: 2, journalID: 20, accountID: 1, amount: 400),
            // Line for purchase
            JournalLine(id: 3, journalID: 30, accountID: 2, amount: 600),
            JournalLine(id: 4, journalID: 40, accountID: 2, amount: 800)
        ]
    }

}

enum LedgerType: String, CaseIterable {
    case Accounts = "Accounts"
    case Budgets = "Budgets"
    case Reports = "Reports"
}

struct Account:Identifiable {
    var id:Int
    var name:String
    var ledger:LedgerType
}

struct Payee:Identifiable {
    var id:Int
    var name:String
}

struct Journal:Identifiable {
    var id:Int
    var date:Date
    var payeeID:Int
    var memo:String?
}

struct JournalLine:Identifiable {
    var id:Int
    var journalID:Int
    var accountID:Int
    var amount:Double
}

推荐阅读