Nasıl çözülür: 'keyWindow' iOS 13.0'da kullanımdan kaldırıldı


152

Cloud Kit ile Core Data kullanıyorum ve bu nedenle uygulama başlatılırken iCloud kullanıcı durumunu kontrol etmem gerekiyor. Sorun olması durumunda kullanıcıya bir iletişim kutusu yayınlamak istiyorum ve bunu UIApplication.shared.keyWindow?.rootViewController?.present(...)şimdiye kadar kullanarak yapıyorum .

Xcode 11 beta 4'te artık bana şunu söyleyen yeni bir kullanımdan kaldırma mesajı var:

iOS 13.0'da 'keyWindow' kullanımdan kaldırıldı: Tüm bağlı sahnelerde bir anahtar pencere döndürdüğü için birden çok sahneyi destekleyen uygulamalar için kullanılmamalıdır

Bunun yerine diyaloğu nasıl sunacağım?


İçinde mi yapıyorsun SceneDelegateyoksa AppDelegate? Ve çoğaltabilmemiz için biraz daha kod gönderebilir misiniz?
dfd

1
Artık iOS'ta 'keyWindow' kavramı yoktur, çünkü tek bir uygulamanın birden fazla penceresi olabilir. SceneDelegateSceneDelegate
Oluşturduğunuz

1
@Sudara: Öyleyse, henüz bir görüntüleme denetleyicim yoksa ve bir uyarı sunmak istiyorsam - bunu bir sahne ile nasıl yapabilirim? Sahne, rootViewController'ın alınabilmesi için nasıl alınır? (Kısacası, UIApplication için "paylaşılan" ile aynı Sahne nedir?)
Hardy

Yanıtlar:


112

Bu benim çözümüm:

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

Kullanım örneğin:

keyWindow?.endEditing(true)

5
Teşekkürler -
Hardy

Bu arada yaklaşımı çoklu sahne örneğiyle ( geliştirici.apple.com/ documentation/uikit/app_and_environment/…) test ettim ve hepsi beklendiği gibi çalıştı.
berni

1
Buradaki activationStatedeğeri test etmek de uygun olabilir foregroundInactive, bu benim testimde bir uyarı verilirse geçerli olacaktır.
Drew

1
@Drew test edilmelidir çünkü uygulama başlangıcında görünüm denetleyicisi zaten görülebilir ancak durum şu foregroundInactive
şekildedir

3
Bu kod benim için keyWindow = nil oluşturuyor. mattçözüm işe yarayandır.
Duck

227

Kabul edilen cevap ustaca olsa da aşırı derecede ayrıntılı olabilir. Tam olarak aynı sonucu çok daha basit bir şekilde elde edebilirsiniz:

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

Ayrıca, kullanımdan kaldırılmasının keyWindowaşırı ciddiye alınmaması gerektiği konusunda da uyarıyorum . Tam uyarı mesajı şunu okur:

iOS 13.0'da 'keyWindow' kullanımdan kaldırıldı: Tüm bağlı sahnelerde bir anahtar pencere döndürdüğü için birden çok sahneyi destekleyen uygulamalar için kullanılmamalıdır

Dolayısıyla, iPad'de birden çok pencereyi desteklemiyorsanız, devam edip kullanmaya devam etmenize hiçbir itiraz yoktur keyWindow.


Bunun gibi bir segmenti nasıl ele alırsınız let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController UIApplication.shared.keyWindow?.rootViewController = vcçünkü iOS 13 ve kart görünümünde bu bir sorun haline gelir, çünkü bir kullanıcı oturumu kapattıktan sonra, aşağı kaydırıp geri dönebilecekleri görünüm hiyerarşisindeki ana uygulama ile giriş ekranına itilir sorunludur.
Lukas Bimba

2
@Mario Windows dizisindeki ilk pencere değil. Windows dizisindeki ilk anahtar penceredir.
matt

1
@Mario Ama soru sadece bir sahne olduğunu varsayıyor. Çözülen sorun, yalnızca belirli bir mülkün kullanımdan kaldırılmasıdır. Açıkçası, iPad'de birden çok pencereniz varsa, hayat çok daha karmaşıktır! Gerçekten çok pencereli bir iPad uygulaması yazmaya çalışıyorsanız, size iyi şanslar.
matt

