Cách giải quyết: 'keyWindow' không được dùng nữa trong iOS 13.0


120

Tôi đang sử dụng Core Data với Cloud Kit và do đó phải kiểm tra trạng thái người dùng iCloud trong quá trình khởi động ứng dụng. Trong trường hợp xảy ra sự cố, tôi muốn đưa ra một hộp thoại cho người dùng và tôi thực hiện nó UIApplication.shared.keyWindow?.rootViewController?.present(...)cho đến nay.

Trong Xcode 11 beta 4, hiện có một thông báo không dùng nữa, cho tôi biết:

'keyWindow' không được dùng nữa trong iOS 13.0: Không nên sử dụng cho các ứng dụng hỗ trợ nhiều cảnh vì nó trả về một cửa sổ chính trên tất cả các cảnh được kết nối

Thay vào đó, tôi sẽ trình bày hộp thoại như thế nào?


Bạn đang làm điều này trong SceneDelegatehoặc AppDelegate? Và, bạn có thể đăng thêm một đoạn mã để chúng tôi có thể nhân bản không?
dfd

1
Không có khái niệm 'keyWindow' trong iOS nữa vì một ứng dụng duy nhất có thể có nhiều cửa sổ. Bạn có thể lưu trữ các cửa sổ bạn tạo trong của bạn SceneDelegate(nếu bạn đang sử dụng SceneDelegate)
Sudara

1
@Sudara: Vì vậy, nếu tôi chưa có bộ điều khiển chế độ xem, nhưng muốn đưa ra một cảnh báo - làm thế nào để thực hiện với một cảnh? Làm cách nào để lấy khung cảnh để có thể truy xuất rootViewController của nó? (Vì vậy, nói ngắn gọn: Cảnh tương đương với "được chia sẻ" cho Ứng dụng UIA là gì?)
Hardy

Câu trả lời:


98

Đây là giải pháp của tôi:

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

Cách sử dụng, ví dụ:

keyWindow?.endEditing(true)

4
Cảm ơn - không phải cái gì đó là rất trực quan để tìm hiểu ... 8-)
Hardy

Trong khi đó, tôi đã thử nghiệm cách tiếp cận với mẫu nhiều cảnh ( developer.apple.com/documentation/uikit/app_and_enosystem/… ) và tất cả đều hoạt động như mong đợi.
berni

1
Cũng có thể thích hợp để kiểm tra activationStategiá trị foregroundInactiveở đây, trong thử nghiệm của tôi sẽ là trường hợp nếu một cảnh báo được đưa ra.
Drew

1
@Drew nó nên được kiểm tra vì khi khởi động ứng dụng, bộ điều khiển chế độ xem đã hiển thị nhưng trạng thái làforegroundInactive
Gargo

3
Mã này tạo keyWindow = nil cho tôi. mattgiải pháp là một trong những hoạt động.
Duck

180

Câu trả lời được chấp nhận, trong khi khéo léo, có thể quá phức tạp. Bạn có thể nhận được chính xác cùng một kết quả đơn giản hơn nhiều:

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

Tôi cũng xin lưu ý rằng không keyWindownên xem xét quá nghiêm túc việc không dùng nữa . Thông báo cảnh báo đầy đủ có nội dung:

'keyWindow' không được dùng nữa trong iOS 13.0: Không nên sử dụng cho các ứng dụng hỗ trợ nhiều cảnh vì nó trả về một cửa sổ chính trên tất cả các cảnh được kết nối

Vì vậy, nếu bạn không hỗ trợ nhiều cửa sổ trên iPad thì không có gì phản đối việc tiếp tục và tiếp tục sử dụng keyWindow.


Bạn sẽ xử lý như thế nào let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController UIApplication.shared.keyWindow?.rootViewController = vcvới một segue như thế này bởi vì với iOS 13 và chế độ xem thẻ, điều này trở thành một vấn đề vì người dùng sau khi đăng xuất sẽ được đẩy đến màn hình đăng nhập với ứng dụng chính trong hệ thống phân cấp chế độ xem nơi họ có thể vuốt xuống và quay lại. là có vấn đề.
Lukas Bimba

1
@Mario Đây không phải là cửa sổ đầu tiên trong mảng cửa sổ. Đây là cửa sổ khóa đầu tiên trong mảng cửa sổ.
matt

1
@Mario Nhưng câu hỏi giả định chỉ có một cảnh. Vấn đề đang được giải quyết chỉ là việc ngừng sử dụng một thuộc tính nhất định. Rõ ràng là cuộc sống phức tạp hơn nhiều nếu bạn thực sự có nhiều cửa sổ trên iPad! Nếu bạn thực sự đang cố gắng viết một ứng dụng iPad nhiều cửa sổ, chúc bạn may mắn.
matt

