UIWindow in iOS

这篇文章,我将分享对UIWindow我所知道的东西。

keyWindow

一个应用能够有许多UIWindow,“The key window”是其中一个,被设计用来接受键盘和其他与点击无关的事件。一个时间段中,如果只有一个Window,那么他就可能是keyWindow。

调用makeKeyAndVisible或者makeKeyWindow,能够使一个UIWindow变成keyWindow。注意,默认情况下,UIWindow是隐藏的。所以调用makeKeyAndVisible,即使一个UIWindow变成了keyWindow,又把它的hidden属性设为NO。

UIWindow is always portrait

在application:didFinishLaunchingWithOptions:中增加以下代码:

1 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
2     view.backgroundColor = [UIColor greenColor];
3     view.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
4     [self.window addSubview:view];
5     view.layer.zPosition = FLT_MAX;

接着,旋转模拟器或者设备,你将会看到绿色视图总在坐标点{0, 0}。

UIWindow’s的坐标系统总是在竖直方向,旋转是通过设置根控制器的位移。所以,自从iOS4开始,应用建议有根控制器。

Keyboard is a window

注意,UIApplication有一个“windows”的实例化方法。

The app’s visible and hidden windows. (read-only)
This property contains the UIWindow objects currently associated with the app. This list does not include windows created and managed by the system, such as the window used to display the status bar.

你能够获取keyboardwindow通过遍历windows array。摘录于SVProgressHUD中得代码。注意,当keyboard显示出来时,window array中包含一种UITextEffectsWindow类型的window。

 1 - (CGFloat)visibleKeyboardHeight {
 2
 3     UIWindow *keyboardWindow = nil;
 4     for (UIWindow *testWindow in [[UIApplication sharedApplication] windows]) {
 5         if(![[testWindow class] isEqual:[UIWindow class]]) {
 6             keyboardWindow = testWindow;
 7             break;
 8         }
 9     }
10
11     for (__strong UIView *possibleKeyboard in [keyboardWindow subviews]) {
12         if([possibleKeyboard isKindOfClass:NSClassFromString(@"UIPeripheralHostView")] || [possibleKeyboard isKindOfClass:NSClassFromString(@"UIKeyboard")])
13             return possibleKeyboard.bounds.size.height;
14     }
15
16     return 0;
17 }

之前说过,UIWindow的坐标系统总是横屏的,Keyboard也是一个window,所以不管设备是的怎么样的方向,他的frame总是 (0 0; 320 480)。当你旋转设备时,系统发送一个旋转位移消息给keyboard window。

设备旋转到竖屏:

<UITextEffectsWindow: 0x8e3ecc0; frame = (0 0; 320 480); opaque = NO; gestureRecognizers = <NSArray: 0x8e3f240>; layer = <UIWindowLayer: 0x8e3ee40>>

设备旋转到横屏:

<UITextEffectsWindow: 0x8e3ecc0; frame = (0 0; 320 480); transform = [0, 1, -1, 0, -80, 80]; opaque = NO; gestureRecognizers = <NSArray: 0x8e3f240>; layer = <UIWindowLayer: 0x8e3ee40>>
Notification

如果你注册了UIKeyboardWillShowNotification,你能够确认的是:

1 [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
2         NSLog(@"%@", note);
3     }];

设备旋转到竖屏:

UIKeyboardFrameEndUserInfoKey = “NSRect: {{0, 264}, {320, 216}}”;

设备旋转到横屏:

UIKeyboardFrameEndUserInfoKey = “NSRect: {{0, 0}, {162, 480}}”;

所以,在你UIKeyboardWillShowNotification接受后,你应该把他转变成你View的坐标系统。代码摘录于Keyboard “WillShow” and “WillHide” vs. Rotation

 1 - (void) keyboardWillShow:(NSNotification *)aNotification
 2 {
 3      CGRect keyboardFrame = [[[aNotification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
 4     CGRect convertedFrame = [self.view convertRect:keyboardFrame fromView:self.view.window];
 5
 6     ......
 7     /* Do whatever you want now with the new frame.
 8      * The width and height will actually be correct now
 9      */
10     ......
11 }

Statusbar is a window

之前说过,windows array不包含the statusbar window,the statusbar window是UIStatusBarWindow类型的,你能够通过以下代码获取。

- (UIWindow *)statusWindow
{
    NSString *statusBarString = [NSString stringWithFormat:@"_statusBarWindow"];
    return [[UIApplication sharedApplication] valueForKey:statusBarString];
}

就像 the keyboard window, the status bar的大小不会改变,当你旋转屏幕,系统会将旋转消息发送给状态栏。

设备旋转到竖屏:

1 <UIStatusBarWindow: 0x8f61e30; frame = (0 0; 320 480); gestureRecognizers = <NSArray: 0x8f62e40>; layer = <UIWindowLayer: 0x8f620d0>>

设备旋转到横屏:

1 <UIStatusBarWindow: 0x8f61e30; frame = (0 0; 320 480); transform = [0, 1, -1, 0, 0, 0]; gestureRecognizers = <NSArray: 0x8f62e40>; layer = <UIWindowLayer: 0x8f620d0>>
statusBarFrame

UIApplication有一个属性,叫statusBarFrame,是在竖屏坐标系统下的。

1 CGRect rect = [UIApplication sharedApplication].statusBarFrame;
2 NSLog(@"statusBarFrame %@", NSStringFromCGRect(rect));

设备旋转到竖屏:

statusBarFrame {{0, 0}, {320, 20}}

设备旋转到横屏:

statusBarFrame {{300, 0}, {20, 480}}

如果你仅仅想获取状态栏高度,而不管设备方向,你能通过以下方式获取:

float statusBarHeight = MIN([UIApplication sharedApplication].statusBarFrame.size.height, [UIApplication sharedApplication].statusBarFrame.size.width);
Notification

当设备旋转,系统发送通知UIApplicationWillChangeStatusBarFrameNotification和UIApplicationDidChangeStatusBarFrameNotification。
让我们来看,接受UIApplicationDidChangeStatusBarFrameNotification后处理。

1 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillChangeStatusBarFrameNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
2         NSLog(@"%@", note);
3     }];
4
5     [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidChangeStatusBarFrameNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
6         NSLog(@"%@", note);
7     }];

