UIApplication.shared.delegate equivalent for SceneDelegate xcode11?
As of iOS 13, UIApplication
has the connectedScenes
property which is Set<UIScene>
. Each of those scenes has a delegate
which is a UISceneDelegate
. So you could access all of the delegates that way.
A scene can manage one or more windows (UIWindow
) and you can get a window's UIScene
from its windowScene
property.
If you want the scene delegate for a specific view controller then note the following. From a UIViewController
you can get its window from its view. From the window you can get its scene and of course from the scene you can get its delegate.
In short, from a view controller, you can do:
let mySceneDelegate = self.view.window.windowScene.delegate
However, 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 finding the view controller's scene. Ultimately you need to use a combination 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:
#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
iOS - Calling SceneDelegate method from ViewController
UIApplication.shared.delegate
is AppDelegate
not SceneDelegate
You need
if let scene = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate {
// to do
}
Swift: application(_: open url: options:) not being called at all
Commented out the SceneDelegate
file and deleted ApplicationSceneManifest
in the info.plist
, restarted Xcode and it worked just fine.
Cannot access SceneDelegate class variable/property
The property you are trying to access is of type UISceneDelegate
.
So to get the value you need to cast the delegate property to SceneDelegate
like this:
if let sceneDelegate = self.view.window.windowScene.delegate as? SceneDelegate {
let variableToAccess = sceneDelegate.variableToAccess
print(variableToAccess)
}
Converting SceneDelegate Enviromental Object to AppMain
Yes, it's exactly as you have it in your example code, except that for some reason you commented it all out. So for instance:
class Thing : ObservableObject {
@Published var name = "Matt"
}
@main
struct SwiftUIApp: App {
@StateObject var thing = Thing()
var body: some Scene {
WindowGroup {
ContentView().environmentObject(thing)
}
}
}
Now in any View struct you just say
@EnvironmentObject var thing : Thing
and presto, you're observing from the global instance.
Related Topics
Get Button Pressed Id on Swift Via Sender
Command Compileswiftsources Failed With a Nonzero Exit Code Xcode 10
What Are the Rules for Spaces in Swift
Swift Write/Save/Move a Document File to Icloud Drive
Why Does Implicitly Unwrapped Optional Not Unwrap in Dictionary of Type [String:Any]
How to Convert Any Generic Numeric into a Double
Closure Use of Non-Escaping Parameter May Allow It to Escape
When to Use Foreach(_:) Instead of for In
Ios13 Navigation Bar Large Titles Not Covering Status Bar
Why Is the Shorthand Argument Name $0 Returning a Tuple of All Parameters
Reading Data into a Struct in Swift
How to Display Data from Firebase Faster
Init(Start:End:)' Is Deprecated: It Will Be Removed in Swift 3. Use the '..<' Operator
Swiftui: Can't Get the Transition of a Detailview to a Zstack in the Mainview to Work
Is Swift Dictionary of Indexed for Performance? Even for Exotic Types (Uuid)