วิธีแก้ไข: 'keyWindow' เลิกใช้แล้วใน iOS 13.0


142

ฉันใช้ Core Data กับ Cloud Kit ดังนั้นจึงต้องตรวจสอบสถานะผู้ใช้ iCloud ระหว่างการเริ่มต้นแอปพลิเคชัน ในกรณีที่มีปัญหาฉันต้องการส่งข้อความโต้ตอบไปยังผู้ใช้และฉันดำเนินการUIApplication.shared.keyWindow?.rootViewController?.present(...)จนถึงตอนนี้

ใน Xcode 11 beta 4 ตอนนี้มีข้อความการเลิกใช้งานใหม่บอกฉันว่า:

'keyWindow' เลิกใช้งานแล้วใน iOS 13.0: ไม่ควรใช้กับแอปพลิเคชันที่รองรับหลายฉากเนื่องจากจะส่งคืนหน้าต่างหลักในฉากที่เชื่อมต่อทั้งหมด

ฉันจะนำเสนอกล่องโต้ตอบแทนได้อย่างไร


คุณกำลังทำสิ่งนี้ในSceneDelegateหรือAppDelegate? และคุณช่วยโพสต์โค้ดอีกเล็กน้อยเพื่อให้เราทำซ้ำได้ไหม
dfd

1
ไม่มีแนวคิด 'keyWindow' ใน iOS อีกต่อไปเนื่องจากแอปเดียวสามารถมีได้หลายหน้าต่าง คุณสามารถจัดเก็บหน้าต่างที่คุณสร้างไว้ในของคุณSceneDelegate(ถ้าคุณใช้งานSceneDelegate)
Sudara

1
@ ซูดาระ: ถ้ายังไม่มีตัวควบคุมมุมมอง แต่ต้องการนำเสนอการแจ้งเตือน - จะทำอย่างไรกับฉาก? จะรับฉากได้อย่างไรเพื่อให้สามารถดึง rootViewController ได้? (เพื่อให้สั้น: ฉากที่เทียบเท่ากับ "แชร์" สำหรับ
Hardy

คำตอบ:


108

นี่คือทางออกของฉัน:

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

การใช้งานเช่น:

keyWindow?.endEditing(true)

5
ขอบคุณ - ไม่ใช่สิ่งที่ง่ายมากที่จะค้นหา ... 8-)
Hardy

ในขณะเดียวกันฉันได้ทดสอบวิธีการนี้กับตัวอย่างหลายฉาก ( developer.apple.com/documentation/uikit/app_and_environment/… ) และทั้งหมดได้ผลตามที่คาดไว้
berni

1
นอกจากนี้ยังอาจเป็นการเหมาะสมที่จะทดสอบactivationStateค่าforegroundInactiveที่นี่ซึ่งในการทดสอบของฉันจะเป็นกรณีนี้หากมีการแจ้งเตือน
Drew

1
@Drew ควรทดสอบเพราะในแอป start ตัวควบคุมมุมมองสามารถมองเห็นได้แล้ว แต่สถานะคือforegroundInactive
Gargo

3
รหัสนี้สร้าง keyWindow = ศูนย์สำหรับฉัน mattวิธีแก้ปัญหาคือวิธีที่ใช้ได้ผล
เป็ด

212

คำตอบที่ได้รับการยอมรับในขณะที่แยบยลอาจซับซ้อนเกินไป คุณจะได้รับผลลัพธ์ที่เหมือนกันทุกประการเพียงแค่:

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 = vcเพราะด้วย iOS 13 และมุมมองการ์ดสิ่งนี้จะกลายเป็นปัญหาเพราะผู้ใช้หลังจากพูดว่าออกจากระบบจะถูกผลักไปที่หน้าจอเข้าสู่ระบบด้วยแอพหลักในลำดับชั้นมุมมองที่พวกเขาสามารถปัดลงและกลับซึ่ง เป็นปัญหา
Lukas Bimba

