sourcecode

해결 방법: 'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다.

copyscript 2023. 5. 9. 22:56
반응형

해결 방법: 'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다.

저는 Cloud Kit와 함께 Core Data를 사용하고 있기 때문에 애플리케이션을 시작하는 동안 iCloud 사용자 상태를 확인해야 합니다.을 띄우고 , 에게 대화창을 띄웁니다.UIApplication.shared.keyWindow?.rootViewController?.present(...)지금까지

Xcode 11 베타 4에서는 이제 다음과 같은 새로운 사용 중지 메시지가 표시됩니다.

'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다. 연결된 모든 장면에서 키 창을 반환하기 때문에 여러 장면을 지원하는 응용 프로그램에서는 사용하면 안 됩니다.

대신 대화 상자를 어떻게 표시해야 합니까?

편집 여기서 제안하는 내용은 iOS 15에서 더 이상 사용되지 않습니다.그럼 이제 어떻게 하죠?글쎄요, 앱에 여러 개의 창이 없다면, 현대적으로 받아들여지는 방법은 앱의 첫 번째 창을 얻는 것이라고 생각합니다.connectedScenes강제로 UI WindowScene으로 이동하고 첫 번째 창을 엽니다.하지그것거정의받확대다답니입진여아들히만은 은 다소 .그래서 이 시점에서 저의 해결책은 다소 미약하게 느껴집니다.하지만 역사적인 이유로 그냥 두겠습니다.


받아들여진 대답은 기발하지만 지나치게 정교할 수 있습니다.훨씬 더 간단하게 동일한 결과를 얻을 수 있습니다.

UIApplication.shared.windows.filter {$0.isKeyWindow}.first

나는 또한 주의할 것이 있습니다.keyWindow지나치게 심각하게 생각해서는 안 됩니다.전체 경고 메시지는 다음과 같습니다.

'keyWindow'는 iOS 13.0에서 더 이상 사용되지 않습니다. 연결된 모든 장면에서 키 창을 반환하기 때문에 여러 장면을 지원하는 응용 프로그램에서는 사용하면 안 됩니다.

하지 않는 해서 iPad를 하는 것에 .keyWindow.

iOS 16, iOS 15와 호환 가능

3년 후에도 이 스레드의 트래픽이 계속해서 발생하기 때문에, 저는 제가 생각하는 가장 우아한 솔루션을 현재의 기능을 공유하고자 합니다.또한 Swift와 함께 작동합니다.UI.

UIApplication
    .shared
    .connectedScenes
    .compactMap { ($0 as? UIWindowScene)?.keyWindow }
    .last

iOS 15 및 16, iOS 13과 호환 가능

UIApplication
    .shared
    .connectedScenes
    .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
    .last { $0.isKeyWindow }

:connectedScenesiOS 13 버전부터 할 수 있습니다. 전 야 하 경 우 을 는 해 지if #available(iOS 13, *)진술.

더 길지만 더 쉽게 이해할 수 있는 변형:

UIApplication
    .shared
    .connectedScenes
    .compactMap { $0 as? UIWindowScene }
    .flatMap { $0.windows }
    .last { $0.isKeyWindow }

iOS 13 및 14

의 역사적 15에서 하지만, 다음역답사에변만은유지여효합, 다하같이대야니어체되유로은다음의과적전히서▁the 되어야 합니다.UIApplication.shared.windows사용되지 않습니다.이것을 지적해 준 @matt에게 감사합니다!

원답:

매트의 훌륭한 답변을 약간 개선하면, 이것은 훨씬 더 간단하고, 짧고, 더 우아합니다.

UIApplication.shared.windows.last { $0.isKeyWindow }

2023년 4월 업데이트:

에서는 이답의이버다전선음택니다습했을은전변▁the다를 선택했습니다.first last키 창이 여러 개 있습니다.@TengL과 @Rob이 코멘트에서 지적했듯이, 이는 일관성 없는 동작으로 이어질 수 있습니다.더 나쁜 것은 iOS 13/14 솔루션이 다른 창 뒤에 숨길 수 있는 창을 선택한다는 것입니다.정확한 사양은 없지만 iOS 16/15 솔루션도 이러한 문제를 일으킬 수 있습니다.

