【问题标题】:Cannot rerender View on CoreData Update in SwiftUI无法在 SwiftUI 中重新渲染 CoreData 更新上的视图
【发布时间】:2021-11-04 12:27:24
【问题描述】:

我能够更新收藏部分中的收藏列表,但只有在我重新启动应用程序后,我有多个答案建议添加 @ObservedObject var asset: Artists 等并添加托管对象上下文,我尝试了所有,但是收藏夹部分不会在 coredata 更改时更新,任何人都可以提出解决方法,下面是文件的代码,我希望在 coredata 更新后看到收藏夹被添加和显示,但目前这个视图正在获取仅在我重新启动应用程序后更新。

代码被分成几个部分,其中 SongCell 显示了每个单元格及其进一步提取的播放按钮。当我重新加载应用程序时,还会显示一张图片,以便在收藏夹部分查看我想要的内容。谢谢。

[![在此处输入图片描述][1]][1]

import Foundation
import SwiftUI
import Combine
import AVFoundation

struct Favorites: View {
    
    
    
    
    @ObservedObject  var favListVM = FavoriteListVM()
    @ObservedObject var repo = FavoriteRepository()
    @Binding var favListVM1: FavoriteListVM
   
    
    
    var body: some View {
        
        VStack {
            
            NavigationView {
                List {
                    
                    ForEach(favListVM.favCellVMs) {
                        songCellVM in
                        
                     
                        SongCell(isVisible: $favListVM.isVisible, favCellVM: songCellVM, selectedSong: $favListVM.selectedSong, favListVM1: $favListVM1, isLoved: favListVM.isFavorite ?? false)
                    }
                }
                .navigationTitle("Favorites")
                .font(.subheadline)
            }
            
        
            if favListVM.isVisible  {
                HStack(alignment: .bottom){
                    Image(uiImage: UIImage(data: favListVM.selectedSong?.artistImage ?? Data()) ?? UIImage())
                        .resizable()
                        .frame(width: 50, height: 50, alignment: .leading)
                        .scaledToFit()
                        .cornerRadius(10)
                    Spacer()
                    VStack {
                        Text(favListVM.selectedSong?.songname ?? " ")
                        Text(favListVM.selectedSong?.artistname ?? " ")
                    }
                    
                    
                    ExtractedView(isVisible: $favListVM.isVisible, selectedSong: $favListVM.selectedSong, favoriteListVM2: $favListVM1, favCellVM: FavoriteCellVM(song: Song(album: favListVM.selectedSong?.album ?? "no album found", artistImage: favListVM.selectedSong?.artistImage, artistname: favListVM.selectedSong?.artistname ?? "unknown", genre: favListVM.selectedSong?.genre, songMp3: favListVM.selectedSong?.songMp3, songname: favListVM.selectedSong?.songname ?? "no songs found", id: favListVM.selectedSong?.id ?? UUID())))
                    
                }
            }
        }
        
    }
    
    struct SongCell: View {
        
