해결 방법: '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 }
:connectedScenes
iOS 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
는 씬()에 대한 .
키 창
- 수동으로 창 추적
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
}
}
이것을 원하는 곳에 놓을 수 있지만, 제가 개인적으로 추가했습니다.extension
UIViewController
.
이를 통해 표시할 확장 기능과 같은 더 유용한 확장 기능을 추가할 수 있습니다.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
'sourcecode' 카테고리의 다른 글
창의 차이점은 무엇입니까?로드됨 및 창.콘텐츠 렌더링 이벤트 (0) | 2023.05.09 |
---|---|
강조 표시된 상태에서 UI 단추의 배경색을 변경하는 방법은 무엇입니까? (0) | 2023.05.09 |
"ReferenceEquals(myObject, null)"이 "myObject == null"보다 더 나은 방법입니까? (0) | 2023.05.09 |
엑셀은 부동소수점 숫자가 부정확함에도 불구하고 어떻게 성공적으로 반올림합니까? (0) | 2023.05.09 |
병합을 미리 보려면 어떻게 해야 합니까? (0) | 2023.05.09 |