1
@ramzesenok Tất nhiên nó có thể tốt hơn. Nhưng nó không sai. Ngược lại, tôi là người đầu tiên đề xuất rằng có thể yêu cầu ứng dụng cung cấp một cửa sổ là cửa sổ khóa là đủ, do đó tránh được việc sử dụng thuộc keyWindowtính này. Do đó các phiếu ủng hộ. Nếu bạn không thích nó, hãy từ chối nó. Nhưng đừng bảo tôi thay đổi nó để phù hợp với câu trả lời của người khác; điều đó, như tôi đã nói, sẽ sai.
matt

7
Điều này bây giờ cũng có thể được đơn giản hóa thànhUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

64

Cải thiện một chút về câu trả lời xuất sắc của matt, câu trả lời này thậm chí còn đơn giản hơn, ngắn hơn và trang nhã hơn:

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

1
Cảm ơn bạn! Có cách nào để làm điều này trong mục tiêu c?
Allenktv

1
@Allenktv Thật không may là NSArraykhông có tương đương với first(where:). Bạn có thể cố gắng tạo một lớp lót với filteredArrayUsingPredicate:firstObject:.
pommy

1
@Allenktv mã bị sai trong phần nhận xét, vì vậy tôi đã đăng một Objective-C tương đương bên dưới.
user2002649

Trình biên dịch Xcode 11.2 đã báo lỗi với câu trả lời này và đề xuất thêm dấu ngoặc đơn và nội dung của nó vào first(where:):UIApplication.shared.windows.first(where: { $0.isKeyWindow })
Yassine ElBadaoui

1
Điều này bây giờ cũng có thể được đơn giản hóa thànhUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

27

Đây là một cách phát hiện tương thích ngược keyWindow:

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

Sử dụng:

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

2
Đây là câu trả lời thanh lịch nhất và chứng tỏ Swift đẹp như thế nào extension. 🙂
Clifton Labrum

1
Kiểm tra tính sẵn sàng là hầu như không cần thiết, vì windowsisKeyWindowđã được khoảng từ iOS 2.0, và first(where:)kể từ Xcode 9.0 / Swift 4 / 2017.
pommy

UIApplication.keyWindowđã không được dùng trên iOS 13.0: @available (iOS, được giới thiệu: 2.0, không được dùng: 13.0, thông báo: "Không nên được sử dụng cho các ứng dụng hỗ trợ nhiều cảnh vì nó trả về một cửa sổ chính trên tất cả các cảnh được kết nối")
Vadim Bulavin

16

Đối với một giải pháp 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

Tốt nhất, vì nó đã không còn được dùng nữa, tôi khuyên bạn nên lưu trữ cửa sổ trong SceneDelegate. Tuy nhiên, nếu bạn muốn một giải pháp tạm thời, bạn có thể tạo một bộ lọc và truy xuất keyWindow giống như thế này.

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

9

Một UIApplicationphần mở rộng:

extension UIApplication {

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

}

Sử dụng:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes

2

thử với điều đó:

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

2

Đối với một giải pháp Objective-C quá

@implementation UIWindow (iOS13)

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

@end

2

Nếu bạn muốn sử dụng nó trong bất kỳ ViewController nào thì bạn chỉ cần sử dụng.

self.view.window

1
Điều này làm việc cho tôi
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

Lấy cảm hứng từ câu trả lời của Berni

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

0

Nhiều nhà phát triển yêu cầu mã Objective C của sự thay thế không dùng nữa này. Bạn có thể sử dụng mã dưới đây để sử dụng keyWindow.

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

Tôi đã tạo và thêm phương thức này trong AppDelegatelớp dưới dạng một phương thức lớp và sử dụng nó với cách rất đơn giản như bên dưới.

[AppDelegate keyWindow];

Đừng quên thêm phương thức này trong lớp AppDelegate.h như bên dưới.

+(UIWindow*)keyWindow;

-1

Tôi đã gặp vấn đề tương tự. Tôi đã newWindowcấp phát một chế độ xem và đặt nó [newWindow makeKeyAndVisible]; Khi sử dụng xong, hãy đặt nó [newWindow resignKeyWindow]; và sau đó cố gắng hiển thị trực tiếp cửa sổ khóa gốc bằng [UIApplication sharedApplication].keyWindow.

Mọi thứ đều ổn trên iOS 12, nhưng trên iOS 13, cửa sổ khóa gốc không thể hiển thị bình thường. Nó hiển thị toàn bộ màn hình trắng.

Tôi đã giải quyết vấn đề này bằng cách:

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

Hy vọng nó giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.