        @Binding var isVisible: Bool
        @ObservedObject var favCellVM: FavoriteCellVM
        @State var playButton: Bool = false
        @Binding var selectedSong: Song?
        @Binding  var favListVM1: FavoriteListVM
        var isSelected: Bool { favCellVM.song.id == selectedSong?.id }
        @Environment(\.managedObjectContext) var managedObjectContext
        @State var isLoved:Bool
        
        
        @FetchRequest(entity: Artists.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Artists.artistname, ascending: true)]) var artists: FetchedResults<Artists>
        
        
        
        var onCommit: () -> () = {  }
        var body: some View {
            
            HStack {
                let result =  artists.filter { artist in
                    artist.id == favCellVM.song.id
                    
                }
                
                Image(uiImage: UIImage(data: favCellVM.song.artistImage ?? Data()) ?? UIImage())
                    .resizable()
                    .frame(width: 70, height: 70, alignment: .center)
                    .scaledToFit()
                    .cornerRadius(20)
                Spacer()
                Text(favCellVM.song.artistname)
                
                
                Button(action: {
                    
                    print(favCellVM.song.id!)
                    print(result[0].id!)
                    
                    if (result[0].isFavorite == nil){
                        result[0].isFavorite = true
                    }
                    else if(result[0].isFavorite == false) {
                        result[0].isFavorite = true
                    }
                    
                    else {
                        result[0].isFavorite = false
                    }
                    
                    
                    do {
                        try managedObjectContext.save()
                        print("done")
                        print(result)
                    }
                    catch {
                        print("\(error.localizedDescription)")
                    }
                    
                    
                    
                }) {  Image(systemName: result[0].isFavorite == true  ? "suit.heart.fill" : "suit.heart")
                    .resizable()
                    .frame(width: 25, height: 25, alignment: .center)
                    .padding()
                }
                .buttonStyle(PlainButtonStyle())
                
                //--
                
                ExtractedView(isVisible: $isVisible, selectedSong: $selectedSong, favoriteListVM2: $favListVM1,  favCellVM: favCellVM)
                
                
                
            }
        }
    }
    
    
    
    struct ExtractedView: View {
        @Binding var isVisible: Bool
        @Binding var selectedSong: Song?
        @Binding var favoriteListVM2: FavoriteListVM
        
        @ObservedObject var favCellVM: FavoriteCellVM
        var isSelected: Bool { favCellVM.song.id == selectedSong?.id }
        
        
        var body: some View {
            Button(action: {
                
                print(isSelected)
                isVisible.toggle()
                if isSelected  {
                    selectedSong = nil
                    favoriteListVM2.audioPlayer?.stop()
                    
                } else {
                    selectedSong = favCellVM.song
                    isVisible = true
                    do {
                        favoriteListVM2.audioPlayer?.stop()
                        favoriteListVM2.audioPlayer = try AVAudioPlayer(data: favCellVM.song.songMp3!)
                        favoriteListVM2.audioPlayer?.prepareToPlay()
                        favoriteListVM2.audioPlayer?.play()
                    } catch let error {
                        print("\(error.localizedDescription)")
                    }
                    
                }
                
                
            }){     Image(systemName: isSelected ? "pause.fill" : "play.fill")
                .resizable()
                .frame(width: 25, height: 25, alignment: .center)
                .padding()
            }
            .buttonStyle(PlainButtonStyle())
        }
    }
}

//loremipsum回答后更新代码

import Foundation
import SwiftUI
import Combine
import AVFoundation

struct Favorites: View {
    
    
    
    //  @ObservedObject var songsListVM = SongListVM()
   // @ObservedObject  var favListVM = FavoriteListVM()
  //  @StateObject  var favListVM: FavoriteListVM
    @StateObject var repo = FavoriteRepository()
    @ObservedObject var favListVM1: FavoriteListVM
   
    
    var body: some View {
        
        VStack {
            
            NavigationView {
                List {
                    
                    ForEach(favListVM1.favCellVMs) {
                        songCellVM in
                        
                        //                        SongCell(isVisible: $favListVM.isVisible , songCellVM: songCellVM, selectedSong: $favListVM.selectedSong, songsListVM1: $favListVM1)
                        SongCell(isVisible: $favListVM1.isVisible, favCellVM: songCellVM, selectedSong: $favListVM1.selectedSong,  isLoved: favListVM1.isFavorite ?? false)
                    }
                }
                .navigationTitle("Favorites")
                .font(.subheadline)
            }
            
            
            //--
            
            //--
            if favListVM1.isVisible  {
                HStack(alignment: .bottom){
                    Image(uiImage: UIImage(data: favListVM1.selectedSong?.artistImage ?? Data()) ?? UIImage())
                        .resizable()
                        .frame(width: 50, height: 50, alignment: .leading)
                        .scaledToFit()
                        .cornerRadius(10)
                    Spacer()
                    VStack {
                        Text(favListVM1.selectedSong?.songname ?? " ")
                        Text(favListVM1.selectedSong?.artistname ?? " ")
                    }
                    
                    
                    ExtractedView(isVisible: $favListVM1.isVisible, selectedSong: $favListVM1.selectedSong, favoriteListVM2: favListVM1, favCellVM: FavoriteCellVM(song: Song(album: favListVM1.selectedSong?.album ?? "no album found", artistImage: favListVM1.selectedSong?.artistImage, artistname: favListVM1.selectedSong?.artistname ?? "unknown", genre: favListVM1.selectedSong?.genre, songMp3: favListVM1.selectedSong?.songMp3, songname: favListVM1.selectedSong?.songname ?? "no songs found", id: favListVM1.selectedSong?.id ?? UUID())))
                    
                }
            }
        }
        
    }
    
