我在视图更新中遇到了问题,Core Data上下文不知道关系的变化,所以我不是swift的专家,还是初学者,我想知道如何观察这些变化或要做的事情,看看模型中的特定变化,(我尝试了ENTITY.objectWillChange.send()不适用于关系)我目前的解决方案是刷新上下文中的Head实体,这不是一个解决方案,因为整个UI将改变(context.refresh())。
这里我使用的例子中,我们说我们有">概要文件";与OneToMany">钱包";和OneToMany钱包Balance,因此核心数据图将为:
,----------------.
| Profile |
|----------------|
`----------------'
| 1
| *
,---------.
|Wallet |
|---------|
`---------'
| 1
| *
,-------------.
|Balance. |
|-------------|
`-------------'
swift的例子,我试图把所有的东西在一个视图,你可以尝试一下,你只需要添加模型和PersistentContainer
import SwiftUI
import CoreData
struct TestView : View {
@Environment(.managedObjectContext)
var context : NSManagedObjectContext
@FetchRequest(sortDescriptors: [])
var profiles : FetchedResults<Profile>
var body: some View {
NavigationView {
List{
ForEach(profiles){ profile in
NavigationLink( destination: {
profileView(profile : profile)
}){
if(profile.isDefault){
Text(profile.profileName).foregroundColor(Color.red).lineLimit(1)
}else{
Text(profile.profileName).foregroundColor(Color.blue).lineLimit(1)
}
}
}
.onDelete(perform: deleteProfile)
.navigationTitle("Profiles")
.toolbar{
Button(action: addProfile, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
}
}
private func deleteProfile(offset : IndexSet) {
offset.map{
profiles[$0]
}.forEach { profile in
context.delete(profile)
saveContext()
}
}
private func deleteWallet(profile : Profile , offset : IndexSet) {
offset.map{
profile.profileWalletsArray()[$0]
}.forEach { wallet in
context.delete(wallet)
saveContext()
}
}
private func deleteBalance(wallet : Wallet , offset : IndexSet) {
offset.map{
wallet.walletBalanceArray()[$0]
}.forEach { balance in
context.delete(balance)
saveContext()
}
}
@ViewBuilder
func profileView(profile : Profile) -> some View {
List{
if profile.profileWalletsArray().isEmpty{
Text("Empty")
}else{
ForEach(profile.profileWalletsArray()){ wallet in
NavigationLink(destination: {
walletView(wallet: wallet)
}){
Text("(wallet.walletName)").lineLimit(1)
}
}
.onDelete(perform: { index in
return deleteWallet(profile: profile, offset: index)
})
}
}
.navigationTitle(Text(profile.profileName))
.toolbar{
Button(action: {
addWallet(profile : profile)
}, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
@ViewBuilder
func walletView(wallet : Wallet) -> some View {
if wallet.walletBalanceArray().isEmpty {
Text("Empty Wallet").lineLimit(1)
}else {
VStack(alignment: .leading, spacing: 0){
List{
ForEach(wallet.walletBalanceArray()) { balance in
VStack{
HStack{
Text("Asset : (balance.balanceAsset)").lineLimit(1)
Text("Balance : (balance.balanceAmount) ")
}
}
}
.onDelete(perform: { index in
return deleteBalance(wallet: wallet, offset: index)
})
}
}
.navigationTitle(Text(wallet.walletName))
.toolbar{
Button(action: {
addBalance(wallet: wallet)
}, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
}
func addWallet(profile : Profile) {
withAnimation{
debugPrint("Add Wallet")
let wallet = Wallet(context: context)
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
profile.addToWallets(wallet)
context.refresh(profile, mergeChanges: true)
saveContext()
}
}
func addBalance(wallet : Wallet) {
withAnimation{
wallet.profile?.objectWillChange.send()
wallet.objectWillChange.send()
debugPrint("Add Balance")
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
saveContext()
}
}
func addProfile() {
withAnimation{
do {
let oldProfile = try context.fetch(Profile.fetchRequest()).filter{
$0.isDefault
}.first
oldProfile?.isDefault = false
debugPrint("old profile (String(describing: oldProfile?.profileName))")
}catch {
fatalError(error.localizedDescription)
}
let profile = Profile(context: context)
let wallet = Wallet(context: context)
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
//Profile
profile.name = UUID().uuidString
profile.isDefault = true
profile.addToWallets(wallet)
profile.createdAt = Date()
profile.updatedAt = Date()
saveContext()
}
}
private func saveContext() {
do {
try context.save()
}catch {
let error = error as NSError
fatalError(error.debugDescription)
}
}
}
extension Profile {
public func profileWalletsArray() -> [Wallet] {
return wallets?.allObjects as? [Wallet] ?? []
}
var profileName : String {
return name ?? "unknown"
}
}
extension Wallet {
var walletName : String {
return name ?? "unknown"
}
public func walletBalanceArray() -> [Balance] {
return balances?.allObjects as? [Balance] ?? []
}
}
extension Balance {
var balanceAmount : Int {
return Int(amount)
}
var balanceAsset : String {
return asset ?? "unknown"
}
}
============= ANSWER + FIX =========
这里的解决方案由"@lorem ipsum"通过创建单独的视图并添加@ObservedObject
修复了这个问题TestView Update =>
var body: some View {
NavigationView {
List{
ForEach(profiles){ profile in
NavigationLink( destination: {
ProfileView(profile : profile)
}){
if(profile.isDefault){
Text(profile.profileName).foregroundColor(Color.red).lineLimit(1)
}else{
Text(profile.profileName).foregroundColor(Color.blue).lineLimit(1)
}
}
}
.onDelete(perform: deleteProfile)
.navigationTitle("Profiles")
.toolbar{
Button(action: addProfile, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
}
}
ProfileView.swift
import SwiftUI
import CoreData
struct ProfileView: View {
@Environment(.managedObjectContext)
var context : NSManagedObjectContext
@ObservedObject
var profile : Profile
var body: some View {
List{
if profile.profileWalletsArray().isEmpty{
Text("Empty")
}else{
ForEach(profile.profileWalletsArray()){ wallet in
NavigationLink(destination: {
WalletView(wallet: wallet)
}){
Text("(wallet.walletName)").lineLimit(1)
}
}
.onDelete(perform: { index in
return deleteWallet(profile: profile, offset: index)
})
}
}
.navigationTitle(Text(profile.profileName))
.toolbar{
Button(action: {
addWallet(profile : profile)
}, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
private func deleteWallet(profile : Profile , offset : IndexSet) {
offset.map{
profile.profileWalletsArray()[$0]
}.forEach { wallet in
context.delete(wallet)
saveContext()
}
}
func addWallet(profile : Profile) {
withAnimation{
debugPrint("Add Wallet")
let wallet = Wallet(context: context)
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
profile.addToWallets(wallet)
context.refresh(profile, mergeChanges: true)
saveContext()
}
}
private func saveContext() {
do {
try context.save()
}catch {
let error = error as NSError
fatalError(error.debugDescription)
}
}
}
WalletView.swift
import SwiftUI
import CoreData
struct WalletView: View {
@Environment(.managedObjectContext)
var context : NSManagedObjectContext
@ObservedObject
var wallet : Wallet
var body: some View {
if wallet.walletBalanceArray().isEmpty {
Text("Empty Wallet").lineLimit(1)
}else {
VStack(alignment: .leading, spacing: 0){
List{
ForEach(wallet.walletBalanceArray()) { balance in
BalanceView(balance: balance)
}
.onDelete(perform: { index in
return deleteBalance(wallet: wallet, offset: index)
})
}
}
.navigationTitle(Text(wallet.profile?.profileName ?? "unknown"))
.toolbar{
Button(action: {
addBalance(wallet: wallet)
}, label: {
Text("Add").foregroundColor(Color.black)
})
}
}
}
private func deleteBalance(wallet : Wallet , offset : IndexSet) {
offset.map{
wallet.walletBalanceArray()[$0]
}.forEach { balance in
context.delete(balance)
saveContext()
}
}
func addBalance(wallet : Wallet) {
withAnimation{
wallet.profile?.objectWillChange.send()
wallet.objectWillChange.send()
debugPrint("Add Balance")
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
saveContext()
}
}
private func saveContext() {
do {
try context.save()
}catch {
let error = error as NSError
fatalError(error.debugDescription)
}
}
}
BalanceView.swift
import SwiftUI
import CoreData
struct BalanceView: View {
@Environment(.managedObjectContext)
var context : NSManagedObjectContext
@ObservedObject
var balance : Balance
var body: some View {
VStack{
HStack{
Text("Asset : (balance.balanceAsset)").lineLimit(1)
Text("Balance : (balance.balanceAmount) ")
}
}
}
private func deleteBalance(wallet : Wallet , offset : IndexSet) {
offset.map{
wallet.walletBalanceArray()[$0]
}.forEach { balance in
context.delete(balance)
saveContext()
}
}
func addBalance(wallet : Wallet) {
withAnimation{
wallet.profile?.objectWillChange.send()
wallet.objectWillChange.send()
debugPrint("Add Balance")
let balance1 = Balance(context: context)
let balance2 = Balance(context: context)
balance1.asset = "EUR"
balance1.amount = Int64.random(in: 1..<100)
balance2.asset = "USD"
balance2.amount = Int64.random(in: 1..<100)
//Wallet
wallet.addToBalances(balance1)
wallet.addToBalances(balance2)
wallet.name = UUID().uuidString
wallet.createdAt = Date()
wallet.updatedAt = Date()
saveContext()
}
}
private func saveContext() {
do {
try context.save()
}catch {
let error = error as NSError
fatalError(error.debugDescription)
}
}
}
希望这个例子可以帮助到其他人:p
所有CoreData对象都是ObservableObject
,如果你想看到变化,你必须将它们包装在@ObservedObject
中。
要做到这一点,使子视图以profile
,wallet
和balance
为参数,而不是拥有所有这些变量与子视图。