应用委托访问环境对象

Posted

技术标签:

【中文标题】应用委托访问环境对象【英文标题】:App Delegate Accessing Environment Object 【发布时间】:2019-12-17 18:11:25 【问题描述】:

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

是否可以让 AppDeleagte 访问环境对象(例如,通过 AppDelegate().environmentobject(myClass),如果可以,在哪里?)还是有更好的方法来做到这一点?

简化代码:

包含播放列表标签的类以及更改播放列表和标签的功能

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)

    


AppDelegate

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)

        
    

【问题讨论】:

【参考方案1】:

这是可能的方法。假设您的 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())
...

取决于什么是可取的。

【讨论】:

如此简单,却是完美的答案。谢谢! 优雅简洁,非常感谢。比我想象的要容易。【参考方案2】:

解决此类问题的更好方法是,您需要使相同的实例全局可用,您应该使用Singleton 设计模式。此外,根据最佳编码实践,我们应该避免使用多个职责和变量重载 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() 将确保该类的其他实例不会再次创建。此外,它将始终显示最新的值。

【讨论】:

我更喜欢这种使用环境对象的方法——它似乎更干净,没有通过我的所有子视图提供环境对象。我必须在 AppDelegate let sharedMusicManager = MusicManager.sharedMusicManager 顶部添加这一行,并且必须在 MusicManager 类的开头引用 static let sharedMusicManager = MusicManager(),但随后完美运行。谢谢! @Priyal 如何在 AppDelegate 中访问 sharedMusicManager。似乎您只是在随机访问一个类的变量。这行不通。您究竟将 MusicManager 类放在哪里? @JoshuaSegal Musicmanager 是单独文件中的单独类。我没有使用任何类的变量。我正在访问 MusicManager 类的共享实例。

以上是关于应用委托访问环境对象的主要内容,如果未能解决你的问题,请参考以下文章

我想要一个委托对象访问主对象的私有字段

数据访问对象 (DAO) 是不是应该委托给其他 DAO 以提高可读性?

扩展 UIViewController 以访问应用程序委托是一种反模式吗?

在嵌套环境中点击 childviewcontroller 中的单元格访问父级父级视图控制器的 tableview 委托

快速访问应用程序委托变量延迟视图加载

如何:访问故事板中的服务对象?