当我使用 属性 更新视图时,ObservedObject 会重置。因此它会导致数据丢失
ObservedObject resets when I update the view using a property. Hence it leads to loss of data
我有一个问题,即 ViewModel
会在更新视图时重新初始化。
我有 2 个视图,SongListView
和 PlayerView
,它们共享 Player
的一个对象。当玩家的 Playing 状态改变 (isPlaying == true)
时,SongListView
中的 viewModel
重置并变为空数组。因此,我视图中的列表变为空。
歌曲列表视图:
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()
}
}
播放器视图:
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)
}
}
}
}
}
SwiftUI 视图是结构,因此是不可变的。当您更新状态并导致视图重绘时,它实际上创建了一个新的视图实例。
在你的SongListView
中你有
@ObservedObject var model: SongListViewModel = SongListViewModel()
这意味着每次重新绘制 SongListView
时(包括 player.isPlaying
更改的任何时间),您都在使用 [=] 的新实例初始化 model
16=].
您应该删除默认值并通过参数将模型提供给 SongListView
-
的初始化程序
@ObservedObject var model: SongListViewModel
所以最后我能够通过删除 player: Player
上的 @ObservedObject
属性 包装器来解决这个问题。我不确定为什么会这样。似乎在一个视图中有多个 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)
}
}
}
}
}
改用@StateObject! ObservedObject 似乎每次都在重新创建整个对象。
使用 @StateObject
而不是 @ObservedObject
对我有用。
只要视图需要,标记为 @StateObject
的 属性 将保留其最初分配的 ObservedObject 实例,即使该结构被 SwiftUI 重新创建。
这允许您维护 ObservedObject 数据的状态。
我有一个问题,即 ViewModel
会在更新视图时重新初始化。
我有 2 个视图,SongListView
和 PlayerView
,它们共享 Player
的一个对象。当玩家的 Playing 状态改变 (isPlaying == true)
时,SongListView
中的 viewModel
重置并变为空数组。因此,我视图中的列表变为空。
歌曲列表视图:
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()
}
}
播放器视图:
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)
}
}
}
}
}
SwiftUI 视图是结构,因此是不可变的。当您更新状态并导致视图重绘时,它实际上创建了一个新的视图实例。
在你的SongListView
中你有
@ObservedObject var model: SongListViewModel = SongListViewModel()
这意味着每次重新绘制 SongListView
时(包括 player.isPlaying
更改的任何时间),您都在使用 [=] 的新实例初始化 model
16=].
您应该删除默认值并通过参数将模型提供给 SongListView
-
@ObservedObject var model: SongListViewModel
所以最后我能够通过删除 player: Player
上的 @ObservedObject
属性 包装器来解决这个问题。我不确定为什么会这样。似乎在一个视图中有多个 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)
}
}
}
}
}
改用@StateObject! ObservedObject 似乎每次都在重新创建整个对象。
使用 @StateObject
而不是 @ObservedObject
对我有用。
只要视图需要,标记为 @StateObject
的 属性 将保留其最初分配的 ObservedObject 实例,即使该结构被 SwiftUI 重新创建。
这允许您维护 ObservedObject 数据的状态。