当 SwiftUI 中的相关实体发生变化时,如何更新@FetchRequest?
How to update @FetchRequest, when a related Entity changes in SwiftUI?
在 SwiftUI View
中,我有一个基于 @FetchRequest
的 List
,显示 Primary
实体的数据和通过关系连接的 Secondary
实体。
View
及其 List
正确更新,当我添加一个新的 Primary
实体和一个新的相关辅助实体时。
问题是,当我在详细视图中更新连接的 Secondary
项目时,数据库得到更新,但更改没有反映在 Primary
列表中。
显然,@FetchRequest
不会被另一个视图中的更改触发。
此后当我在主视图中添加新项目时,之前更改的项目最终得到更新。
作为解决方法,我另外更新了详细视图中 Primary
实体的属性,并且更改正确传播到 Primary
视图。
我的问题是:
如何强制更新 SwiftUI Core Data 中所有相关的 @FetchRequests
?
特别是,当我无法直接访问相关实体时/@Fetchrequests
?
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
// TODO: ❌ workaround to trigger update on primary @FetchRequest
primary.managedObjectContext.refresh(primary, mergeChanges: true)
// primary.primaryName = primary.primaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
我尝试像这样触摸详细视图中的主要对象:
// TODO: ❌ workaround to trigger update on primary @FetchRequest
if let primary = secondary.primary {
secondary.managedObjectContext?.refresh(primary, mergeChanges: true)
}
然后主列表将更新。但是详细视图必须了解父对象。这会起作用,但这可能不是 SwiftUI 或 Combine 方式...
编辑:
基于上述解决方法,我使用全局保存(managedObject:) 函数修改了我的项目。这将触及所有相关的实体,从而更新所有相关的@FetchRequest。
import SwiftUI
import CoreData
extension Primary: Identifiable {}
// MARK: - Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
sortDescriptors: [
NSSortDescriptor(keyPath: \Primary.primaryName, ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
print("body PrimaryListView"); return
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(secondary: primary.secondary!)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")")
.font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// MARK: - Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var secondary: Secondary
@State private var newSecondaryName = ""
var body: some View {
print("SecondaryView: \(secondary.secondaryName ?? "")"); return
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.secondary.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
secondary.secondaryName = newSecondaryName
// save Secondary and touch Primary
(UIApplication.shared.delegate as! AppDelegate).save(managedObject: secondary)
presentationMode.wrappedValue.dismiss()
}
}
extension AppDelegate {
/// save and touch related objects
func save(managedObject: NSManagedObject) {
let context = persistentContainer.viewContext
// if this object has an impact on related objects, touch these related objects
if let secondary = managedObject as? Secondary,
let primary = secondary.primary {
context.refresh(primary, mergeChanges: true)
print("Primary touched: \(primary.primaryName ?? "no name")")
}
saveContext()
}
}
您需要一个 Publisher,它会生成有关上下文更改的事件和主视图中的一些状态变量,以强制在从该发布者接收事件时重建视图。
重要提示:状态变量 必须 在视图生成器代码中使用,否则渲染引擎将不知道发生了什么变化。
这里是对代码受影响部分的简单修改,可提供您需要的行为。
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
// below use of .refreshing is just as demo,
// it can be use for anything
Text("\(primary.primaryName ?? "nil")" + (self.refreshing ? "" : ""))
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
我也为此苦苦挣扎,并找到了一个非常好的和干净的解决方案:
您必须将行包装在单独的视图中,并在实体的该行视图中使用@ObservedObject。
这是我的代码:
酒单:
struct WineList: View {
@FetchRequest(entity: Wine.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Wine.name, ascending: true)
]
) var wines: FetchedResults<Wine>
var body: some View {
List(wines, id: \.id) { wine in
NavigationLink(destination: WineDetail(wine: wine)) {
WineRow(wine: wine)
}
}
.navigationBarTitle("Wines")
}
}
葡萄酒行:
struct WineRow: View {
@ObservedObject var wine: Wine // !! @ObserveObject is the key!!!
var body: some View {
HStack {
Text(wine.name ?? "")
Spacer()
}
}
}
要解决此问题,您必须将 @ObservedObject
添加到 SecondaryView
中的 var primary: Primary
才能正常工作 List
。 Primary
属于NSManagedObject
class,已经符合@ObservableObject
协议。这样可以观察到 Primary
实例的变化。
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
另一种方法:使用 Publisher 和 List.id():
struct ContentView: View {
/*
@FetchRequest...
*/
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) //the publisher
@State private var refreshID = UUID()
var body: some View {
List {
...
}
.id(refreshID)
.onReceive(self.didSave) { _ in //the listener
self.refreshID = UUID()
print("generated a new UUID")
}
}
}
每次在上下文中调用 NSManagedObjects 的 save() 时,它都会为列表视图生成一个新的 UUID,并强制刷新列表视图。
如果你在这里,我没有找到你的视图没有更新的原因,我认为这会对你有所帮助:
- 声明核心数据类型时始终使用@ObservedObject。
- 如果您使用的是 MVVM,请也使用 @ObservedObject 包装视图模型,并在 VM 中使用 @Published 创建核心数据类型。
这是一个使用@ObservedObject 创建 VM 的示例,因此当核心数据收到更新时,视图模型的实例会重新创建自身,并更新视图。
class ProductTitleValueViewModel: BaseViewModel, ObservableObject {
// MARK: - Properties
@Published var product: Product
var colorSet: [Color]
var currency: Currency
// MARK: - Init
init(product: Product, colorSet: [Color], currency: Currency) {
self.product = product
self.colorSet = colorSet
self.currency = currency
}
}
struct ProductTitleValueView: View {
@ObservedObject var viewModel: ProductTitleValueViewModel
var body: some View {
VStack(alignment: .leading, spacing: 5) {
HStack {
Circle()
.fill(
LinearGradient(colors: viewModel.colorSet, startPoint: .leading, endPoint: .trailing)
)
.opacity(0.6)
.frame(width: 20, height: 20)
Text(viewModel.product.wrappedName)
.font(.callout.bold())
.foregroundColor(ThemeColor.lightGray)
}
Text(viewModel.product.balance.toCurrency(with: viewModel.currency))
.font(.callout.bold())
.padding(.leading, 28)
}
}
}
如果您遵循这 2 个简单的事情,您将不会遇到核心日期更新视图的问题。
在 SwiftUI View
中,我有一个基于 @FetchRequest
的 List
,显示 Primary
实体的数据和通过关系连接的 Secondary
实体。
View
及其 List
正确更新,当我添加一个新的 Primary
实体和一个新的相关辅助实体时。
问题是,当我在详细视图中更新连接的 Secondary
项目时,数据库得到更新,但更改没有反映在 Primary
列表中。
显然,@FetchRequest
不会被另一个视图中的更改触发。
此后当我在主视图中添加新项目时,之前更改的项目最终得到更新。
作为解决方法,我另外更新了详细视图中 Primary
实体的属性,并且更改正确传播到 Primary
视图。
我的问题是:
如何强制更新 SwiftUI Core Data 中所有相关的 @FetchRequests
?
特别是,当我无法直接访问相关实体时/@Fetchrequests
?
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
// TODO: ❌ workaround to trigger update on primary @FetchRequest
primary.managedObjectContext.refresh(primary, mergeChanges: true)
// primary.primaryName = primary.primaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
我尝试像这样触摸详细视图中的主要对象:
// TODO: ❌ workaround to trigger update on primary @FetchRequest
if let primary = secondary.primary {
secondary.managedObjectContext?.refresh(primary, mergeChanges: true)
}
然后主列表将更新。但是详细视图必须了解父对象。这会起作用,但这可能不是 SwiftUI 或 Combine 方式...
编辑:
基于上述解决方法,我使用全局保存(managedObject:) 函数修改了我的项目。这将触及所有相关的实体,从而更新所有相关的@FetchRequest。
import SwiftUI
import CoreData
extension Primary: Identifiable {}
// MARK: - Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
sortDescriptors: [
NSSortDescriptor(keyPath: \Primary.primaryName, ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
print("body PrimaryListView"); return
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(secondary: primary.secondary!)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")")
.font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// MARK: - Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
var secondary: Secondary
@State private var newSecondaryName = ""
var body: some View {
print("SecondaryView: \(secondary.secondaryName ?? "")"); return
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.secondary.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
secondary.secondaryName = newSecondaryName
// save Secondary and touch Primary
(UIApplication.shared.delegate as! AppDelegate).save(managedObject: secondary)
presentationMode.wrappedValue.dismiss()
}
}
extension AppDelegate {
/// save and touch related objects
func save(managedObject: NSManagedObject) {
let context = persistentContainer.viewContext
// if this object has an impact on related objects, touch these related objects
if let secondary = managedObject as? Secondary,
let primary = secondary.primary {
context.refresh(primary, mergeChanges: true)
print("Primary touched: \(primary.primaryName ?? "no name")")
}
saveContext()
}
}
您需要一个 Publisher,它会生成有关上下文更改的事件和主视图中的一些状态变量,以强制在从该发布者接收事件时重建视图。
重要提示:状态变量 必须 在视图生成器代码中使用,否则渲染引擎将不知道发生了什么变化。
这里是对代码受影响部分的简单修改,可提供您需要的行为。
@State private var refreshing = false
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave)
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
// below use of .refreshing is just as demo,
// it can be use for anything
Text("\(primary.primaryName ?? "nil")" + (self.refreshing ? "" : ""))
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
// here is the listener for published context event
.onReceive(self.didSave) { _ in
self.refreshing.toggle()
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
我也为此苦苦挣扎,并找到了一个非常好的和干净的解决方案:
您必须将行包装在单独的视图中,并在实体的该行视图中使用@ObservedObject。
这是我的代码:
酒单:
struct WineList: View {
@FetchRequest(entity: Wine.entity(), sortDescriptors: [
NSSortDescriptor(keyPath: \Wine.name, ascending: true)
]
) var wines: FetchedResults<Wine>
var body: some View {
List(wines, id: \.id) { wine in
NavigationLink(destination: WineDetail(wine: wine)) {
WineRow(wine: wine)
}
}
.navigationBarTitle("Wines")
}
}
葡萄酒行:
struct WineRow: View {
@ObservedObject var wine: Wine // !! @ObserveObject is the key!!!
var body: some View {
HStack {
Text(wine.name ?? "")
Spacer()
}
}
}
要解决此问题,您必须将 @ObservedObject
添加到 SecondaryView
中的 var primary: Primary
才能正常工作 List
。 Primary
属于NSManagedObject
class,已经符合@ObservableObject
协议。这样可以观察到 Primary
实例的变化。
import SwiftUI
extension Primary: Identifiable {}
// Primary View
struct PrimaryListView: View {
@Environment(\.managedObjectContext) var context
@FetchRequest(
entity: Primary.entity(),
sortDescriptors: [NSSortDescriptor(key: "primaryName", ascending: true)]
)
var fetchedResults: FetchedResults<Primary>
var body: some View {
List {
ForEach(fetchedResults) { primary in
NavigationLink(destination: SecondaryView(primary: primary)) {
VStack(alignment: .leading) {
Text("\(primary.primaryName ?? "nil")")
Text("\(primary.secondary?.secondaryName ?? "nil")").font(.footnote).foregroundColor(.secondary)
}
}
}
}
.navigationBarTitle("Primary List")
.navigationBarItems(trailing:
Button(action: {self.addNewPrimary()} ) {
Image(systemName: "plus")
}
)
}
private func addNewPrimary() {
let newPrimary = Primary(context: context)
newPrimary.primaryName = "Primary created at \(Date())"
let newSecondary = Secondary(context: context)
newSecondary.secondaryName = "Secondary built at \(Date())"
newPrimary.secondary = newSecondary
try? context.save()
}
}
struct PrimaryListView_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
return NavigationView {
PrimaryListView().environment(\.managedObjectContext, context)
}
}
}
// Detail View
struct SecondaryView: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var primary: Primary
@State private var newSecondaryName = ""
var body: some View {
VStack {
TextField("Secondary name:", text: $newSecondaryName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
.onAppear {self.newSecondaryName = self.primary.secondary?.secondaryName ?? "no name"}
Button(action: {self.saveChanges()}) {
Text("Save")
}
.padding()
}
}
private func saveChanges() {
primary.secondary?.secondaryName = newSecondaryName
try? primary.managedObjectContext?.save()
presentationMode.wrappedValue.dismiss()
}
}
另一种方法:使用 Publisher 和 List.id():
struct ContentView: View {
/*
@FetchRequest...
*/
private var didSave = NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave) //the publisher
@State private var refreshID = UUID()
var body: some View {
List {
...
}
.id(refreshID)
.onReceive(self.didSave) { _ in //the listener
self.refreshID = UUID()
print("generated a new UUID")
}
}
}
每次在上下文中调用 NSManagedObjects 的 save() 时,它都会为列表视图生成一个新的 UUID,并强制刷新列表视图。
如果你在这里,我没有找到你的视图没有更新的原因,我认为这会对你有所帮助:
- 声明核心数据类型时始终使用@ObservedObject。
- 如果您使用的是 MVVM,请也使用 @ObservedObject 包装视图模型,并在 VM 中使用 @Published 创建核心数据类型。
这是一个使用@ObservedObject 创建 VM 的示例,因此当核心数据收到更新时,视图模型的实例会重新创建自身,并更新视图。
class ProductTitleValueViewModel: BaseViewModel, ObservableObject {
// MARK: - Properties
@Published var product: Product
var colorSet: [Color]
var currency: Currency
// MARK: - Init
init(product: Product, colorSet: [Color], currency: Currency) {
self.product = product
self.colorSet = colorSet
self.currency = currency
}
}
struct ProductTitleValueView: View {
@ObservedObject var viewModel: ProductTitleValueViewModel
var body: some View {
VStack(alignment: .leading, spacing: 5) {
HStack {
Circle()
.fill(
LinearGradient(colors: viewModel.colorSet, startPoint: .leading, endPoint: .trailing)
)
.opacity(0.6)
.frame(width: 20, height: 20)
Text(viewModel.product.wrappedName)
.font(.callout.bold())
.foregroundColor(ThemeColor.lightGray)
}
Text(viewModel.product.balance.toCurrency(with: viewModel.currency))
.font(.callout.bold())
.padding(.leading, 28)
}
}
}
如果您遵循这 2 个简单的事情,您将不会遇到核心日期更新视图的问题。