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)
}
}
}
}
}