ObservedObject сбрасывается, когда я обновляю представление с помощью свойства. Следовательно, это приводит к потере данных

У меня проблема, когда ViewModel будет повторно инициализирован при обновлении представления.

У меня 2 просмотра, SongListView а также PlayerView которые разделяют объект Player. Когда состояние игры игрока изменяется(isPlaying == true), то viewModel в SongListViewсбрасывается и становится пустым массивом. Из-за чего список на мой взгляд становится пустым.

SongListView:

struct SongListView: View {

    @ObservedObject var model: SongListViewModel = SongListViewModel() // This resets when player.isPlaying is set to true

    @ObservedObject var player: Player

    var body: some View {

        List(model.songs, id: \.id) { song in
            Button(action: {
                self.player.play(link: song.link)
            }) {
                TitleRowView(title: song)
            }
        }
        .onAppear {
            self.model.get()
        }
        .navigationBarTitle(Text("Songs"), displayMode: .inline)
    }
}

SongListViewModel:

class SongListViewModel: ObservableObject {

    @Published var songs: [Song] = [Song(id: 2, name: "ish", link: "ishm")] // When I tap the row, the songs var is re-initialized

    func get() {

        guard let url = URL(string: "apiPath") else { return }

        URLSession(configuration: URLSessionConfiguration.default).dataTask(with: url) {data, response, error 
               // Some more code
               self.songs = data
        }.resume()
    }
}

PlayerView:

struct PlayerView: View {

    @ObservedObject var player: Player

    var body: some View {
        HStack {
            Button(action: {
                if self.player.isPlaying {
                    self.player.pause()
                } else {
                    self.player.play()
                }
            }) {
                // This change causes the viewModel to reset to empty array
                if self.player.isPlaying { 
                    Image(systemName: "pause.fill")
                        .resizable()
                } else {
                    Image(systemName: "play.fill")
                        .resizable()
                }
            }
        }
    }
}

Игрок:

class Player : ObservableObject
{

    @Published var isPlaying: Bool = false

    private var player: AVPlayer?

    // This method is called when the user taps on a row in List
    func play(link: String) {
        guard let url = URL(string: link) else { return }
        let playerItem = AVPlayerItem(url: url)
        player = AVPlayer(playerItem: playerItem)
        player?.play()
        isPlaying = true // If I comment this line, the songs list in viewModel does not changes
    }    
}

Заранее спасибо!

ОБНОВЛЕНИЕ: все еще не работает

struct SongListView: View {

    @ObservedObject var model: SongListViewModel

    var body: some View {
        // View 
    }
}

struct CategoryListView: View {
    var categoryData : [Category]
    @ObservedObject var player: Player

    var body: some View {
        List(categoryData, id: \.id) { category in
            if category.id == 3 {
                NavigationLink(destination: SongListView(model: SongListViewModel(), player: self.player)) {
                    TitleRowView(title: category)
                }
            }

        }
    }
}

4 ответа

Решение

Представления SwiftUI являются структурами и, следовательно, неизменяемы. Когда вы обновляете состояние и вызываете перерисовку представления, он фактически создает новый экземпляр представления.

В вашем SongListView у тебя есть

@ObservedObject var model: SongListViewModel = SongListViewModel()

Это означает, что каждый раз, когда вы SongListView перерисовывается (включая любое время, когда player.isPlaying изменен), вы инициализируете model с новым экземпляром SongListViewModel.

Вы должны удалить значение по умолчанию и передать модель через параметр инициализатору SongListView -

@ObservedObject var model: SongListViewModel

Использование вместо @ObservedObject работал у меня.

Свойство, отмеченное как @StateObject сохранит свой изначально назначенный экземпляр ObservedObject до тех пор, пока требуется представление, даже если структура воссоздается с помощью SwiftUI.

Это позволяет вам поддерживать состояние ваших данных ObservedObject.

Вместо этого используйте @StateObject! Кажется, что ObservedObject каждый раз воссоздает весь объект целиком.

Итак, наконец, я смог исправить эту проблему, удалив @ObservedObject оболочка свойств на player: Player. Я не уверен, почему это работает. Похоже, что у них больше одногоObservedObjectв виду вызывает эту проблему. Теперь мой код выглядит так:

struct SongListView: View {

    @ObservedObject var model: SongListViewModel

    @State var player: Player

    var body: some View {

        List(model.songs, id: \.id) { song in
            Button(action: {
                self.player.play(link: song.link)
            }) {
                TitleRowView(title: song)
            }
        }
        .onAppear {
            self.model.get()
        }
        .navigationBarTitle(Text("Songs"), displayMode: .inline)
    }
}

struct CategoryListView: View {
    var categoryData : [Category]
    @ObservedObject var player: Player
    let viewModel = SongListViewModel()

    var body: some View {
        List(categoryData, id: \.id) { category in

            if category.id == 3 {
                NavigationLink(destination: SongListView(player: self.player).environmentObject(self.viewModel)) {
                    TitleRowView(title: category)
                }
            }

        }
    }
}
Другие вопросы по тегам