1
@ramzesenok Elbette daha iyi olabilirdi. Ama bu yanlış değil. Aksine, uygulamadan anahtar pencere olan bir pencere istemenin yeterli olabileceğini, böylece keyWindowmülkün kullanımdan kaldırılmasını önleyen ilk kişi bendim . Dolayısıyla olumlu oylar. Beğenmediyseniz, olumsuz oy verin. Ama başka birinin cevabına uyacak şekilde değiştirmemi söyleme; bu, dediğim gibi, yanlış olur.
matt

11
Bu artık şu şekilde de basitleştirilebilirUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

84

Matt'in mükemmel cevabını biraz geliştirerek, bu daha da basit, daha kısa ve daha zarif:

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

1
Teşekkür ederim! Bunu hedef c'de yapmanın bir yolu var mı?
Allenktv

1
@Allenktv Maalesef NSArraybir muadili yok first(where:). filteredArrayUsingPredicate:Ve ile tek satırlık bir metin oluşturmayı deneyebilirsiniz firstObject:.
pommy

1
@Allenktv kod yorumlar bölümünde karıştırıldı, bu yüzden aşağıda bir Objective-C eşdeğeri yayınladım.
user2002649

Xcode 11.2 derleyicisi bu yanıtla ilgili bir hata bildirdi ve parantez eklemeyi önerdi ve içeriği first(where:):UIApplication.shared.windows.first(where: { $0.isKeyWindow })
Yassine ElBadaoui

3
Bu artık şu şekilde de basitleştirilebilirUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

34

Geriye dönük uyumlu bir algılama yöntemi keyWindow:

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

Kullanım:

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

3
Bu en zarif cevap ve Swift'in ne kadar güzel olduğunu gösteriyor extension. 🙂
Clifton

1
Kullanılabilirlik kontrolleri beri, pek gerekli olan windowsve isKeyWindowiOS 2.0 beri var olmuştur ve first(where:)Xcode 9.0 / Swift beri 4/2017
pommy

1
UIApplication.keyWindowiOS 13.0'da
Vadim Bulavin


19

Bir Objective-C çözümü için

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

nullableBaşlık bildirimine eklemeyi unutmayın !
Ben Leggiero

10

Bir UIApplicationuzantı:

extension UIApplication {

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

}

Kullanım:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes

9

İdeal olarak, kullanımdan kaldırıldığı için, pencereyi SceneDelegate'de saklamanızı tavsiye ederim. Bununla birlikte, geçici bir çözüm istiyorsanız, bir filtre oluşturabilir ve keyWindow'u aynen bunun gibi alabilirsiniz.

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

Bu bir yorum yapma veya düzenleme olmalıdır mat cevabı değil, ayrı bir cevap
Ben Leggiero

5

Herhangi bir ViewController'da kullanmak istiyorsanız, o zaman kullanabilirsiniz.

self.view.window

4

bununla dene:

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

Bu bir yorum yapma veya düzenleme olmalıdır mat cevabı değil, ayrı bir cevap
Ben Leggiero

3

Bir Objective-C çözümü için de

@implementation UIWindow (iOS13)

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

@end

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

Ben de aynı problemle karşılaştım. newWindowBir görünüm için bir tane tahsis ettim ve ayarladım [newWindow makeKeyAndVisible]; Kullanmayı bitirdiğinizde, ayarlayın [newWindow resignKeyWindow]; ve ardından doğrudan orijinal anahtar penceresini göstermeye çalışın [UIApplication sharedApplication].keyWindow.

İOS 12'de her şey yolunda, ancak iOS 13'te orijinal anahtar penceresi normal şekilde gösterilemiyor. Tamamen beyaz bir ekran gösterir.

Bu sorunu şu şekilde çözdüm:

UIWindow *mainWindow = nil;
if ( @available(iOS 13.0, *) ) {
   mainWindow = [UIApplication sharedApplication].windows.firstObject;
   [mainWindow makeKeyWindow];
} else {
    mainWindow = [UIApplication sharedApplication].keyWindow;
}

Umarım yardımcı olur.


0

Cevabını esinlenerek Berni

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

0

Geliştiricilerin çoğu, bu kullanımdan kaldırmanın değiştirilmesinin Amaç C kodunu soruyor . KeyWindow'u kullanmak için aşağıdaki kodu kullanabilirsiniz.

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

Bu metodu AppDelegatesınıf metodu olarak oluşturdum ve sınıf içerisinde ekledim ve aşağıdaki gibi çok basit bir şekilde kullanıyorum.

[AppDelegate keyWindow];

Bu yöntemi AppDelegate.h sınıfına aşağıdaki gibi eklemeyi unutmayın.

+(UIWindow*)keyWindow;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.