따라서 선택한 키 창이 실제로 표시될 가능성을 높이기 위해 4가지 솔루션 변형을 모두 업데이트했습니다.이는 iOS에서 실행되는 대부분의 앱에 충분할 것입니다.할 수 . 되는 앱은 iPad를 할 수 .OS에서 앱을 보다 정확하게 제어할 수 있습니다. 특히 macOS에서 실행되는 앱은 다음과 같은 순서로 장면을 정렬하여 얻을 수 있습니다.activationState또는 사용자 정의 기능.

이것이 제 해결책입니다.

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .compactMap({$0 as? UIWindowScene})
        .first?.windows
        .filter({$0.isKeyWindow}).first

사용 예:

keyWindow?.endEditing(true)

인 탐지 방법입니다.keyWindow:

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}

용도:

if let keyWindow = UIWindow.key {
    // Do something
}

보통 사용

스위프트 5

UIApplication.shared.windows.filter {$0.isKeyWindow}.first

UIView 컨트롤러의 추가 기능:

self.view.window

view.window는 씬()에 대한 .

WWDC 2019: 여기에 이미지 설명 입력

키 창

  • 수동으로 창 추적

Objective-C 솔루션의 경우

+ (UIWindow *)keyWindow
{
    NSArray<UIWindow *> *windows = [[UIApplication sharedApplication] windows];
    for (UIWindow *window in windows) {
        if (window.isKeyWindow) {
            return window;
        }
    }
    return nil;
}

A UIApplication확장명:

extension UIApplication {

    /// The app's key window taking into consideration apps that support multiple scenes.
    var keyWindowInConnectedScenes: UIWindow? {
        return windows.first(where: { $0.isKeyWindow })
    }

}

용도:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes

이상적으로, 이 창은 더 이상 사용되지 않으므로 Scene Delegate에 저장하는 것이 좋습니다.그러나 임시 해결 방법이 필요한 경우 필터를 만들고 이와 같이 키 Windows(윈도우)를 검색할 수 있습니다.

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

iOS 13 이상을 지원합니다.

이전 버전과 동일한 을 참조하십시오.UIApplication.shared.keyWindow다음 확장자 만들기:

extension UIApplication {
    var mainKeyWindow: UIWindow? {
        get {
            if #available(iOS 13, *) {
                return connectedScenes
                    .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
                    .first { $0.isKeyWindow }
            } else {
                return keyWindow
            }
        }
    }
}

사용.

if let keyWindow = UIApplication.shared.mainKeyWindow {
    // Do Stuff
}

View 컨트롤러에서 사용하려면 를 사용하면 됩니다.

self.view.window

(Xcode 13.2.1에서 실행되는 iOS 15.2로 테스트됨)

extension UIApplication {
    
    var keyWindow: UIWindow? {
        // Get connected scenes
        return UIApplication.shared.connectedScenes
            // Keep only active scenes, onscreen and visible to the user
            .filter { $0.activationState == .foregroundActive }
            // Keep only the first `UIWindowScene`
            .first(where: { $0 is UIWindowScene })
            // Get its associated windows
            .flatMap({ $0 as? UIWindowScene })?.windows
            // Finally, keep only the key window
            .first(where: \.isKeyWindow)
    }
    
}

이 제시된 을 찾고 ,UIViewController 조로에.UIWindow 에 또 다른 여또있다니습기가 .extension유용한 정보:

extension UIApplication {
    
    var keyWindowPresentedController: UIViewController? {
        var viewController = self.keyWindow?.rootViewController
        
        // If root `UIViewController` is a `UITabBarController`
        if let presentedController = viewController as? UITabBarController {
            // Move to selected `UIViewController`
            viewController = presentedController.selectedViewController
        }
        
        // Go deeper to find the last presented `UIViewController`
        while let presentedController = viewController?.presentedViewController {
            // If root `UIViewController` is a `UITabBarController`
            if let presentedController = presentedController as? UITabBarController {
                // Move to selected `UIViewController`
                viewController = presentedController.selectedViewController
            } else {
                // Otherwise, go deeper
                viewController = presentedController
            }
        }
        
        return viewController
    }
    
}

이것을 원하는 곳에 놓을 수 있지만, 제가 개인적으로 추가했습니다.extensionUIViewController.

이를 통해 표시할 확장 기능과 같은 더 유용한 확장 기능을 추가할 수 있습니다.UIViewController를 들어더 쉽게: 게쉽예를들어더,들어,