    struct SongCell: View {
        
        @Binding var isVisible: Bool
        @ObservedObject var favCellVM: FavoriteCellVM
        @State var playButton: Bool = false
        @Binding var selectedSong: Song?
     //   @Binding  var favListVM1: FavoriteListVM
        var isSelected: Bool { favCellVM.song.id == selectedSong?.id }
        @Environment(\.managedObjectContext) var managedObjectContext
        @State var isLoved:Bool
        
        
        @FetchRequest(entity: Artists.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Artists.artistname, ascending: true)]) var artists: FetchedResults<Artists>
        
        
        
        var onCommit: () -> () = {  }
        var body: some View {
            
            HStack {
                let result =  artists.filter { artist in
                    artist.id == favCellVM.song.id
                    
                }
                
                Image(uiImage: UIImage(data: favCellVM.song.artistImage ?? Data()) ?? UIImage())
                    .resizable()
                    .frame(width: 70, height: 70, alignment: .center)
                    .scaledToFit()
                    .cornerRadius(20)
                Spacer()
                Text(favCellVM.song.artistname)
                
                
                Button(action: {
                    
                    print(favCellVM.song.id!)
                    print(result[0].id!)
                    
                    if (result[0].isFavorite == nil){
                        result[0].isFavorite = true
                    }
                    else if(result[0].isFavorite == false) {
                        result[0].isFavorite = true
                    }
                    
                    else {
                        result[0].isFavorite = false
                    }
                    
                    
                    do {
                        try managedObjectContext.save()
                        
                        print("done")
                      //  print(result)
                    }
                    catch {
                        print("\(error.localizedDescription)")
                    }
                    
                    
                    
                }) {  Image(systemName: result[0].isFavorite == true  ? "suit.heart.fill" : "suit.heart")
                    .resizable()
                    .frame(width: 25, height: 25, alignment: .center)
                    .padding()
                }
                .buttonStyle(PlainButtonStyle())
                
                //--
                
                ExtractedView(isVisible: $isVisible, selectedSong: $selectedSong, favoriteListVM2: favCellVM,  favCellVM: favCellVM)
                
                
                
            }
        }
    }
    
    
    
    struct ExtractedView: View {
        @Binding var isVisible: Bool
        @Binding var selectedSong: Song?
        @ObservedObject var favoriteListVM2: FavoriteListVM
        
        @ObservedObject var favCellVM: FavoriteCellVM
        var isSelected: Bool { favCellVM.song.id == selectedSong?.id }
        
        
        var body: some View {
            Button(action: {
                
                print(isSelected)
                isVisible.toggle()
                if isSelected  {
                    selectedSong = nil
                    favoriteListVM2.audioPlayer?.stop()
                    
                } else {
                    selectedSong = favCellVM.song
                    isVisible = true
                    do {
                        favoriteListVM2.audioPlayer?.stop()
                        favoriteListVM2.audioPlayer = try AVAudioPlayer(data: favCellVM.song.songMp3!)
                        favoriteListVM2.audioPlayer?.prepareToPlay()
                        favoriteListVM2.audioPlayer?.play()
                    } catch let error {
                        print("\(error.localizedDescription)")
                    }
                    
                }
                
                
            }){     Image(systemName: isSelected ? "pause.fill" : "play.fill")
                .resizable()
                .frame(width: 25, height: 25, alignment: .center)
                .padding()
            }
            .buttonStyle(PlainButtonStyle())
        }
    }
}

