无法在 SwiftUI 中重新呈现 CoreData 更新视图
Cannot rerender View on CoreData Update in SwiftUI
我能够更新收藏夹部分中的收藏夹列表,但只有在我重新启动应用程序后,我有多个答案建议添加 @ObservedObject var asset: Artists 等并添加托管对象上下文,我试过所有,但是收藏夹部分不会在核心数据更改时更新,任何人都可以提出解决此问题的方法,下面是文件的代码,我希望在核心数据更新后看到添加和显示的收藏夹,但目前是这个视图只有在我重新启动应用程序后才会更新。
代码已分为 SongCell 部分,显示每个单元格及其进一步提取的播放按钮。当我重新加载应用程序时,还会显示一张图片,以查看我在收藏夹中想要的内容 section.Thanks。
[![在此处输入图片描述][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)
}
}}}
您的代码不是 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()
在做什么。因此,如果您期望一个人知道另一个人在做什么,您就会遇到脱节。
总结
所有的变化加起来只有一个FavoriteListVM
在每个View
.
第一个 FavoriteListVM
应该是一个 @StateObject
,它可能在 Favorites
的父视图中。 FavoriteListVM
数组除外,它应该在存储单元 vms 时存储。
并且子视图中的所有后续引用都应该是 @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"
}
}
}
}
现在您的 play/pause 按钮看起来像这样
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())
}
首先,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)
}
} ....
我能够更新收藏夹部分中的收藏夹列表,但只有在我重新启动应用程序后,我有多个答案建议添加 @ObservedObject var asset: Artists 等并添加托管对象上下文,我试过所有,但是收藏夹部分不会在核心数据更改时更新,任何人都可以提出解决此问题的方法,下面是文件的代码,我希望在核心数据更新后看到添加和显示的收藏夹,但目前是这个视图只有在我重新启动应用程序后才会更新。
代码已分为 SongCell 部分,显示每个单元格及其进一步提取的播放按钮。当我重新加载应用程序时,还会显示一张图片,以查看我在收藏夹中想要的内容 section.Thanks。
[![在此处输入图片描述][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)
}
}}}
您的代码不是 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()
在做什么。因此,如果您期望一个人知道另一个人在做什么,您就会遇到脱节。
总结
所有的变化加起来只有一个FavoriteListVM
在每个View
.
第一个 FavoriteListVM
应该是一个 @StateObject
,它可能在 Favorites
的父视图中。 FavoriteListVM
数组除外,它应该在存储单元 vms 时存储。
并且子视图中的所有后续引用都应该是 @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"
}
}
}
}
现在您的 play/pause 按钮看起来像这样
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())
}
首先,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)
}
} ....