UIApplication.shared.delegate是否等效于SceneDelegate xcode11?

zum*_*zum 11 swift uiscenedelegate

我在SceneDelegate中定义了一个let属性。我希望某些ViewController能够在场景中访问它。

在UIKit中,我可以访问App Delegate属性,如下所示:

UIApplication.shared.delegate
Run Code Online (Sandbox Code Playgroud)

然后强制转换并指定属性名称...

是否有等效的方法可以从UIViewController的实例获取对视图控制器所在的SceneDelegate的引用?

rka*_*a87 23

JoeGalind 谢谢 我也用类似的方法解决了。

// iOS13 or later
if #available(iOS 13.0, *) {
    let sceneDelegate = UIApplication.shared.connectedScenes
        .first!.delegate as! SceneDelegate
    sceneDelegate.window!.rootViewController = /* ViewController Instance */

// iOS12 or earlier
} else {
    // UIApplication.shared.keyWindow?.rootViewController
    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    appDelegate.window!.rootViewController = /* ViewController Instance */
}
Run Code Online (Sandbox Code Playgroud)


rma*_*ddy 14

从iOS 13开始,UIApplication具有的connectedScenes属性Set<UIScene>。这些场景中的每一个都有一个delegate,即UISceneDelegate。因此,您可以通过这种方式访问​​所有代表。

一个场景可以管理一个或多个窗口(UIWindow),您可以UIScene从其windowScene属性中获取一个窗口。

如果您想要特定视图控制器的场景委托,请注意以下几点。从a UIViewController可以从其视图中获取其窗口。从窗口中可以获取其场景,当然也可以从场景中获取其委托。

简而言之,可以从视图控制器执行以下操作:

let mySceneDelegate = self.view.window.windowScene.delegate
Run Code Online (Sandbox Code Playgroud)

但是,很多时候视图控制器没有窗口。当视图控制器显示另一个全屏视图控制器时,会发生这种情况。当视图控制器在导航控制器中并且视图控制器不是顶部的可见视图控制器时,可能会发生这种情况。

这需要一种不同的方法来查找视图控制器的场景。最终,您需要结合使用遍历响应程序链和视图控制器层次结构的组合,直到找到通往场景的路径。

以下扩展将(可能)从视图或视图控制器中获取UIScene。拥有场景后,就可以访问其委托。

添加UIResponder + Scene.swift:

let mySceneDelegate = self.view.window.windowScene.delegate
Run Code Online (Sandbox Code Playgroud)

可以从任何视图或视图控制器中调用它来获取其场景。但是请注意,只有viewDidAppear在至少调用一次之后,才能从视图控制器获取场景。如果您尽早尝试,那么视图控制器可能还不属于视图控制器层次结构。

即使视图控制器的视图窗口为零,只要视图控制器是视图控制器层次结构的一部分,并且在该层次结构的某个位置,它也将附加到窗口,则该方法将起作用。


这是UIResponder扩展的Objective-C实现:

UIResponder + Scene.h:

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UIResponder (Scene)

@property (nonatomic, readonly, nullable) UIScene *scene API_AVAILABLE(ios(13.0));

@end

NS_ASSUME_NONNULL_END
Run Code Online (Sandbox Code Playgroud)

UIResponder + Scene.m:

#import "ViewController+Scene.h"

@implementation UIResponder (Scene)

- (UIScene *)scene {
    return nil;
}

@end

@implementation UIScene (Scene)

- (UIScene *)scene {
    return self;
}

@end

@implementation UIView (Scene)

- (UIScene *)scene {
    if (self.window) {
        return self.window.windowScene;
    } else {
        return self.nextResponder.scene;
    }
}

@end

@implementation UIViewController (Scene)

- (UIScene *)scene {
    UIScene *res = self.nextResponder.scene;
    if (!res) {
        res = self.parentViewController.scene;
    }
    if (!res) {
        res = self.presentingViewController.scene;
    }

    return res;
}

@end
Run Code Online (Sandbox Code Playgroud)


Sam*_*fes 12

SceneDelegate如果您只是想查找窗口,则不需要引用:

(UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first
Run Code Online (Sandbox Code Playgroud)

仅当您的应用程序只有一个场景和一个窗口时,才应使用此方法。


bso*_*sod 10

iOS 13+、斯威夫特

场景代表应用程序 UI 的实例,每个场景都维护自己的、完全独立的状态。应用程序本身永远只是一个实例,它维护对所有这些连接场景的引用。场景是类,因此它们是引用类型。因此,只需在连接时维护对场景本身的引用,然后在 内的连接场景集中找到该场景即可UIApplication.shared

// Create a globally-accessible variable of some kind (global
// variable, static property, property of a singleton, etc.) that
// references this current scene (itself).
var currentScene: UIScene?

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = scene as? UIWindowScene else {
            return
        }
        // Save the reference when the scene is born.
        currentScene = scene
    }
    
    func borat() {
        print("great success")
    }
}

// Here is a convenient view controller extension.
extension UIViewController {
    var sceneDelegate: SceneDelegate? {
        for scene in UIApplication.shared.connectedScenes {
            if scene == currentScene,
               let delegate = scene.delegate as? SceneDelegate {
                return delegate
            }
        }
        return nil
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        sceneDelegate?.borat() // "great success"
    }
}
Run Code Online (Sandbox Code Playgroud)


Kau*_*eya 9

我将 SceneDelegate 的引用存储在static weak属性中并将其初始化scene(:willConnectTo:options)

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    static weak var shared: SceneDelegate?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        Self.shared = self
    }
}
Run Code Online (Sandbox Code Playgroud)

稍后,可以使用以下方式访问场景SceneDelegate.shared


更新:正如评论中提到的,该解决方案仅适用于仅处理一个场景的应用程序。在 iPhone 上运行的应用程序通常只有一个场景。iPad 应用程序可以有多个场景。


Joe*_*ind 5

我能够使用它来使其工作:

let scene = UIApplication.shared.connectedScenes.first
if let sd : SceneDelegate = (scene?.delegate as? SceneDelegate) {
    sd.blah()
}
Run Code Online (Sandbox Code Playgroud)

  • 仅当您的应用程序有一个场景时,这才有效。支持场景的全部好处是您可以拥有多个场景。当有多个场景时,这样的代码将无法正常工作。 (4认同)
  • 这是不正确的,您将获得任何第一个连接的“UIScene”,而不是“UIViewController”的“UIScene”父级。 (4认同)
  • 不过,这不是正确的解决方案,因为您是从全局控制器获取值,而不是从当前的 UIScene 获取值。 (2认同)