我已经在SceneDelegate中定义了出租房产.我希望一些ViewController能够在场景中访问它.

在UIKit中,我可以访问如下应用程序代理属性:

UIApplication.shared.delegate

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

从UIViewController的实例中获取对视图控制器所在的SceneDelegate的引用是否有一个类似功能?

推荐答案

截至iOS 13,UIApplication具有connectedScenes属性,即Set<UIScene>.每个场景都有一个delegate,也就是UISceneDelegate.所以你可以通过这种方式访问所有代表.

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

如果需要特定视图控制器的场景代理,请注意以下事项.从UIViewController中你可以看到它的窗口.从窗口可以看到它的场景,当然也可以从场景中看到它的代理.

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

let mySceneDelegate = self.view.window.windowScene.delegate

然而,很多时候视图控制器没有窗口.当一个视图控制器显示另一个全屏视图控制器时,就会发生这种情况.当视图控制器位于导航控制器中,而视图控制器不是顶部可见的视图控制器时,可能会发生这种情况.

这需要使用不同的方法来查找视图控制器的场景.最终,您需要结合使用漫游响应器链和视图控制器层次 struct ,直到找到通向场景的路径.

以下扩展将(可能)从视图或视图控制器获取UIScene.获得场景后,可以访问其代理.

添加UIResponder+场景.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次之后,才能从视图控制器获取场景.如果您尽快try ,则视图控制器可能尚未成为视图控制器层次 struct 的一部分.

即使视图控制器视图的窗口为零,只要视图控制器是视图控制器层次 struct 的一部分,并且在该层次 struct 上的某个位置,它被附加到窗口,这也会起作用.


以下是UIResponder扩展的Objective-C实现:

UIResponder+场景.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+场景.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

Swift相关问答推荐

传递给SWIFT ResultBuilder的闭包中结果类型的对象

SwiftUI - NavigationLink(value:)和navigationDestination不工作

为SwiftUI文本视图提供固定宽度,并仅在文本换行时使文本视图底部增大

为什么要在SWIFT RandomAccessCollection协议中重新定义元素类型?

如何才能在同一线程上调用类中的每个方法,而不对每个调用使用同步块?

';NSInternal不一致异常';,原因:';可见导航栏Xcode 15.0 Crash请求布局

相互取消任务

可以将属性名称传递给 Swift 中的函数吗?

减小SF符号的边框宽度

如何在 switch case 模式语句中使用 Swift 文字正则表达式?

如何为等待函数调用添加超时

Swift-SceneKit-无法从art.scnassets加载.scn文件

如何测试可选 struct 的协议一致性

如何裁剪图像 3:4 ?迅速

如何使用带有 span 样式标签的 Swift XMLParser

获取具有关联类型的协议的self

如何在 SwiftUI 中通过按钮点击一次为一个更改设置动画?

swift : 具有类型和值的枚举常量

在 Swiftui 中是否有一种简单的方法可以通过捏合来放大图像?

类型myViewController不符合 Swift 中的协议 UIPIckerDataSource