extension UIViewController {
    
    func presentInKeyWindow(animated: Bool = true, completion: (() -> Void)? = nil) {
        DispatchQueue.main.async {
            UIApplication.shared.keyWindow?.rootViewController?
                .present(self, animated: animated, completion: completion)
        }
    }
    
    func presentInKeyWindowPresentedController(animated: Bool = true, completion: (() -> Void)? = nil) {
        DispatchQueue.main.async {
            UIApplication.shared.keyWindowPresentedController?
                .present(self, animated: animated, completion: completion)
        }
    }
    
}

사용해 보십시오.

UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController!.present(alert, animated: true, completion: nil)

이 권장 사항을 대체할 목적 C 코드를 요청하는 많은 개발자들.아래 코드를 사용하여 키 창을 사용할 수 있습니다.

+(UIWindow*)keyWindow {
    UIWindow        *windowRoot = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            windowRoot = window;
            break;
        }
    }
    return windowRoot;
}

는 이 를 이메드생추 에 만들어 했습니다.AppDelegate클래스 메소드로 클래스를 지정하고 아래와 같이 매우 간단한 방법으로 사용합니다.

[AppDelegate keyWindow];

아래와 같이 AppDelegate.h 클래스에 이 메서드를 추가하는 것을 잊지 마십시오.

+(UIWindow*)keyWindow;

Objective-C 솔루션의 경우에도 마찬가지입니다.

@implementation UIWindow (iOS13)

+ (UIWindow*) keyWindow {
   NSPredicate *isKeyWindow = [NSPredicate predicateWithFormat:@"isKeyWindow == YES"];
   return [[[UIApplication sharedApplication] windows] filteredArrayUsingPredicate:isKeyWindow].firstObject;
}

@end

베르니의 대답에 영감을 받았습니다.

let keyWindow = Array(UIApplication.shared.connectedScenes)
        .compactMap { $0 as? UIWindowScene }
        .flatMap { $0.windows }
        .first(where: { $0.isKeyWindow })

해결 방법:

let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first

아시다시피 여러 장면이 발생할 수 있기 때문에 키 창은 더 이상 사용되지 않습니다.가장 편리한 해결책은 다음을 제공하는 것입니다.currentWindow확장명으로 검색한 다음 대체합니다.

extension UIApplication {
    var currentWindow: UIWindow? {
        connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }
    }
}
NSSet *connectedScenes = [UIApplication sharedApplication].connectedScenes;
for (UIScene *scene in connectedScenes) {
    if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
        UIWindowScene *windowScene = (UIWindowScene *)scene;
        for (UIWindow *window in windowScene.windows) {
            UIViewController *viewController = window.rootViewController;
            // Get the instance of your view controller
            if ([viewController isKindOfClass:[YOUR_VIEW_CONTROLLER class]]) {
                // Your code here...
                break;
            }
        }
    }
}

버니의 코드는 좋지만 앱이 백그라운드에서 돌아오면 작동하지 않습니다.

내 코드는 다음과 같습니다.

class var safeArea : UIEdgeInsets
{
    if #available(iOS 13, *) {
        var keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
        // <FIX> the above code doesn't work if the app comes back from background!
        if (keyWindow == nil) {
            keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        }
        return keyWindow?.safeAreaInsets ?? UIEdgeInsets()
    }
    else {
        guard let keyWindow = UIApplication.shared.keyWindow else { return UIEdgeInsets() }
        return keyWindow.safeAreaInsets
    }
}
- (UIWindow *)mainWindow {
    NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
    for (UIWindow *window in frontToBackWindows) {
        BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
        BOOL windowIsVisible = !window.hidden && window.alpha > 0;
        BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal);
        BOOL windowKeyWindow = window.isKeyWindow;
        if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
            return window;
        }
    }
    return nil;
}

저는 다음과 같은 문제에 직면했습니다..foregroundActive.

여기 제 해결 방법이 있습니다.

