Измените порядок элементов To Many с помощью CoreData и SwiftUI - PullRequest
1 голос
/ 08 мая 2020

У меня есть отношение «один ко многим» в CoreData, которое представляет собой один список воспроизведения для многих песен, песни представляют собой упорядоченный набор в основном классе данных, и я сохраняю атрибут позиции с каждой песней, чтобы отслеживать порядок. Вот как я это реализовал:

Класс плейлиста:

extension Playlist {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Playlist> {
    return NSFetchRequest<Playlist>(entityName: "Playlist")
}

@NSManaged public var name: String?
@NSManaged public var numberOfSongs: Int16
@NSManaged public var songs: NSOrderedSet?

public var songsArray: [Song] {

    if let set = self.songs{

        let array = set.sortedArray { (song1, song2) -> ComparisonResult in
            guard let s1 = song1 as? Song, let s2 = song2 as? Song else {
                return ComparisonResult.orderedSame
            }
            if s1.position < s2.position {
                return ComparisonResult.orderedAscending
            } else if s1.position == s2.position {
                return ComparisonResult.orderedSame
            } else {
                return ComparisonResult.orderedDescending
            }
        }
        return array as! [Song]
    } else {
        return []
    }
 }

public var wrappedName: String {
    self.name ?? "Unknown Playlist Name"
}

Как видите, я сортирую песни по их атрибуту позиции, прежде чем возвращать их в массив для последующего просмотра и повторного заказа с помощью swiftUI.

Класс песни:

extension Song {

@nonobjc public class func fetchRequest() -> NSFetchRequest<Song> {
    return NSFetchRequest<Song>(entityName: "Song")
}

@NSManaged public var name: String?
@NSManaged public var position: Int16
@NSManaged public var playlist: Playlist?

public var wrappedName: String {
    self.name ?? "Unknown Playlist Name"
}

}

ContentView.swift:

struct ContentView: View {
@Environment(\.managedObjectContext) var moc
@FetchRequest(entity: Playlist.entity(), sortDescriptors: []) var playlists: FetchedResults<Playlist>

var body: some View {
    NavigationView {
         List {ForEach(playlists, id:\.self)
            { playlist in
                 NavigationLink(destination:
                 PlaylistView(songs: playlist.songsArray) ){
                    Text(playlist.wrappedName)
                 }
             }
            .onDelete(perform: deletePlaylist)
        }
         .navigationBarTitle("Playlists")
         .navigationBarItems(trailing:
            Button(action: {
                let newPlaylist = Playlist(context: self.moc)
                newPlaylist.name = "Playlist Name"

                let newSong1 = Song(context: self.moc)
                newSong1.name = "Song_1"
                newSong1.position = 0
                newPlaylist.addToSongs(newSong1)

                let newSong2 = Song(context: self.moc)
                newSong2.name = "Song_2"
                newSong2.position = 1
                newPlaylist.addToSongs(newSong2)

                newPlaylist.numberOfSongs = 2

                try? self.moc.save()

        }) {
            Image(systemName: "plus").imageScale(.large)
        })
    }
}

func deletePlaylist(offsets: IndexSet) {
    for index in offsets {
        let playlist = playlists[index]
        moc.delete(playlist)
    }
    do {
        try moc.save()
    } catch {
        print(error)
    }
}

}

PlaylistView:

struct PlaylistView: View {
@State var songs: [Song]
@Environment(\.managedObjectContext) var moc

var body: some View {
    List { ForEach(self.songs, id: \.self)
        { song in
            Text(song.wrappedName)
        }
        .onMove(perform: move)
    }
    .navigationBarTitle(Text(self.songs[0].playlist!.wrappedName))
    .navigationBarItems(trailing:
        Button(action: {

    }) {
        EditButton()
    })

}

func move(from source: IndexSet, to destination: Int) {
    self.songs.move(fromOffsets: source, toOffset: destination)
    var counter: Int16 = 0
    for song in self.songs {
        song.position = counter
        counter += 1
    }

    try! self.moc.save()
}

}

Теперь в PlaylistView, когда я переупорядочиваю массив песен, я обновляю атрибуты позиции песен до отражать новый порядок (в move ()), но теперь, когда я возвращаюсь из PlaylistView обратно в ContentView, а затем go обратно в PlaylistView, песни находятся не в новом порядке. Однако, если я перезапущу приложение и введу go в список воспроизведения, песни будут расположены в правильном новом порядке.

Насколько я понимаю, каждый раз, когда я нажимаю на NavigationLink для PlaylistView, playlist.songsArray должен захватывать массив из атрибута класса Playlist, в котором я выполняю сортировку, поэтому я не понимаю, почему песни в PlaylistView арены не отсортировано.

Любая помощь будет принята с благодарностью!

Посмотрите проект xcode здесь

1 Ответ

0 голосов
/ 08 мая 2020

Это потому, что ваша Playlist сущность не знает, что Songs нужно изменить. Сделайте исправление, как показано здесь , и ваш @fetchRequest получит уведомление и refre sh результат.

  1. make en extension для отправки уведомления objectWillChange, когда вы меняете объект

    extension Playlist{
        override public func willChangeValue(forKey key: String) {
            super.willChangeValue(forKey: key)
            self.objectWillChange.send()
        }
    }
    
  2. вы обмели вручную отправляете уведомление об изменении списка воспроизведения, когда начинается переупорядочивание песен:

    song.playlist?.ObjectWillChange.send()
    

    но лучше сгруппировать песни, чтобы отправить одну уведомление для одного действия, а не в l oop для каждой песни

...