I am upgrading my app to use the new UIScene
patterns as defined in iOS 13, however a critical part of the app has stopped working.
I have been using a UIWindow
to cover the current content on the screen and present new information to the user, but in the current beta I am working with (iOS + XCode beta 3) the window will appear, but then disappear straight away.
Here is the code I was using, that now does not work:
let window = UIWindow(frame: UIScreen.main.bounds)
let viewController = UIViewController()
viewController.view.backgroundColor = .clear
window.rootViewController = viewController
window.windowLevel = UIWindow.Level.statusBar + 1
window.makeKeyAndVisible()
viewController.present(self, animated: true, completion: nil)
I have tried many things, including using WindowScenes
to present the new UIWindow
, but cannot find any actual documentation or examples out there.
One of my attempts (Did not work - same behaviour with window appearing and dismissing straight away)
let windowScene = UIApplication.shared.connectedScenes.first
if let windowScene = windowScene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let viewController = UIViewController()
viewController.view.backgroundColor = .clear
window.rootViewController = viewController
window.windowLevel = UIWindow.Level.statusBar + 1
window.makeKeyAndVisible()
viewController.present(self, animated: true, completion: nil)
}
Has anyone been able to do this yet in iOS 13 beta?
Thanks
I was experiencing the same problems while upgrading my code for iOS 13 scenes pattern. With parts of your second code snippet I managed to fix everything so my windows are appearing again. I was doing the same as you except for the last line. Try removing viewController.present(...)
. Here's my code:
let windowScene = UIApplication.shared
.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first
if let windowScene = windowScene as? UIWindowScene {
popupWindow = UIWindow(windowScene: windowScene)
}
Then I present it like you do:
popupWindow?.frame = UIScreen.main.bounds
popupWindow?.backgroundColor = .clear
popupWindow?.windowLevel = UIWindow.Level.statusBar + 1
popupWindow?.rootViewController = self as? UIViewController
popupWindow?.makeKeyAndVisible()
Anyway, I personally think that the problem is in viewController.present(...)
, because you show a window with that controller and immediately present some 'self', so it depends on what 'self' really is.
Also worth mentioning that I store a reference to the window you're moving from inside my controller. If this is still useless for you I can only show my small repo that uses this code. Have a look inside AnyPopupController.swift
and Popup.swift
files.
Hope that helps, @SirOz
Based on all the proposed solutions, I can offer my own version of the code:
private var window: UIWindow!
extension UIAlertController {
func present(animated: Bool, completion: (() -> Void)?) {
window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIViewController()
window.windowLevel = .alert + 1
window.makeKeyAndVisible()
window.rootViewController?.present(self, animated: animated, completion: completion)
}
open override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
window = nil
}
}
How to use:
// Show message (from any place)
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Button", style: .cancel))
alert.present(animated: true, completion: nil)
Thank you @glassomoss. My problem is with UIAlertController.
I solved my problem in this way:
- I added a variable
var windowsPopUp: UIWindow?
- I modified the code to display the PopUp:
public extension UIAlertController {
func showPopUp() {
windowsPopUp = UIWindow(frame: UIScreen.main.bounds)
let vc = UIViewController()
vc.view.backgroundColor = .clear
windowsPopUp!.rootViewController = vc
windowsPopUp!.windowLevel = UIWindow.Level.alert + 1
windowsPopUp!.makeKeyAndVisible()
vc.present(self, animated: true)
}
}
- In the action of the UIAlertController I added:
windowsPopUp = nil
without the last line the PopUp is dismissed but the windows remains active not allowing the iteration with the application (with the application window)
You just need to store strong reference of UIWindow
that you want to present. It seems that under the hood view controller that presented does not references to the window.
iOS 13 broke my helper functions for managing alerts.
Because there may be cases where you need multiple alerts to be displayed at the same time (the most recent above the older) for example in case you're displaying a yes or no alert and in the meanwhile your webservice returns with a error you display via an alert (it's a limit case but it can happen),
my solution is to extend the UIAlertController like this, and let it have its own alertWindow to be presented from.
The pro is that when you dismiss the alert the window is automatically dismissed because there is any strong reference left, so no further mods to be implemented.
Disclaimer : I just implemented it, so I still need to see if it's consistent...
class AltoAlertController: UIAlertController {
var alertWindow : UIWindow!
func show(animated: Bool, completion: (()->(Void))?)
{
alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindow.Level.alert + 1
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
}
}
As everyone else mentioned, the issue is that a strong reference to the window is required. So to make sure that this window is removed again after use, I encapsulated everything needed in it's own class..
Here's a little Swift 5 snippet:
class DebugCheatSheet {
private var window: UIWindow?
func present() {
let vc = UIViewController()
vc.view.backgroundColor = .clear
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = vc
window?.windowLevel = UIWindow.Level.alert + 1
window?.makeKeyAndVisible()
vc.present(sheet(), animated: true, completion: nil)
}
private func sheet() -> UIAlertController {
let alert = UIAlertController.init(title: "Cheatsheet", message: nil, preferredStyle: .actionSheet)
addAction(title: "Ok", style: .default, to: alert) {
print("Alright...")
}
addAction(title: "Cancel", style: .cancel, to: alert) {
print("Cancel")
}
return alert
}
private func addAction(title: String?, style: UIAlertAction.Style, to alert: UIAlertController, action: @escaping () -> ()) {
let action = UIAlertAction.init(title: title, style: style) { [weak self] _ in
action()
alert.dismiss(animated: true, completion: nil)
self?.window = nil
}
alert.addAction(action)
}
}
And here is how I use it.. It's from the lowest view controller in the whole apps view hierarchy, but could be used from anywhere else also:
private let cheatSheet = DebugCheatSheet()
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
if motion == .motionShake {
cheatSheet.present()
}
}
Creating a variable for UIWindow
outside of function where you present the custom alert can solve this issue and yeah it's weird.
And you have to set that variable back to nil
来源:https://stackoverflow.com/questions/57060606/uiwindow-not-showing-over-content-in-ios-13