//收藏夹

import Foundation
import SwiftUI
import CoreData
import AVFoundation
import Combine

class FavoriteRepository: ObservableObject, Identifiable {
    @Published var song = [Song]()
   
       
    @Environment(\.managedObjectContext) var managedObjectContext
    
    @FetchRequest(entity: Artists.entity(), sortDescriptors: []) var artists1: FetchedResults<Artists>
   
   
 
    init(){
        
        loadData()
    }
    
    
    
    func loadData() {
      

     
      

        let context = PersistenceManager.shared.container.viewContext
        let fetchRequest: NSFetchRequest<Artists>
        
        fetchRequest = Artists.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "isFavorite == %@", NSNumber(value: true))
        let objects = try! context.fetch(fetchRequest)
        song = objects.map {
                        artist in

            Song(album: artist.album!, artistImage: artist.artistImage, artistname: artist.artistname!, genre: artist.genre, songMp3: artist.songMp3, songname: artist.songname!, id: artist.id)
        
            }
 
        }
    }
        

//在 loremipsum 建议删除 ViewModel 和存储库后更新

import Foundation
import SwiftUI
import Combine
import AVFoundation

struct Favorites: View {
    
    @Binding var songLVM: SongListVM
   
    
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(entity: Artists.entity(), sortDescriptors: [], predicate: NSPredicate(format: "isFavorite == %@ ", NSNumber(value: true))) var artists1: FetchedResults<Artists>
   
    var body: some View {
        
        VStack {
            
            NavigationView {
                List {
                    
                    ForEach(artists1) {
                        artist in
                        
                        HStack {
                            Image(uiImage: UIImage(data: artist.artistImage ?? Data()) ?? UIImage())
                                .resizable()
                                .frame(width: 50, height: 50, alignment: .leading)
                                .scaledToFit()
                                .cornerRadius(10)
                            Spacer()
                            Text(artist.artistname ?? "no name")
                            Text(artist.songname ?? "no song name")
                            
                            //-
                            Button(action: {
                                
                                //    print(artist.song.id!)
                                print(artist.id!)
                                
                                if (artist.isFavorite == nil){
                                    artist.isFavorite = true
                                }
                                else if(artist.isFavorite == false) {
                                    artist.isFavorite = true
                                }
                                
                                else {
                                    artist.isFavorite = false
                                }
                                
                                
                                do {
                                    try managedObjectContext.save()
                                    
                                    print("done")
                                    //  print(result)
                                }
                                catch {
                                    print("\(error.localizedDescription)")
                                }
                                
                                
                                
                            }) {  Image(systemName: artist.isFavorite == true  ? "suit.heart.fill" : "suit.heart")
                                .resizable()
                                .frame(width: 25, height: 25, alignment: .center)
                                .padding()
                            }
                            .buttonStyle(PlainButtonStyle())
                            // --
                            Button(action: {
                                do {
                                        songLVM.audioPlayer?.stop()
                                        songLVM.audioPlayer = try AVAudioPlayer(data: artist.songMp3!)
                                        songLVM.audioPlayer?.prepareToPlay()
                                        songLVM.audioPlayer?.play()
                                        
                                    }
                                catch {
                                        print("\(error.localizedDescription)")
                                    }
                            }){ Image(systemName:  false ? "pause.fill" : "play.fill")
                                .resizable()
                                .frame(width: 25, height: 25, alignment: .center)
                                .padding()
                            }
                            .buttonStyle(PlainButtonStyle())
                            // --
                            
                        }
                        
                        
                    }
                    
                }
                .navigationTitle("Favorites")
                .font(.subheadline)
                
            }
}}}