public extension UIWindow {
    @objc
    static var main: UIWindow {
        // Here we sort all the scenes in order to work around the case
        // when no .foregroundActive scenes available and we need to look through
        // all connectedScenes in order to find the most suitable one
        let connectedScenes = UIApplication.shared.connectedScenes
            .sorted { lhs, rhs in
                let lhs = lhs.activationState
                let rhs = rhs.activationState
                switch lhs {
                case .foregroundActive:
                    return true
                case .foregroundInactive:
                    return rhs == .background || rhs == .unattached
                case .background:
                    return rhs == .unattached
                case .unattached:
                    return false
                @unknown default:
                    return false
                }
            }
            .compactMap { $0 as? UIWindowScene }

        guard connectedScenes.isEmpty == false else {
            fatalError("Connected scenes is empty")
        }
        let mainWindow = connectedScenes
            .flatMap { $0.windows }
            .first(where: \.isKeyWindow)

        guard let window = mainWindow else {
            fatalError("Couldn't get main window")
        }
        return window
    }
}

Scene 기반 앱 라이프사이클을 채택하도록 앱이 업데이트되지 않은 경우 활성 창 개체를 가져오는 또 다른 간단한 방법은 다음과 같습니다.UIApplicationDelegate:

let window = UIApplication.shared.delegate?.window
let rootViewController = window??.rootViewController

SwiftLint를 'first_where' 규칙과 함께 사용하고 있으며 전쟁을 침묵시키려면 다음과 같이 하십시오.

UIApplication.shared.windows.first(where: { $0.isKeyWindow })

목표 C 솔루션:

UIWindow *foundWindow = nil;
NSSet *scenes=[[UIApplication sharedApplication] connectedScenes];
NSArray *windows;
for(id aScene in scenes){  // it's an NSSet so you can't use the first object
    windows=[aScene windows];
    if([aScene activationState]==UISceneActivationStateForegroundActive)
         break;
}
for (UIWindow  *window in windows) {
    if (window.isKeyWindow) {
        foundWindow = window;
        break;
    }
}
 // and to find the parent viewController:
UIViewController* parentController = foundWindow.rootViewController;
while( parentController.presentedViewController &&
      parentController != parentController.presentedViewController ){
    parentController = parentController.presentedViewController;
}

iOS 15에서 작동하는 솔루션은 다음과 같습니다.

let window = (UIApplication.shared.connectedScenes.first as? UIWindowScene)?.windows.first

할했습다니를 했습니다.newWindow보기 위해, 그리고 그것을 설정합니다.[newWindow makeKeyAndVisible];용▁it설정▁set▁when면,▁it되료로 설정합니다.[newWindow resignKeyWindow];그리고 나서 원본 키를 직접 보여주려고 노력합니다.[UIApplication sharedApplication].keyWindow.

iOS 12에서는 모든 것이 정상이지만, iOS 13에서는 원래 키 창이 정상적으로 표시될 수 없습니다.전체 흰색 화면이 표시됩니다.

이 문제를 해결한 방법은 다음과 같습니다.

UIWindow *mainWindow = nil;
if ( @available(iOS 13.0, *) ) {
   mainWindow = [UIApplication sharedApplication].windows.firstObject;
   [mainWindow makeKeyWindow];
} else {
    mainWindow = [UIApplication sharedApplication].keyWindow;
}
let allScenes = UIApplication.shared.connectedScenes
                    let scene = allScenes.first { $0.activationState == .foregroundActive }
                    
                    if let windowScene = scene as? UIWindowScene {
                        windowScene.keyWindow?.rootViewController?.present(SFSafariViewController(url: url, configuration: conf), animated: isAnimated, completion: nil)
                    }
public extension UIApplication {
    func currentUIWindow() -> UIWindow? {
        let connectedScenes = UIApplication.shared.connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .compactMap { $0 as? UIWindowScene }
        
        let window = connectedScenes.first?
            .windows
            .first { $0.isKeyWindow }

        return window
    }
    
    static func setRootViewVC(_ viewController: UIViewController){
        UIApplication.shared.currentUIWindow()?.rootViewController = viewController
    }
}

그래서 우리는 사용할 수 있습니다.

func redirectingToMainScreen(){
    
    let mainVC = UIStoryboard.loadMainScreen()
    UIApplication.setRootViewVC(mainVC)
} 

iOS 16의 경우 다음을 사용했습니다.

let keyWindow = UIApplication.shared.currentUIWindow()?.windowScene?.keyWindow

언급URL : https://stackoverflow.com/questions/57134259/how-to-resolve-keywindow-was-deprecated-in-ios-13-0

반응형