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


120

Cloud Kit와 함께 Core Data를 사용하고 있으므로 응용 프로그램 시작 중에 iCloud 사용자 상태를 확인해야합니다. 문제가 발생하면 사용자에게 다이얼로그를 발행하고 싶은데 UIApplication.shared.keyWindow?.rootViewController?.present(...)지금까지 사용 하고 있습니다.

Xcode 11 베타 4에는 이제 다음과 같은 새로운 지원 중단 메시지가 있습니다.

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

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


SceneDelegate또는 에서이 작업을하고 AppDelegate있습니까? 그리고 복제 할 수 있도록 코드를 조금 더 게시 해 주시겠습니까?
dfd

1
단일 앱이 여러 개의 창을 가질 수 있으므로 iOS에는 더 이상 'keyWindow'개념이 없습니다. 당신은 당신의에서 만든 창을 저장할 수 SceneDelegate(당신이 사용하는 경우 SceneDelegate)
Sudara

1
@Sudara : 아직 뷰 컨트롤러가 없지만 경고를 표시하고 싶다면 장면으로 어떻게할까요? rootViewController를 검색 할 수 있도록 장면을 가져 오는 방법은 무엇입니까? (그래서, 짧게 만들려면 다음 UIApplication에 대해 "공유"로 장면에 해당 무엇인지?)
하디

답변:


98

이것이 내 해결책입니다.

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

사용 예 :

keyWindow?.endEditing(true)

4
감사합니다 - 알아 매우 직관적하지 뭔가 ... 8)
하디

한편 여러 장면 샘플 ( developer.apple.com/documentation/uikit/app_and_environment/… )을 사용 하여 접근 방식을 테스트 했으며 모두 예상대로 작동했습니다.
berni

1
여기 에서 activationState값 을 테스트하는 것도 적절할 수 있습니다 foregroundInactive. 내 테스트에서는 경고가 표시되는 경우에 해당됩니다.
Drew

1
@Drew 앱 시작시 뷰 컨트롤러가 이미 표시되지만 상태는foregroundInactive
Gargo

3
이 코드는 나를 위해 keyWindow = nil을 생성합니다. matt해결책은 작동하는 것입니다.
Duck

178

받아 들여지는 대답은 독창적이지만 지나치게 정교 할 수 있습니다. 훨씬 더 간단하게 정확히 동일한 결과를 얻을 수 있습니다.

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

또한의 지원 중단이 keyWindow지나치게 심각하게 받아 들여서 는 안된다는 점에주의하겠습니다 . 전체 경고 메시지는 다음과 같습니다.

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

따라서 iPad에서 여러 창을 지원하지 않는 경우 계속해서 사용하는 데 이의가 없습니다 keyWindow.


이와 같은 segue를 어떻게 처리할까요? let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController UIApplication.shared.keyWindow?.rootViewController = vciOS 13과 카드보기에서는 로그 아웃 후 사용자가보기 계층 구조에서 메인 앱을 사용하여 로그인 화면으로 푸시되어 아래로 스 와이프하고 돌아올 수 있기 때문에 이것이 문제가되기 때문입니다. 문제가 있습니다.
Lukas Bimba 19 년

1
@Mario Windows 배열의 첫 번째 창이 아닙니다. Windows 배열 의 첫 번째 창입니다.
매트

1
@Mario 그러나 질문은 하나의 장면 만 있다고 가정합니다. 해결되는 문제는 특정 속성의 사용 중단 일뿐입니다. iPad에 실제로 여러 개의 창이있는 경우 분명히 삶은 훨씬 더 복잡합니다! 정말 다중 창 iPad 앱을 작성하려는 경우 행운을 빕니다.
매트

1
@ramzesenok 물론 더 나을 수 있습니다. 그러나 그것은 잘못된 것이 아닙니다. 반대로, 애플리케이션에 키 창인 창을 요청하는 것으로 충분할 수 있음을 처음으로 제안하여 keyWindow자산 의 폐기를 피 했습니다. 따라서 찬성. 마음에 들지 않으면 반대 투표하십시오. 그러나 다른 사람의 답변과 일치하도록 변경하라고하지 마십시오. 내가 말했듯이 그것은 틀릴 것입니다.
매트

7
이제 다음과 같이 단순화 할 수도 있습니다.UIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

64

Matt의 탁월한 답변을 약간 개선하면 더 간단하고 짧고 우아합니다.

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

1
감사합니다! 객관적인 c에서 이것을 수행하는 방법이 있습니까?
Allenktv

1
@Allenktv 불행히도 NSArray에 해당이 없습니다 first(where:). filteredArrayUsingPredicate:및을 사용 하여 한 줄짜리를 작성해 볼 수 있습니다 firstObject:.
pommy

1
@Allenktv 댓글 섹션에서 코드가 엉망이되었으므로 아래에 Objective-C에 해당하는 내용을 게시했습니다.
user2002649

Xcode 11.2 컴파일러가이 답변에 오류를보고하고 괄호를 추가 할 것을 제안했으며 내용은 first(where:)다음 과 같습니다.UIApplication.shared.windows.first(where: { $0.isKeyWindow })
Yassine ElBadaoui

1
이제 다음과 같이 단순화 할 수도 있습니다.UIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

27

다음은 이전 버전과 호환되는 감지 방법입니다 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
}

2
이것은 가장 우아한 대답이며 Swift가 얼마나 아름다운지 보여줍니다 extension. 🙂
Clifton Labrum

1
가용성 점검 이후, 거의 필요 windowsisKeyWindow아이폰 OS 2.0부터 주변에 있고, first(where:)엑스 코드 9.0 / 스위프트 이후 4 / 2017
pommy

UIApplication.keyWindowiOS 13.0에서 더 이상 사용되지 않음 : @available (iOS, 도입 : 2.0, 더 이상 사용되지 않음 : 13.0, 메시지 : "연결된 모든 장면에서 키 창을 반환하므로 여러 장면을 지원하는 응용 프로그램에는 사용하지 않아야합니다.")
Vadim Bulavin

16

Objective-C 솔루션의 경우

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


10

이상적으로는 더 이상 사용되지 않기 때문에 SceneDelegate에 창을 저장하는 것이 좋습니다. 그러나 임시 해결 방법을 원하면 필터를 만들고 이와 같이 keyWindow를 검색 할 수 있습니다.

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

9

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

2

그것을 시도하십시오 :

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

2

Objective-C 솔루션의 경우도

@implementation UIWindow (iOS13)

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

@end

2

ViewController에서 사용하려면 간단히 사용할 수 있습니다.

self.view.window

1
이것은 나를 위해 일했습니다
Sanzio Angeli

1
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;
            }
        }
    }
}

1
- (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;
}

0

베르니 의 답변에서 영감을 얻음

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

0

많은 개발자들이이 지원 중단의 대체품에 대한 Objective C 코드를 요청합니다 . 아래 코드를 사용하여 keyWindow를 사용할 수 있습니다.

+(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;

-1

나는 같은 문제를 만났다. newWindow뷰에를 할당 하고 설정합니다. 사용 [newWindow makeKeyAndVisible]; 이 끝나면 설정 [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;
}

도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.