It might helpHowever, there are plenty of times where a view controller has no window. This happens when a view controller presents another full screen view controller. This can happened when the view controller is in a navigation controller and the view controller is not the top, visible view controller.
This requires a different approach to writefinding the view controller's scene. Ultimately you need to use a littlecombination of walking the responder chain and the view controller hierarchy until you find a path that leads to the scene.
The following extension will (may) get you a UIScene from a view or view controller. Once you have the scene, you can access its delegate.
Add 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
}
}
This can be called from any view or view controller to get its scene. But note that you can only get the scene from a view controller only after viewDidAppear
has been called at least once. If you try any sooner then the view controller may not yet be part of the view controller hierarchy.
This will work even if the window of the view of the view controller is nil as long as the view controller is part of a view controller hierarchy and somewhere up that hierarchy, it is attached to a window.
Here is an Objective-C implementation of the UIResponder extension:
UIResponder+Scene.h:
extension#import UIViewController<UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface {UIResponder (Scene)
@property (nonatomic, readonly, nullable) varUIScene sceneDelegate:*scene UISceneDelegate?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.viewwindow) {
return self.window?.windowScene?windowScene;
} else {
return self.delegatenextResponder.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