@State 初始值不会在 init() 上重置变量 [英] @State initial value not resetting variable on init()

查看:23
本文介绍了@State 初始值不会在 init() 上重置变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a segmented control that I am using as a tabs in the toolbar of my app linked to the selectedTab variable. when I switch tabs the accounts list changes but it does not reset the lines list. I tried using initialValue on the selected to make sure it reset to 0 but this did not effect it. I tried a print in the init to make sure that the value of selected was 0, after the init. it was every time but it still did not refresh the lines foreach list.

我错过了什么?

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
}

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

edit abridged demo code to try to isolate the problem

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
}

推荐答案

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

Based on your recent comment, what you need is @Binding and not @State. To explain: The struct LedgerView is just an extract of the picker in a separate view. When you call LedgerView(), this doesn't instantiate a new object. It simply adds the picker view in that place. Therefore, when you need the picker to reset on switching tabs, you need to use binding to reset the picker. Here's the working code. Hope it helps.

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)
            }
        }
    }
}

<小时>

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


[Earlier answer containing alternate solution]

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

I've modified your code to make the lines work. I've added sample data to get the code to work. I doubt if the problem is with your data. Also I have modified the enum LedgerType to make it iterable. Here's the working code.

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

I've modified the following in code:

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

  1. Removed passing ledgerType to LedgerView as the source of truth is

@EnvironmentObject var shared: SharedObject

@EnvironmentObject var shared: SharedObject

添加代码以在切换标签时默认选择第一个帐户.这会在选项卡之间切换时刷新行.查看 .onReceive 中的代码

Added code to select the first account by default when switching tabs. This refreshes the lines when switching between tabs. See the code in .onReceive

希望这会有所帮助.如果您需要什么,请告诉我.

Hope this helps. If you need anything, let me know.

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
}

这篇关于@State 初始值不会在 init() 上重置变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