How to create alert view

有两种方式创建alertView.

Don’t use rootViewController approach

像OLGhostAlertView,SVProgressHUD,WYPopoverController,MTStatusBarOverlay,这样的第三方库,不使用rootViewController。他们创建新window(MTStatusBarOverlay)或者使用存在的UIWindow,他们直接在window上添加子视图。所以他们通过接受UIApplicationDidChangeStatusBarOrientationNotification和UIApplicationWillChangeStatusBarFrameNotification来处理视图方向的。

处理大妈如下,摘录于SVProgressHUD。方法是手动获取屏幕方向并且发送一个 rotation transform 给他的视图。

 1 - (void)handleOrientationChange:(NSNotification *)note
 2 {
 3     UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
 4
 5     CGRect orientationFrame = [UIScreen mainScreen].bounds;
 6     CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
 7
 8     if(UIInterfaceOrientationIsLandscape(orientation)) {
 9         float temp = orientationFrame.size.width;
10         orientationFrame.size.width = orientationFrame.size.height;
11         orientationFrame.size.height = temp;
12
13         temp = statusBarFrame.size.width;
14         statusBarFrame.size.width = statusBarFrame.size.height;
15         statusBarFrame.size.height = temp;
16     }
17
18     switch (orientation) {
19         case UIInterfaceOrientationPortraitUpsideDown:
20             rotateAngle = M_PI;
21
22             break;
23         case UIInterfaceOrientationLandscapeLeft:
24             rotateAngle = -M_PI/2.0f;
25
26             break;
27         case UIInterfaceOrientationLandscapeRight:
28             rotateAngle = M_PI/2.0f;
29
30             break;
31         default: // as UIInterfaceOrientationPortrait
32             rotateAngle = 0.0;
33
34             break;
35     }
36
37 }
How to add view to existing window

第三方库都有他们的在windows上添加视图的方法。

SVProgressHUD 是在最前面的window上添加视图的。

1 if(!self.overlayView.superview){
2         NSEnumerator *frontToBackWindows = [[[UIApplication sharedApplication]windows]reverseObjectEnumerator];
3
4         for (UIWindow *window in frontToBackWindows)
5             if (window.windowLevel == UIWindowLevelNormal) {
6                 [window addSubview:self.overlayView];
7                 break;
8             }
9     }
How the OS creates AlertView

在WWDC 2014 Session 228,“A Look Inside Presentation Controllers”,他们说:

Behind the scenes, the framework creates a window on your app’s behalf, but this predates iOS 8 window rotation behavior, so this window is technically still in portrait.

We then add the action sheet to that window and mimic the transform hierarchy of the presenting view to get into the right orientation.

Use rootViewController approach

一些第三方库,像FLEX,SIAlertView...他们创建一个新window,并且将根控制器赋予给window。这种方式下,设备方向旋转发送给了rootViewController,他们在根控制器上添加视图。添加视图和控制设备方向的代码都在控制器中。

代码摘抄于FLEXViewExplorerViewController:

 1 - (NSUInteger)supportedInterfaceOrientations
 2 {
 3     UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
 4     NSUInteger supportedOrientations = [FLEXUtility infoPlistSupportedInterfaceOrientationsMask];
 5     if (viewControllerToAsk && viewControllerToAsk != self) {
 6         supportedOrientations = [viewControllerToAsk supportedInterfaceOrientations];
 7     }
 8
 9     // The UIViewController docs state that this method must not return zero.
10     // If we weren‘t able to get a valid value for the supported interface orientations, default to all supported.
11     if (supportedOrientations == 0) {
12         supportedOrientations = UIInterfaceOrientationMaskAll;
13     }
14
15     return supportedOrientations;
16 }
17
18 - (BOOL)shouldAutorotate
19 {
20     UIViewController *viewControllerToAsk = [self viewControllerForStatusBarAndOrientationProperties];
21     BOOL shouldAutorotate = YES;
22     if (viewControllerToAsk && viewControllerToAsk != self) {
23         shouldAutorotate = [viewControllerToAsk shouldAutorotate];
24     }
25     return shouldAutorotate;
26 }
27
28 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
29 {
30     [...]
31 }
32
33 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
34 {
35     [...]
36 }

