在顶部显示视图:UIWindow子视图 VS UIViewController子视图

27

为了让一个UIViewUIAlertView一样出现在所有的视图之上,我认为最好的方法是将它添加到应用程序窗口中:

[[[UIApplication sharedApplication] delegate] window]

然而,我发现它不会自动接受旋转。为了让它接受旋转,最好的方法是将视图添加到当前的AppDelegate的window.navigationController/rootViewController中,但这样做将不再置于顶部。假设当视图正在显示并且有一个modalViewController弹出时,模态视图肯定会覆盖视图。

我的问题是,是否可以将子视图添加到最顶层的窗口并支持方向?在这种情况下,我需要两组Nib文件吗?UIAlertView如何将最顶层和方向支持结合在一起?当我查看UIAlertView.h时,我发现实际上有2个UIWindows,一个称为originalWindow,另一个称为dimWindow。我在哪里可以找到UIAlertView.m的源代码?

3个回答

15

我找到的最佳解决方案是创建一个透明的容器视图,将该容器添加到窗口中,并将警报放置在容器内。然后,您可以注册 UIApplicationWillChangeStatusBarOrientationNotification 以接收旋转事件并转换容器;这使您可以独立地操作警报的框架:

- (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);
     }];
}
如果你真的想要,可以创建一个全新的窗口,并将其windowLevel属性设置为UIWindowLevelAlert,这将确保该窗口始终位于所有其他视图(包括键盘)之上,但是窗口管理会有点棘手。对于大多数需求,上述解决方案应该足够了。

将视图控制器的视图简单地添加到窗口中存在问题,因为除非它通过在根视图控制器上使用addChildViewController:添加到视图控制器层次结构中,否则它不能保证接收所有旋转和view[Will|Did][Disa|A]ppear: 消息。


我对这个解决方案有问题。我无法在最顶层的容器视图中注册触摸。例如,UIButtons不起作用。 - asendra
@asendra 听起来你的窗口不是关键窗口,但你可能想发布自己的问题并提供更多细节。 - Austin

4

只有第一个UIWindow的子视图会收到有关方向更改的通知。 (< iOS8)

您的问题与此非常相似:

在UIWindow中使用多个视图

正如那里的答案所说,您可以注册方向更改的通知并通过处理“置顶”子视图的重新布局来处理。


1
如何将视图注册到设备以接收旋转事件?这不是视图控制器,因此没有委托函数。我需要捕获 UIDeviceOrientationDidChangeNotification 以更改为相应的方向吗? - Zian Chen

2

这里提供一种可能的解决方案,我将分几步来解释。

  1. Start With a UIViewController inherited class instead of UIView, Lets say class named TopViewController inherited by UIViewController
  2. Since you you can not register device/interface rotation in UIView inherited class, the TopViewController class which is inherited from UIViewController has methods responding to View Rotation Events. Rotation events can be captured now.
  3. At last you can put the TopViewController view on the top of UIWindow by using the same method that you mentioned in your question .

    [[[[UIApplication sharedApplication] delegate] window] addSubview:(TopViewController object).view];
    
希望这能有所帮助。

UIViewController 的 view 属性也无法旋转。不幸的是。https://developer.apple.com/library/ios/qa/qa1688/_index.html - khunshan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接