Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

View on top of everything: UIWindow subview VS UIViewController subview

In order to make a UIView on top of all views like the behavior of UIAlertView, the best way I see is by putting it in the app window, which is by adding the view to:

[[[UIApplication sharedApplication] delegate] window] 

However, the downside I found is that it doesn't automatically take in rotation. In order for it to take in rotation, the best way to do is by adding the view on current AppDelegate window.navigationController/rootViewController, however, by doing this it will no longer be on top of everything. Let say when view is displaying and there is a modalViewController popup, the modal view will certainly cover up the view.

My question is, is it possible to add subview to the top most window and support orientation? Do I need two set of Nib file in this case? How UIAlertView does the magic to combine the topmost & orientation support? When I look into the UIAlertView.h, I see there is actually 2 UIWindows, once is called originalWindow and another called dimWindow. Is there a place where I can find the source code for UIAlertView.m?

like image 889
Zian Chen Avatar asked Jan 08 '12 00:01

Zian Chen


People also ask

What is the difference between Uiwindow and UIView?

So think of a UIView as controlling part of the screen: drawing to it etc. A window is a mere container. The window does cover the entire screen (usually), the window is the root view in the view hierarchy (it is a UIView subclass).

How do I add a view in Uiwindow?

window; UIView *aView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)]; aView.


2 Answers

The best solution I've found is to create a transparent container view, add that container to the window, and place your alert inside the container. You may then register for UIApplicationWillChangeStatusBarOrientationNotification to receive rotation events and transform the container; this allows you to independently manipulate the frame of the alert:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  {      ...      self.container = [[UIView alloc] initWithFrame:self.window.bounds];      [self.container addSubview:self.alertView];      [self.window addSubview:self.container];      [[NSNotificationCenter defaultCenter] addObserver:self                                                selector:@selector(statusBarWillRotate:)                                                    name:UIApplicationWillChangeStatusBarOrientationNotification                                                  object:nil];      ... } ... - (void)statusBarWillRotate:(NSNotification *)theNotification {     CGFloat rotation = 0;     switch ([notification[UIApplicationStatusBarOrientationUserInfoKey] intValue])     {         case UIInterfaceOrientationLandscapeLeft:             rotation = -M_PI_2;             break;         case UIInterfaceOrientationLandscapeRight:             rotation = M_PI_2;             break;         case UIInterfaceOrientationPortraitUpsideDown:             rotation = M_PI;             break;         case UIInterfaceOrientationPortrait:          default:             rotation = 0;             break;     }     CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(rotation);     CGSize rotatedSize = CGRectApplyAffineTransform(self.window.bounds, rotationTransform).size;     [UIView animationWithDuration:[UIApplication sharedApplication].statusBarOrientationAnimationDuration                        animations:^{          self.container.transform = rotationTransform;          // Transform invalidates the frame, so use bounds/center          self.container.bounds = CGRectMake(0, 0, rotatedSize.width, rotatedSize.height);          self.container.center = CGPointMake(self.window.bounds.size.width / 2, self.window.bounds.size.height / 2);      }]; } 

You can, if you really want, create an entirely new window with its windowLevel property set to UIWindowLevelAlert, which will guarantee that the window remain above all other views, including the keyboard, but window management gets a littly tricky. The above solution should suffice for most needs.

The problem with simply adding a view controller's view to a window is that it is not guaranteed to receive all the rotation and view[Will|Did][Disa|A]ppear: messages unless it is added to the view controller hierarchy via addChildViewController: on the root view controller.

like image 188
Austin Avatar answered Sep 21 '22 21:09

Austin


Only the first subview of UIWindow gets told about orientation changes. (< iOS8)

Your question is very similar to this one:

Multiple views in a UIWindow

As the answer there says, you can register for notifications of orientation changes and handle relayout of your 'on top' subview that way.

like image 24
occulus Avatar answered Sep 20 '22 21:09

occulus