swiftios13uiwindowuiwindowscene

How to add a UIView object to the current application window in iOS 13


Here is what I'm trying to do:

When the user clicks a button on the current view controller - I want to present a UIView that takes up the whole screen - status bar and everything - and lies on top of the current view controller stack. Up until iOS 13, I was doing it this way:

var mainWindow = UIApplication.shared.keyWindow

mainWindow!.addSubview(self.temp_view2!

leadingConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: mainWindow, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1, constant: 0)
trailingConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: mainWindow, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1, constant: 0)
topConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: mainWindow, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1, constant: 0)
bottomConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: mainWindow, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1, constant: 0)
mainWindow?.addConstraints([topConstraint!, bottomConstraint!, leadingConstraint!, trailingConstraint!])

This approach doesn't seem to be working in iOS 13, I'm guessing because of the way UIWindow and UIScenes work - which I don't have a full understanding of yet. I'm only using a single window in my app. Using this SO post How to resolve: 'keyWindow' was deprecated in iOS 13.0 I tried to see if the following would work:

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

keyWindow?.addSubview(self.temp_view2!)

leadingConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.leading, relatedBy: NSLayoutConstraint.Relation.equal, toItem: keyWindow, attribute: NSLayoutConstraint.Attribute.leading, multiplier: 1, constant: 0)
trailingConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: keyWindow, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1, constant: 0)
topConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: keyWindow, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1, constant: 0)
bottomConstraint = NSLayoutConstraint(item: temp_view2, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: keyWindow, attribute: NSLayoutConstraint.Attribute.bottom, multiplier: 1, constant: 0)
keyWindow?.addConstraints([topConstraint!, bottomConstraint!, leadingConstraint!, trailingConstraint!]) 

The view does get added but it all the way in the back, behind the root view controller, I added a bringSubviewToFront line, but that doesn't help either. Here is how it looks - there are 3 views actually that I add as described above:

Screenshot - Debug View Hierarchy

How can I do this for >= iOS 13 ?


Solution

  • It looks like from your screen shot that the view is being added to the correct window; its just in the wrong z order. Without more context about where in your code you are adding this its impossible to say why that is (ie you could be creating the VC afterwards and new subviews always get pushed on top of the existing subviews unless you specify a z order). One very easy way to test if your z ordering is correct is to simply pull the view you added to the window to the front as soon as the VC you see in front appears (ie in viewWillAppear call window.bringSubviewToFront(viewThatsSupposedToBeInFront ).

    If you ALWAYS want your view to be on top of the current window, you can make another window with a higher window level onTopOfEveryThingWindow.windowLevel = .init(.greatestFiniteMagnitude). This can block out everything in the app including alerts until you hide the window.