简体   繁体   English

如何在SwiftUI中不使用按钮更新@EnvironmentObject?

[英]How to update @EnvironmentObject without button in SwiftUI?

What I am trying to accomplish here is when filterListGraph is updating data, iconArray will add the amount and then update data too. 我要在这里完成的是,当filterListGraph更新数据时,iconArray将添加数量,然后也更新数据。 I was trying to use .onAppear(), but it does not work properly as I expected since it adds amount only once when GraphView appears (I need to exit from GraphView and go back in order to update data). 我试图使用.onAppear(),但是它没有按我预期的那样正常工作,因为它仅在GraphView出现时才添加一次(我需要从GraphView退出并返回以更新数据)。 Is that possible to add amount into iconArray in GraphView and then update it? 可以在GraphView的iconArray中添加金额然后进行更新吗?

struct IconStruct: Identifiable {
let id = UUID()
var icon: String
var amount: Double
var color: Color
var index: Int
}

class UserSettings: ObservableObject {
@Published var iconArray: [IconStruct] = [IconStruct(icon: "FoodIcon", amount: 0, color: Color(.red), index: 0),
                                          IconStruct(icon: "ClothesIcon", amount: 0, color: Color(.blue), index: 1),
                                          IconStruct(icon: "SportIcon", amount: 0, color: Color(.yellow), index: 2)]

}

struct GraphView: View {

var dateFormatter: DateFormatter {
    let formatter = DateFormatter()
    formatter.dateStyle = .long
    return formatter
}

@EnvironmentObject var settings: UserSettings

@State private var fromDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())!
@State private var toDate = Date()
@State private var selectedType = "type != %@"
@State private var selectedDate = true
@State private var searchText = ""
@State private var isNavigationBarHidden: Bool = true

var body: some View {
    ScrollView {
        VStack {

            ForEach(settings.iconArray) { row in
                Text(row.amount.description)
            }

            HStack {
                Picker("", selection: $selectedType) {
                    Text("Expense").tag("type != %@")
                    Text("Income").tag("type == %@")
                }
                .pickerStyle(SegmentedPickerStyle())

            }

            HStack {
                Picker("", selection: $selectedDate) {
                    Text("From: \(dateFormatter.string(from: fromDate))").tag(true)
                    Text("To: \(dateFormatter.string(from: toDate))").tag(false)
                }
                .pickerStyle(SegmentedPickerStyle())
            }

            DatePicker("", selection: selectedDate ? $fromDate : $toDate, displayedComponents: .date)
                .labelsHidden()
        }

        PieChartRow(data: settings.iconArray, backgroundColor: .white)
            .frame(width: 300, height: 300)

        ForEach(settings.iconArray.sorted { $0.amount > $1.amount }) { icon in
            filterListGraph(type: self.selectedType, icon: icon.icon, from: self.fromDate, to: self.toDate, iconIndex: icon.index)
        }
    }
}
}


struct filterListGraph: View {
@Environment(\.managedObjectContext) var context
var financeArray: FetchRequest<Finance>
var type: Bool
var icon: String
var iconIndex: Int

@EnvironmentObject var settings: UserSettings

var amount: Double {
    return financeArray.wrappedValue.map { $0.wrappedAmount }.reduce(0,+)
}

// var amount: Double {
// self.settings.iconArray[self.iconIndex].amount = self.financeArray.wrappedValue.map { $0.wrappedAmount }.reduce(0,+)
//    return self.settings.iconArray[self.iconIndex].amount
// }

var body: some View {
    VStack {
        if amount != 0 {
            CellContainer(image: icon, label: nil, amount: amount.description, type: type)
        }
//        .onAppear() {
//           self.settings.iconArray[self.iconIndex].amount = self.financeArray.wrappedValue.map { $0.wrappedAmount }.reduce(0,+)
//        }
    }
}

init(type: String, icon: String, from: Date, to: Date, iconIndex: Int) {
    self.iconIndex = iconIndex
    self.icon = icon
    if type == "type == %@" {
        self.type = true
    } else {
        self.type = false
    }
    financeArray = FetchRequest<Finance>(
        entity: Finance.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Finance.date, ascending: true)
        ], predicate:
        NSCompoundPredicate(notPredicateWithSubpredicate: NSCompoundPredicate.init(type: .or, subpredicates: [
            NSPredicate(format: type, "false"),
            NSPredicate(format: "icon != %@", icon),
            NSPredicate(format: "date < %@ OR date >= %@", (Calendar.current.startOfDay(for: from) as NSDate), Calendar.current.date(byAdding: .day, value: 1, to: to)! as NSDate )
        ]))
    )
}
}

If I understood correctly, you need to track variable amount of array in UserSettings . 如果我理解正确,则需要在UserSettings跟踪可变数量的数组。 I can't run your code, because there are some whitespaces, so there are few tips, hope they may help you. 我无法运行您的代码,因为有一些空格,所以这里有一些技巧,希望它们对您有所帮助。 For both you need use Combine framework (you may read about it here ) 两者都需要使用Combine框架(您可以在此处阅读有关内容)

  1. create variable amount strictly in UserSettings and change it while your array cnahges: 严格在UserSettings创建可变数量 ,并在数组更改时更改它:
import Combine

class UserSettings: ObservableObject {

    @Published var amount: Double = 0.0
    @Published var iconArray: [IconStruct] {
        didSet {
            self.amount = Double(iconArray.count)
        }

    }

    init() {
        self.iconArray = [IconStruct(icon: "FoodIcon", amount: 0, color: Color(.red), index: 0),
        IconStruct(icon: "ClothesIcon", amount: 0, color: Color(.blue), index: 1),
        IconStruct(icon: "SportIcon", amount: 0, color: Color(.yellow), index: 2)]
    }

}
  1. But because I'm not sure that code in first tip is enough, you may add AnyPublisher to settings and after that subscribe for it: 但是由于我不确定第一个技巧中的代码是否足够,您可以将AnyPublisher添加到设置中,然后对其进行订阅:
class UserSettings: ObservableObject {

    @Published var amount: Double = 0.0
    @Published var iconArray: [IconStruct] {
        didSet {
            self.amount = Double(iconArray.count)
        }

    }

    var trackingAmount: AnyPublisher<Double, Never> {
        return $amount
            .eraseToAnyPublisher()
            // or something else you need, read about Combine
    }


    init() {
        self.iconArray = [IconStruct(icon: "FoodIcon", amount: 0, color: Color(.red), index: 0),
        IconStruct(icon: "ClothesIcon", amount: 0, color: Color(.blue), index: 1),
        IconStruct(icon: "SportIcon", amount: 0, color: Color(.yellow), index: 2)]
    }

}

// here the key idea is .onReceive(settings.trackingAmount)
struct filterListGraph: View {

    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Text("some another view").onReceive(settings.trackingAmount) { _ in
            // change what you need
        }
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM