web-dev-qa-db-ja.com

SceneDelegate xcode11に相当するUIApplication.shared.delegate?

SceneDelegate内にletプロパティを定義しました。いくつかのViewControllersがシーン内でそれにアクセスできるようにしたいと思います。

UIKitでは、次のようにApp Delegateプロパティにアクセスできます。

UIApplication.shared.delegate

次に、プロパティ名をキャストして指定します...

UIViewControllerのインスタンスからビューコントローラーが含まれているSceneDelegateへの参照を取得する同等のものはありますか?

26
zumzum

IOS 13以降、UIApplicationにはconnectedScenesプロパティがあり、これはSet<UIScene>。これらの各シーンにはdelegateがあり、これはUISceneDelegateです。したがって、その方法ですべてのデリゲートにアクセスできます。

シーンは1つ以上のウィンドウ(UIWindow)を管理でき、そのUISceneプロパティからウィンドウのwindowSceneを取得できます。

特定のビューコントローラーのシーンデリゲートが必要な場合は、次の点に注意してください。 UIViewControllerから、ビューからウィンドウを取得できます。窓からシーンを取得でき、シーンから代理オブジェクトを取得できます。

要するに、ビューコントローラーから、あなたは次のことができます:

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

ただし、ビューコントローラーにウィンドウがない場合はたくさんあります。これは、ビューコントローラーが別のフルスクリーンビューコントローラーを提示するときに発生します。これは、ビューコントローラーがナビゲーションコントローラー内にあり、ビューコントローラーが一番上に表示されるビューコントローラーではない場合に発生する可能性があります。

これには、ビューコントローラーのシーンを見つけるための別のアプローチが必要です。最終的には、シーンにつながるパスが見つかるまで、レスポンダーチェーンとView Controller階層を歩くことを組み合わせて使用​​する必要があります。

次の拡張機能は、ビューまたはビューコントローラーから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が少なくとも1回呼び出された後でないと、ビューコントローラからシーンを取得できないことに注意してください。すぐに試してみると、View ControllerがまだView Controller階層の一部になっていない可能性があります。

これは、ビューコントローラーのビューのウィンドウがnilの場合でも機能します。ただし、ビューコントローラーがビューコントローラー階層の一部であり、その階層のどこかにある場合は、ウィンドウに接続されています。


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
24
rmaddy

これを使用して動作させることができました:

let scene = UIApplication.shared.connectedScenes.first
if let sd : SceneDelegate = (scene?.delegate as? SceneDelegate) {
    sd.blah()
}
18
JoeGalind

JoeGalindありがとうございます私も同じように解決しました。

// 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 */
}
3
rkamiya87

これを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で使用しています。

0
malhal