Advantages, problems, examples of adding another UIWindow to an iOS app? - objective-c

Recently I've been wondering about the fact that that an iOS app only has one UIWindow.
It does not seem to be an issue to create another UIWindow and place it on screen.
My question is kind of vague, but I'm interested in:
What could I potentially achieve with a second UIWindow that cannot be done in other ways?
What can go wrong when using multiple UIWindow instances?
I have seen that people use a 2nd UIWindow to display popover like views on iPhone. Is this a good way of doing it? Why? Why not?
Are there other examples where it is making perfectly sense to have another UIWindow?
It's not that I'm missing something. I have never felt the need to create another UIWindow instance but maybe it would allow doing amazing things I'm not aware of! :-)
I'm hoping that it might help me solve this problem:
I need to add a "cover view" over whatever is currently displayed. It should also work if there are already one or more modal controllers presented. If I add a UIView to the root controller's view, the modal controllers sit on top, so do the popover controllers.
If I present the cover view modally and there is already a modal controller, only part of the screen is covered.

Starting with Rob's answer I played around a bit and would like to write down some notes for others trying to get information on this topic:
It is not a problem at all to add another UIWindow. Just create one and makeKeyAndVisible. Done.
Remove it by making another window visible, then release the one you don't need anymore.
The window that is "key" receives all the keyboard input.
UIWindow covers everything, even modals, popovers, etc. Brilliant!
UIWindow is always portrait implicitly. It does no rotate. You'll have to add a controller as the new window's root controller and let that handle rotation. (Just like the main window)
The window's level determines how "high" it gets displayed. Set it to UIWindowLevelStatusBar to have it cover everything. Set its hidden property to NO.
A 2nd UIWindow can be used to bring views on the screen that float on top of everything. Without creating a dummy controller just to embed that in a UIPopoverController.
It can be especially useful on iPhone where there is no popover controller but where you might want to mimic something like it.
And yes, it solved of course my problem: if the app resigns activation, add a cover window over whatever is currently shown to prevent iOS from taking a screenshot of your app's current content.

A UIWindow can float above other UI elements like the system keyboard.
To address your last paragraph: Make a UIWindow with the same frame as your main window. Set its windowLevel property to UIWindowLevelStatusBar. Set its hidden property to NO.

Here is Apple's Documentation for better understanding UIWindow:
https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/WindowAndScreenGuide/WindowScreenRolesinApp/WindowScreenRolesinApp.html
One good though specific reason to use multiple instances of UIWindow is when you need to video record the app screen. You may not want to include certain elements (recording button, recording status, etc.) in the final recorded video, so you can put those elements in a separate UIWindow on top.
In fact, if you are using ReplayKit, you will have to use a separate UIWindow for these excluded UI elements. More info here: https://medium.com/ar-tips-and-tricks/how-to-record-a-screen-capture-with-replaykit-whilst-hiding-the-hud-element-bedcca8e31e

Related

What really happens when a UIView becomes hidden?

This may sound like a weird question, but what exactly happens when it becomes hidden. It would be great to see the UIView source code, but that isn't going to happen.
Here why I'm wondering:
I'm trying to add a UIWindow (a transparent one with userinteractionenabled set to NO) above my application to tint the screen. It works perfectly fine until the user tries to share by SMS using Apples MessageUI.framework. When this happens and the MFMessageComposeViewController or MFMailComposeViewController appears, these view controllers won't receive user input. I've tried tons of thing and the only things that worked, allowing the user to interact with the views, was setting the UIWindow (the one I added) to either an alpha of 0 or set hidden to YES. I want to replicate this without hiding the view, which is why I want to know exactly what happens when the UIWindow (which is a subclass of UIView) is hidden.
There is usually only one window in iOS apps. You're better off using just a UIView for this task instead of a UIWindow. UIWindow adds some view hierarchy and event management capabilities to the UIView class. This functionality is interfering with the expected behavior in your app. I think it will just work if you change the class of this view to UIView instead of UIWindow.

What is the proper way to deal with changing a single UIView within a storyboard scene