【问题讨论】:

    标签: core-data swiftui


    【解决方案1】:

    首先使用 ViewModel 最好使用 @StateObject 而不是 @ObservedObject。

    所以问题是在您的 ViewModel 中,favCellVMs 没有更新到更改,它只在初始化时设置一次。

    只要不更新,就没有新的价值可以发布。

    如何解决:

    通常最喜欢的列表可以由用户在任何地方更改,因此将您的 SongRepository 写入如下内容:

    class SongRepository : ObservableObject {
        @Published var favSongs : [SongModel] = []
    }
    

    在您的应用视图中添加 (@main):

    @StateObject var songRepository = SongRepository
    

    并像这样将其传递给您的根视图:

    .environmentObject(songRepository)
    

    最后在你的标签中添加

    @EnvoirmentObject var songRepository : SongRepository
    

    现在您可以在任何地方从 songRepository.favSongs 表单中删除、添加和读取项目。

    结构收藏夹:查看{

    @EnvoirmentObject var songRepository : SongRepository
    @StateObject  var favListVM = FavoriteListVM()
    //@ObservedObject var repo = FavoriteRepository()
    //@Binding var favListVM1: FavoriteListVM
    
    
    
    var body: some View {
        
        VStack {
            
            NavigationView {
                List {
                    
                    ForEach(songRepository.favSongs) {
                        songCellVM in
                        
                     
                        SongCell(SongModel)
                    }
                } ....
    

    【讨论】:

    • 那是@StateObjectdeveloper.apple.com/documentation/swiftui/…的不正确初始化
    • 那是@ObservedObject的初始化不当和@Binding的不当使用
    • 哦,SongDataModel 不存在
    • 他应该创建一个结构来存储歌曲数据,如艺术家姓名、歌曲名称和......并将其命名为 SongModel。它是 mvvm 架构的一部分,它更干净。
    【解决方案2】:

    您的代码不是Minimal Reproducible Example,因此无法知道这是否能解决问题,但我看到了一些“错误”。

    首先,您应该只在 View 中使用 @StateObject initialize 和 ObservableObject,所以像这样更改所有具有 init 的代码

    @ObservedObject  var favListVM = FavoriteListVM()
    @ObservedObject var repo = FavoriteRepository()
    

    到

    @StateObject  var favListVM = FavoriteListVM()
    @StateObject var repo = FavoriteRepository()
    

    其次,ObservableObject 不应该是 @Binding,而应该是 StateObject、ObservedObject 或 EnvironmentObject。所以,

    @Binding var favListVM1: FavoriteListVM
    @Binding var favoriteListVM2: FavoriteListVM
    

    被滥用了。

    第三,您似乎使用了 2 个不同的 FavoriteListVM,其中一个看不到另一个在做什么。

    第一个实例是

    @Binding var favListVM1: FavoriteListVM
    

    第二个是

    @ObservedObject  var favListVM = FavoriteListVM()
    

    那么,你如何解决这个问题...

    在Favorites改

    @Binding var favListVM1: FavoriteListVM 
    

    到

    @ObservedObject var favListVM1: FavoriteListVM 
    

    然后删除@ObservedObject var favListVM = FavoriteListVM()

    并将对favListVM 的引用更改为favListVM1

    在SongCell 中将@Binding var favListVM1: FavoriteListVM 更改为@ObservedObject var favListVM1: FavoriteListVM

    在ExtractedView改

    @Binding var favoriteListVM2: FavoriteListVM
    

    到

    @ObservedObject var favoriteListVM2: FavoriteListVM
    

    另外,这个@Published var favRepository = FavoriteRepository() 不知道@ObservedObject var repo = FavoriteRepository() 在做什么。因此,如果您希望一个人知道另一个人在做什么,那么您将遇到断开连接。

    总结

    所有的变化加起来在每个View中只有一个FavoriteListVM。

    第一个FavoriteListVM 应该是@StateObject,它很可能在Favorites 的父视图中。除了FavoriteListVM 数组之外,应该在存储单元虚拟机时存储它。

    并且子视图中的所有后续引用都应该是@ObservedObject 而不是@Binding。

    每次初始化某些东西(例如FavoriteRepository() 和FavoriteListVM())时,您都在创建完全独立的不同实例。就像拥有两个人、两辆汽车、两座房子、两首歌曲等。坚持创建尽可能少的单个实例。

    旁注: 一旦你得到它的工作摆脱额外的变量,如

    @Binding var isVisible: Bool
    @Binding var selectedSong: Song?
    

    你有视图模型,单独引用它没有意义

    上次更新

    替换此行

    @Binding var songLVM: SongListVM
    

    有

    @ObservedObject var avManager: ArtistsAVManager
    

    当然,您必须对 SongListVM 进行更改才能看起来像这样

    //This class will keep track of everything to do with AVFoundation
    //Will replace SongListVM
    
    
    class ArtistsAVManager:ObservableObject{
        //You want this private because you dont want it modied on its own. Only via Play and Stop methods
        @Published private (set)var nowPlaying: Artists?
        @Published private (set)var status: Status = .stop
        @Published var alert: Alert?
        //Include other code you might have in SongListVM such as audioPlayer
        func play(artist: Artists){
            do {
                audioPlayer?.stop()
                audioPlayer = try AVAudioPlayer(data: artist.songMp3!)
                audioPlayer?.prepareToPlay()
                audioPlayer?.play()
                status = .play
                nowPlaying = artist
            }
            catch {
                print("\(error)")
                let nsError: NSError = error as NSError
                let message = "\(nsError.localizedDescription) \(nsError.localizedFailureReason ?? "") \(nsError.localizedRecoveryOptions?.first ?? "") \(nsError.localizedRecoverySuggestion ?? "")"
                alert = Alert(title: Text("Error: \(nsError.code)"), message: Text(message), dismissButton: .cancel())
                stop()
            }
            
            
        }
        
        func stop(){
            status = .stop
            audioPlayer?.stop()
            nowPlaying = nil
        }
        
        func pause(){
            status = .pause
            //Your code here
        }
        enum Status: String{
            case play
            case pause
            case stop
            
            func systemImageName() -> String{
                switch self {
                case .play:
                    return "play.fill"
                case .pause:
                    return "pause.fill"
                case .stop:
                    return "stop.fill"
                }
            }
        }
    }
    

    现在你的播放/暂停按钮看起来像这样

    Button(action: {
        if avManager.nowPlaying == artist{
            avManager.pause()
        }else{
            avManager.play(artist: artist)
        }
    }){ Image(systemName: (avManager.nowPlaying == artist && avManager.status != .pause) ? ArtistsAVManager.Status.pause.systemImageName() : ArtistsAVManager.Status.play.systemImageName())
            .resizable()
            .frame(width: 25, height: 25, alignment: .center)
            .padding()
    }
    .buttonStyle(PlainButtonStyle())
    

    如果你想添加一个停止按钮,你可以这样做

    if avManager.nowPlaying == artist && avManager.status != .stop{
        Button(action: {
            avManager.stop()
        }){ Image(systemName:  ArtistsAVManager.Status.stop.systemImageName())
                .resizable()
                .frame(width: 25, height: 25, alignment: .center)
                .padding()
        }
        .buttonStyle(PlainButtonStyle())
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-13
      • 1970-01-01
      • 2013-08-22
      • 2023-03-22
      • 2020-05-25
      • 2016-12-11
      相关资源
      最近更新 更多