当我使用属性更新视图时,ObservedObject将重置。因此会导致数据丢失

时间:2019-12-22 20:04:14

标签: ios swift swiftui observableobject

我遇到一个问题,在更新视图时ViewModel会重新初始化。

我有两个视图SongListViewPlayerView,它们共享一个对象Player。当玩家的游戏状态更改为(isPlaying == true)时,viewModel中的SongListView会重置并变为空数组。因此,我视图上的列表变为空。

enter image description here

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 个答案:

答案 0 :(得分:3)

使用 Traceback (most recent call last): File "<ipython-input-48-dcb4b19c1bd3>", line 25, in <module> engine = sqlalchemy.create_engine(url) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\__init__.py", line 479, in create_engine return strategy.create(*args, **kwargs) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\strategies.py", line 61, in create entrypoint = u._get_entrypoint() File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\url.py", line 172, in _get_entrypoint cls = registry.load(name) File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\langhelpers.py", line 240, in load "Can't load plugin: %s:%s" % (self.group, name) NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:driver 而不是 Traceback (most recent call last): File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2276, in _wrap_pool_connect return fn() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 363, in connect return _ConnectionFairy._checkout(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 773, in _checkout fairy = _ConnectionRecord.checkout(pool) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 492, in checkout rec = pool._do_get() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\impl.py", line 139, in _do_get self._dec_overflow() File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\langhelpers.py", line 68, in __exit__ compat.reraise(exc_type, exc_value, exc_tb) File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\compat.py", line 153, in reraise raise value File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\impl.py", line 136, in _do_get return self._create_connection() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 308, in _create_connection return _ConnectionRecord(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 437, in __init__ self.__connect(first_connect_check=True) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 652, in __connect connection = pool._invoke_creator(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\strategies.py", line 114, in connect return dialect.connect(*cargs, **cparams) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\default.py", line 489, in connect return self.dbapi.connect(*cargs, **cparams) File "C:\Anaconda3\lib\site-packages\psycopg2\__init__.py", line 126, in connect dsn = _ext.make_dsn(dsn, **kwargs) File "C:\Anaconda3\lib\site-packages\psycopg2\extensions.py", line 175, in make_dsn parse_dsn(dsn) ProgrammingError: invalid dsn: invalid connection option "encoding" The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<ipython-input-53-3b57714b0ad8>", line 28, in <module> data_frame = pd.read_sql('select date_signed from edw.fpds.fpds_atom limit 5;', engine) File "C:\Anaconda3\lib\site-packages\pandas\io\sql.py", line 438, in read_sql chunksize=chunksize, File "C:\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1218, in read_query result = self.execute(*args) File "C:\Anaconda3\lib\site-packages\pandas\io\sql.py", line 1087, in execute return self.connectable.execute(*args, **kwargs) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2181, in execute connection = self._contextual_connect(close_with_result=True) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2242, in _contextual_connect self._wrap_pool_connect(self.pool.connect, None), File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2280, in _wrap_pool_connect e, dialect, self File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 1547, in _handle_dbapi_exception_noconnection util.raise_from_cause(sqlalchemy_exception, exc_info) File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\compat.py", line 398, in raise_from_cause reraise(type(exception), exception, tb=exc_tb, cause=cause) File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\compat.py", line 152, in reraise raise value.with_traceback(tb) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\base.py", line 2276, in _wrap_pool_connect return fn() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 363, in connect return _ConnectionFairy._checkout(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 773, in _checkout fairy = _ConnectionRecord.checkout(pool) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 492, in checkout rec = pool._do_get() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\impl.py", line 139, in _do_get self._dec_overflow() File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\langhelpers.py", line 68, in __exit__ compat.reraise(exc_type, exc_value, exc_tb) File "C:\Anaconda3\lib\site-packages\sqlalchemy\util\compat.py", line 153, in reraise raise value File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\impl.py", line 136, in _do_get return self._create_connection() File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 308, in _create_connection return _ConnectionRecord(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 437, in __init__ self.__connect(first_connect_check=True) File "C:\Anaconda3\lib\site-packages\sqlalchemy\pool\base.py", line 652, in __connect connection = pool._invoke_creator(self) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\strategies.py", line 114, in connect return dialect.connect(*cargs, **cparams) File "C:\Anaconda3\lib\site-packages\sqlalchemy\engine\default.py", line 489, in connect return self.dbapi.connect(*cargs, **cparams) File "C:\Anaconda3\lib\site-packages\psycopg2\__init__.py", line 126, in connect dsn = _ext.make_dsn(dsn, **kwargs) File "C:\Anaconda3\lib\site-packages\psycopg2\extensions.py", line 175, in make_dsn parse_dsn(dsn) ProgrammingError: (psycopg2.ProgrammingError) invalid dsn: invalid connection option "encoding" (Background on this error at: http://sqlalche.me/e/f405) 对我有用。

只要需要视图,标记为 @StateObject 的属性将保留其最初分配的 ObservedObject 实例,即使该结构被 SwiftUI 重新创建。

这允许您维护 ObservedObject 数据的状态。

答案 1 :(得分:2)

SwiftUI视图是结构,因此是不可变的。当您更新状态并导致视图重绘时,它实际上会创建视图的新实例。

您的SongListView中有

@ObservedObject var model: SongListViewModel = SongListViewModel()

这意味着每次重新绘制SongListView时(包括更改player.isPlaying的任何时间),您都在使用{{1}的新实例初始化model }。

您应该删除默认值,并通过参数将模型提供给SongListViewModel的初始化程序-

SongListView

答案 2 :(得分:0)

因此,最终我能够通过删除dirname(base_url()); 上的dirname(dirname(base_url())); 属性包装程序来解决此问题。我不确定为什么会这样。似乎一个视图中有多个@ObservedObject会导致此问题。现在我的代码如下:

player: Player

答案 3 :(得分:0)

改用@StateObject! ObservedObject 似乎每次都在重新创建整个对象。