UIApplication.shared.delegate
那么,转换并指定属性名称...
从UIViewController的实例中获取对应视图控制器所在的SceneDelegate的引用,是否有相应的方法?
UIApplication.shared.delegate
那么,转换并指定属性名称...
从UIViewController的实例中获取对应视图控制器所在的SceneDelegate的引用,是否有相应的方法?
UIApplication
有一个 connectedScenes
属性,它是 Set<UIScene>
。每个场景都有一个 delegate
,它是一个 UISceneDelegate
。因此,您可以通过这种方式访问所有的代理。UIWindow
),您可以从其 windowScene
属性获取窗口的 UIScene
。UIViewController
中,您可以从其视图中获取其窗口。从窗口中,您可以获取其场景,当然也可以从场景中获取其代理。let mySceneDelegate = self.view.window.windowScene.delegate
import UIKit
@available(iOS 13.0, *)
extension UIResponder {
@objc var scene: UIScene? {
return nil
}
}
@available(iOS 13.0, *)
extension UIScene {
@objc override var scene: UIScene? {
return self
}
}
@available(iOS 13.0, *)
extension UIView {
@objc override var scene: UIScene? {
if let window = self.window {
return window.windowScene
} else {
return self.next?.scene
}
}
}
@available(iOS 13.0, *)
extension UIViewController {
@objc override var scene: UIScene? {
// Try walking the responder chain
var res = self.next?.scene
if (res == nil) {
// That didn't work. Try asking my parent view controller
res = self.parent?.scene
}
if (res == nil) {
// That didn't work. Try asking my presenting view controller
res = self.presentingViewController?.scene
}
return res
}
}
这个方法可以从任何视图或视图控制器中调用,以获取其场景。但请注意,在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
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
let scene = UIApplication.shared.connectedScenes.first
if let sd : SceneDelegate = (scene?.delegate as? SceneDelegate) {
sd.blah()
}
UIScene
,而不是包含 UIViewController
的 UIScene
父级。 - Pedro Paulo AmorimJoeGalind 谢谢你,我也是用类似的方法解决的。
// 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 */
}
如果您只是想查找窗口,则不需要引用 SceneDelegate
:
(UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first
仅当您的应用程序只包含一个场景和一个窗口时,才应使用此方法。
iOS 13+、Swift
场景表示应用程序 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"
}
}
currentScene
被设置为场景A。现在假设场景C中的一个视图控制器想要获取其代理。这段代码将返回场景A的代理,而不是场景C的代理。 - HangarRashclass SceneDelegate: UIResponder, UIWindowSceneDelegate {
static weak var shared: SceneDelegate?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
Self.shared = self
}
}
SceneDelegate.shared
来访问场景。
MasterViewController
中使用了这个:- (void)viewDidLoad {
[super viewDidLoad];
SceneDelegate *sceneDelegate = (SceneDelegate *)self.parentViewController.view.window.windowScene.delegate;
}
self.view.window
在此时为nil,因此我不得不到达已经加载并添加到窗口的父级。
如果您正在使用分割视图控制器并且场景委托是分割委托,则可以避免使用窗口/场景,只需执行以下操作:
SceneDelegate *sceneDelegate = (SceneDelegate *)self.splitViewController.delegate;
我在我的DetailViewController
中使用了这个方法。
Objective-C的版本:
NSSet<UIScene *> * sceneArr = [[UIApplication sharedApplication] connectedScenes];
UIScene * scene = [[sceneArr allObjects] firstObject];
NSObject * sceneDelegate = (NSObject *)scene.delegate;
// for example, try to get variable "window"
UIWindow *currentKeyWindow = [sceneDelegate valueForKey: @"window"];
NSLog(@"%@", currentKeyWindow);