2
@ มาริโอไม่ใช่หน้าต่างแรกในอาร์เรย์ของ windows เป็นคีย์หน้าต่างแรกในอาร์เรย์ของ windows
แมต

1
@ มาริโอ้ แต่คำถามสันนิษฐานว่ามีเพียงฉากเดียว ปัญหาที่กำลังแก้ไขเป็นเพียงการเลิกใช้งานคุณสมบัติบางอย่าง เห็นได้ชัดว่าชีวิตจะซับซ้อนกว่านี้มากถ้าคุณมีหลายหน้าต่างบน iPad! หากคุณกำลังพยายามเขียนแอป iPad หลายหน้าต่างขอให้คุณโชคดี
แมต

1
@ramzesenok แน่นอนว่ามันอาจจะดีกว่านี้ แต่ก็ไม่ผิดอะไร ในทางตรงกันข้ามฉันเป็นคนแรกที่แนะนำว่าอาจจะเพียงพอที่จะขอหน้าต่างที่เป็นหน้าต่างหลักของแอปพลิเคชันเพื่อหลีกเลี่ยงการเลิกใช้งานkeyWindowคุณสมบัติ ดังนั้นการโหวตขึ้น หากคุณไม่ชอบให้โหวตลงคะแนน แต่อย่าบอกให้ฉันเปลี่ยนให้ตรงกับคำตอบของคนอื่น ที่ฉันพูดจะผิด
แมต

11
ตอนนี้สามารถทำให้ง่ายขึ้นได้เช่นUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

77

ปรับปรุงคำตอบที่ยอดเยี่ยมของ 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

3
ตอนนี้สามารถทำให้ง่ายขึ้นได้เช่นUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

30

นี่คือวิธีตรวจจับที่เข้ากันได้แบบย้อนหลัง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
}

3
นี่คือคำตอบที่สวยหรูที่สุดและแสดงให้เห็นว่า Swift extensions สวยงามเพียงใด 🙂
Clifton Labrum

1
การตรวจสอบความพร้อมใช้งานแทบจะไม่จำเป็นเลยwindowsและisKeyWindowมีมาตั้งแต่ iOS 2.0 และfirst(where:)ตั้งแต่ Xcode 9.0 / Swift
4/2017

1
UIApplication.keyWindowได้เลิกใช้งานบน iOS 13.0: @available (iOS, แนะนำ: 2.0, เลิกใช้งาน: 13.0, ข้อความ: "ไม่ควรใช้สำหรับแอปพลิเคชันที่รองรับหลายฉากเนื่องจากจะส่งคืนหน้าต่างหลักในฉากที่เชื่อมต่อทั้งหมด")
Vadim Bulavin

19

โดยปกติจะใช้

สวิฟต์ 5

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

นอกจากนี้, ใน UIViewController:

self.view.window

view.window คือหน้าต่างปัจจุบันสำหรับฉาก

WWDC 2019: ป้อนคำอธิบายภาพที่นี่

คีย์ Windows

  • ติดตามหน้าต่างด้วยตนเอง

17

สำหรับโซลูชัน 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;
}

อย่าลืมเพิ่มnullableการประกาศส่วนหัว!
Ben Leggiero

10

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

9

ตามหลักการแล้วเนื่องจากเลิกใช้แล้วฉันขอแนะนำให้คุณเก็บหน้าต่างไว้ใน SceneDelegate อย่างไรก็ตามหากคุณต้องการวิธีแก้ปัญหาชั่วคราวคุณสามารถสร้างตัวกรองและเรียกค้น keyWindow ได้เช่นนี้

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

นี่ควรเป็นความคิดเห็นหรือแก้ไขคำตอบของ Mattไม่ใช่คำตอบแยกต่างหาก
Ben Leggiero



3

สำหรับโซลูชัน Objective-C ด้วย

@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

ฉันเจอปัญหาเดียวกัน ฉัน alloced 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;
}

หวังว่าจะช่วยได้



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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.