从 iOS 13 开始,UIApplication
有connectedScenes
财产是Set<UIScene>
。每个场景都有一个delegate
这是一个UISceneDelegate
。因此您可以通过这种方式访问所有代表。
一个场景可以管理一个或多个窗口(UIWindow
)你可以得到一个窗口UIScene
从它的windowScene
财产。
如果您想要特定视图控制器的场景委托,请注意以下事项。来自一个UIViewController
你可以从它的视图中获取它的窗口。从窗口中你可以得到它的场景,当然从场景中你可以得到它的委托。
简而言之,从视图控制器,您可以执行以下操作:
let mySceneDelegate = self.view.window.windowScene.delegate
然而,很多时候视图控制器没有窗口。当视图控制器呈现另一个全屏视图控制器时,就会发生这种情况。当视图控制器位于导航控制器中并且视图控制器不是顶部可见视图控制器时,可能会发生这种情况。
这需要采用不同的方法来查找视图控制器的场景。最终,您需要结合使用响应者链和视图控制器层次结构,直到找到通向场景的路径。
以下扩展将(可能)从视图或视图控制器获取 UIScene。获得场景后,您可以访问其委托。
添加 UIResponder+Scene.swift:
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