Lets say I have a scene which includes a UIView container on the top half of the screen, and a UIView container on the bottom half of the screen and a few buttons at the very bottom of the screen.
Basically the bottom container will always display static text while the buttons across the bottom will change the content of the top container which may include an image, more buttons, or more text depending on what button is pressed on the bottom. Also each time a bottom button is pressed the top container is transitioned to the new view with a flip from bottom transition.
I have achieved this purely programmatically, but decided to convert my app to a storyboard file since it makes producing the rest of my app much faster and simpler, plus makes the code not look like a crazy mess.
My limited understanding of storyboards seems to deduce that I would need a separate story board scene for every UIView change, and Apple's coding conventions with storyboards seem to imply that we should use a new ViewController every time you create a new scene. All this adds up to an even bigger mess than I currently have.
Is there a better way of doing this? Am I misunderstanding something? If I am not confused, is there some way to make all these scene and view controller duplication cleaner?
The storyboard editor makes it difficult to do what you're describing, because it doesn't let you edit freestanding views associated with a scene.
I suggest you just create a separate nib (not storyboard) for each of the top-half views. These can exist separate from your storyboard. Your view controller (which is instantiated from the storyboard) can then load whichever nib it needs when a button is pressed, and put the view from the nib into its (the view controller's) top-level view.
There must be a way!
I accidentally opened one one day (see attached image). Although I have no idea how I did it and really really want to know, I cannot reproduce it, nor close it. The UIView opened when I was dragging my connection for the table header view from the Connections Inspector to the list of controls on the left side of the screen (not to the actual UIViewController).
I too am reworking a project with storyboards and have a similar problem with multiple views per UIViewController.
In this case it is a table header. I have other UIViewControllers in the project with the same configuration but I cannot get them to pop up either.

Manipulate UI from AppDelegate

I am a newbie in all this as will be apparent really soon.
I am using the iOS: Application: Tabbed application template. I have placed a UIImageView in the first view and two standard rounded buttons. One button is attached to an action in the FirstView Controller which places a picture into the UIImageView. The second button is attached to an action in the AppDelegate which calls a method in the FirstViewController which in turn places a second picture into the UIImageView.
The AppDelegate method does not replace the picture. It doesn't crash… it just does not seem to do anything.
How can I manipulate the view in the First and Second View Controller from the AppDelegate?
#dasdom
Well that's one issue explained. I've been reading the theory of MVC and trying to put it into practice now. Short version is I am trying to write a Battleship app for practice. Was planning on using the first screen to setup the game pieces, prefs, etc.. and use the second screen for actual game play.
I've created another class to use as my "brain center" but I ran into the same issue of not being able to manipulate anything on the screen for the first or second views. (That's why I tried the appDelegate).
That's my life story right now… can you throw some pointers my way on how to proceed and how to solve my one of many problems?
First you shouldn't do that. The AppDelegate should only be responsible for bringing the first view onto the screen.
Second you should have a look into the Model-View-Controller design pattern. Search for it in your preferred search machine.
But I you really still want to do that you should have a look into delegation and/or notifications. For example you could send the First View Controller a notification from the AppDelegate to change the image.

May I add a second subview to window if rootViewController is a UISplitViewController to simulate a modal effect?

I'm currently using UISplitViewController as my app's rootViewController. To present progress dialogs I use presentModalViewController, but for the one and other reason I'm not entirely happy with it and want to do my own modal thing.
If one of my modal views is supposed to be shown, I want to add another subview to my app's main window. This subview is going to be managed by its own UIViewController subclass to make it rotate properly and do all the stuff I need.
Is this design aproach okay or will I run into issues with UISplitViewController (it is very special in so many ways and seems to be offended easily if not treaten right! :-))?
Is it a problem to have two UIViewControllers next to each other?
Please don't discuss "why don't you use presentModalViewController then?".
You may be ok with UISplitViewController if you do it properly, as presentModalViewController does something similar.
One alternative you should look into is UIPopoverController and the UIViewController's modalIfPopover property.
Also, you say you aren't happy with presentModalViewController and perhaps if you say what was wrong with it we can help you work around whatever issues you have with it. This is the exact case that it seems to be meant for.

Can I use UITabBarController as a simple viewController switcher?

I'm creating an iPad app based on a UINavigationController (with the bar hidden) so I can push and pop other viewControllers for navigation around the app. However, I am now wanting to add a section in which there are two viewControllers that I want to be able to switch between, so in other words they are side-by-side, rather than hierarchical.
Is it okay to use a UITabBarController for this? I am aware that on the iPhone it is recommended they are used only at the root level of the app, but since this is an iPad app I wondered if I could use it? Also, I guess I need to create an empty viewController, create a UITabBarController within it and set the delegate to it, then add the two viewControllers to it... So in effect I will have a viewController within another viewController, and when I have done that in the past the results have been very flaky.
Can I do it this way? The only other way I can think of doing it is to have two plan UIViews within a UIViewController, but that also means I shouldn't really put any business logic in them (bad MVC!), and not being able to will be a right pain in the a**.
Any ideas?
Thanks!
:-Joe
EDIT: I also need to be able to swipe-animate between the two VCs within the TabBarController, AND have a menubar over the top which doesn't animate... Can I do this?
Sure.
I do this kind of thing all over the place in an app I'm working on. I actually have several different types of "toolbars" that can be optionally shown at different times.
What I do is create a "parent" member in my toolbar's class - and when a button is pressed, I have the toolbar call a method in the parent class to do whatever needs to be done - (i.e. display another view).
This avoids the whole mess of creating a view inside another view (or viewcontroller inside another viewcontroller - or whatever) - the toolbar can take the button hits, but all the views are opened by the root view/controller.
Hope this helps/makes sense!