应用代理访问环境对象

4

我有一个变量(描述播放列表的标签)在一个类中,我需要通过@EnvironmentObject状态在我的视图之间传递。当由一个视图调用更改该标签的函数(与变量在同一类中)时,其他视图中的变量也会更新。但是,当通知被触发时,AppDelegate也会调用该函数。目前,我已将包含标签的类声明为AppDelegate中的新实例,这导致视图/结构中的变量没有变化。

是否可以让AppDelegate访问环境对象(例如通过AppDelegate().environmentobject(myClass)),如果可以,那么应该在哪里进行?或者有更好的方法吗?

简化代码:

包含playlistLabel和更改播放列表及其标签函数的类

class MusicManager: NSObject, ObservableObject {

    var playlistLabel: String = ""

    func playPlaylistNow(chosenPlaylist: String?) {  
        playlistLabel = "Playlist: \(chosenPlaylist!)"
    }

}

首页视图显示标签

struct HomeView: View {

    @EnvironmentObject var musicManager: MusicManager

    var body: some View {

        Text(musicManager.playlistLabel)

    }

}

应用代理

class AppDelegate: UIResponder, UIApplicationDelegate, AVAudioPlayerDelegate {

    var musicManager: MusicManager = MusicManager()

        func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
            var playlistName: String = ""
            if let userInfo = notification.userInfo {
                playlistName = userInfo["soundName"] as! String
            }
            musicPlayerManager.playPlaylist(chosenPlaylist: playlistName)

        }
    }
2个回答

7

以下是可能的方法。假设您的AppDelegate有这样的属性:

var musicManager: MusicManager?

在您的 SceneDelegate 中,我假设您创建了 HomeView,您可以添加以下代码:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    let musicManager = MusicManager()
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        addDelegate.musicManager = musicManager
    }
    let contentView = HomeView().environmentObject(musicManager)
    ...

因此,AppDelegateHomeView都可以访问相同实例的MusicManager。或者反过来说。
...
var musicManager: MusicManager?
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
    musicManager = addDelegate.musicManager
}
let contentView = HomeView().environmentObject(musicManager ?? MusicManager())
...

根据个人喜好而定。


如此简单,却是完美的答案。谢谢! - Guilherme Crozariol
优雅简洁,非常感谢。比我想象中要容易。 - Luke
在上面的示例代码中,addDelegate被写了两次。难道不应该是appDelegate吗? - soundflix
在上面的示例代码中,addDelegate被写了两次。难道不应该是appDelegate吗? - undefined

4

解决这些需要全局访问同一实例的问题,最好的方法是使用单例模式。此外,根据最佳编码实践,我们应该避免在AppDelegate中负载过多的职责和变量。通过分离职责,将代码解耦始终是更好的选择。

class MusicManager: NSObject, ObservableObject {
   let sharedMusicManager = MusicManager()
   var playlistLabel: String = ""

   private init() {}

  func playPlaylistNow(chosenPlaylist: String?) {  
    playlistLabel = "Playlist: \(chosenPlaylist!)"
  }
}

AppDelegate

 class AppDelegate: UIResponder, UIApplicationDelegate, AVAudioPlayerDelegate {

     func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
        var playlistName: String = ""
        if let userInfo = notification.userInfo {
            playlistName = userInfo["soundName"] as! String
        }
        sharedMusicManager.playPlaylist(chosenPlaylist: playlistName)
    }
}

同样你可以从其他视图更新变量。保持一个 private init() 会确保该类的其他实例不会再次创建。此外,它将始终显示最新的值。

1
我更喜欢这种方法,而不是使用环境对象——不需要通过所有我的子视图传递环境对象,这样看起来更干净。我不得不在AppDelegate的顶部添加这一行let sharedMusicManager = MusicManager.sharedMusicManager,并且在MusicManager类的开头引用*static let sharedMusicManager = MusicManager()*,但随后完美地工作。谢谢! - Jack Vanderpump
@Priyal,你如何在AppDelegate中访问sharedMusicManager?看起来你只是随意访问一个类的变量。这样是行不通的。你把MusicManager类放在哪里了? - Joshua Segal
@JoshuaSegal MusicManager是一个单独的类,位于一个单独的文件中。我没有使用任何类的变量,而是访问MusicManager类的共享实例。 - Priyal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接