因为FLEX创建了新Window,Window必须询问app原始的Window才能获取旋转消息。注意infoPlistSupportedInterfaceOrientationsMask和viewControllerForStatusBarAndOrientationProperties两个属性。你能够从中学到很多。

UIWindow is a UIView

因为UIWindow是UIView的子类,你能够想UIView一样对UIWindow做很多事情。

例如,FLEX重写了pointInside:withEvent: to,用来拦截它的toolbar上的点击。

1 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
2 {
3     BOOL pointInside = NO;
4     if ([self.eventDelegate shouldHandleTouchAtPoint:point]) {
5         pointInside = [super pointInside:point withEvent:event];
6     }
7     return pointInside;
8 }

UIWindow in iOS 8

在iOS8,苹果引入了Size Classes,并且“旋转是一种动画式的视图边界变化”。并且UIWindow能够获取屏幕旋转通知。

在WWDC 2014 Session 216,《Building Adaptive Apps with UIKit》

Trait Environments are a new protocol that are able to return their current Trait Collection, and these include Screens, Windows, View Controllers, and also Views.

All of these are able to return their current Trait Collection to you to use to determine how your interface should be laid out.
时间: 2024-10-19 20:12:01

UIWindow in iOS的相关文章

[iOS基础控件 - 6.10.7] UIWindow &amp; 程序启动过程

A.UIWindow概念 1.继承UIView,是一种特殊的UIView 2.通常一个APP只有一个UIWindow 3.iOS程序启动后,创建的第一个视图就是UIWindow 4.没有UIWindow,不能显示任何东西 B.使用 1.创建一个Empty Application项目 没有了storyboard,要手动实现UIWindow的创建代码(其实这就是storyboard做的事情) 1 // 手动创建UIWindow,并加到screen上 2 self.window = [[UIWindo

猫猫学IOS(二十二)UI之UIApplicationDelegate和UIWindow

猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents UIApplicationDelegate 每次新建完项目,都有个带有"AppDelegate"字眼的类,它就是UIApplication的代理 NYAppDelegate默认已经遵守了UIApplicationDelegate协议,已经 是UIApplication的代理 UIApplication和delegat

iOS开发之UIWindow

1.概述 UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow. iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上了. 一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow.也就说,没有UIWindow,就看不见任何UI界面. 2.添加UIView到UIWindow中两种常见方式 方式一: - (void)addSubview

iOS中的应用启动原理

iOS中的应用启动原理 来源: http://m.warting.com/program/201601/127355.html 一.UIApplication  1. 简单介绍 (1)UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序. (2)每一个应用都有自己的UIApplication对象,而且是单例的,如果试图在程序中新建一个UIApplication对象,那么将报错提示. (3)通过[UIApplicationsharedApplicat

iOS开发之App启动原理

iOS程序的启动过程 程序启动的完整过程大致步骤如下: 1.main函数 2.UIApplicationMain * 创建UIApplication对象 * 创建UIApplication的delegate对象 3.delegate对象开始处理(监听)系统事件(没有storyboard) * 程序启动完毕的时候, 就会调用代理的application:didFinishLaunchingWithOptions:方法 * 在application:didFinishLaunchingWithOpt

关于UIWindow(转)

(原文出自:http://www.cnblogs.com/wendingding/p/3770052.html,特别感谢) 一:[[UIScreen mainScreen] bounds] 和[UIScreen mainScreen] applicationFrame]的区别: bounds就是屏幕的全部区域,applicationFrame就是app显示的区域,不包含状态栏(高度20,如果状态栏隐藏的话,那么,这个结果就和bounds一样了) 二:简单介绍: UIWindow是一种特殊的UIV

UIWindow 详解及使用场景

首先来看一下UIWindow 继承关系 UIView的功能  负责渲染区域的内容,并且响应该区域内发生的触摸事件 UIWindow 在iOS App中,UIWindow是最顶层的界面内容,我们使用UIWindow和UIView来呈现界面.UIWindow并不包含任何默认的内容,但是它被当作UIView的容器,用于放置应用中所有的UIView. 从继承关系来看,UIWindow继承自UIView,所以UIWindow除了具有UIView的所有功能之外,还增加了一些特有的属性和方法,而我们最常用的方

iOS UIKit:view

1.View架构 1.1 简介 UIView表示屏幕上的一块矩形区域,它在App中占有绝对重要的地位,因为IOS中几乎所有可视化控件都是UIView的子类.UIView的功能 :         1) 管理矩形区域里的内容:         2) 处理矩形区域中的事件:         3) 子视图的管理:         4) 实现动画. 图 11 UIView及子类继承关系 1.2 基本结构体         1) CGPoint        该结构表示在二维坐标系中的坐标点. struc

UIWindow和UIView

1.UIWindow UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow. iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上. 一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow,也就说,没有UIWindow,就看不见任何UI界面. 如何创建UIWindow?创建UIWindow需要指定UIWindow的大小.通常wind