在所有视图之上添加一个 UIView,甚至是导航栏

210
我想要显示一个"弹出窗口"视图,它在任何其他视图(甚至导航栏)之上,看起来像这样:
  • 全屏黑色背景,透明度为0.5,可以看到其他UIViewController下面的内容.
  • 一个位于中间的UIView窗口,其中包含一些信息(如果你想知道所有的内容,这是一个日历)。
为了实现这个,我创建了一个包含两个UIViews(背景和窗口)的UIViewController,并试图显示它。我尝试了简单的[mySuperVC addSubview:myPopUpVC.view],但我还是有导航栏在上面。
我试图将其呈现为模态视图,但下面的UIViewController消失了,我失去了透明效果。
有什么办法可以做到这一点,我相信它很简单...
谢谢!

在您的应用程序窗口上添加一个子视图。处理方向更改可能会很棘手:https://dev59.com/Ymoy5IYBdhLWcg3wF6QL - Amar
17个回答

221
你可以通过将你的视图直接添加到 keyWindow 来实现该功能:
UIView *myView = /* <- Your custom view */;
UIWindow *currentWindow = [UIApplication sharedApplication].keyWindow;
[currentWindow addSubview:myView];

更新--适用于Swift 4.1及以上版本

let currentWindow: UIWindow? = UIApplication.shared.keyWindow
currentWindow?.addSubview(myView)

iOS13及以上版本更新

keyWindow已经被弃用,现在应该使用以下方法:

UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.addSubview(myView)

2
很遗憾,如果您在横向模式下,您需要旋转视图。我不确定如何正确地做到这一点。 - Nicolas Roy
是的,我只处于横屏模式,而我的视图则以90度旋转的方式出现。似乎这就是他们在这里讨论的内容:https://dev59.com/Ymoy5IYBdhLWcg3wF6QL - Nicolas Roy
将视图添加到窗口中,可以让它出现在你的所有视图控制器中。如果你想让它只在特定的视图控制器中出现,就像@dalef在下面所说的那样,将你的视图添加到导航栏本身中:[self.navigationController.view addSubview:overlayView]; - Marcos Reboucas
当我尝试这个时,它会在屏幕上显示,但是当屏幕加载后很快就消失了。我尝试将其添加到viewDidLoad、viewDidAppear和viewWillAppear中,结果相同。 - Orlando
7
它不包括选项卡栏。有什么解决办法吗? - Manisha
显示剩余5条评论

142

[self.navigationController.view addSubview:overlayView]; 这是你真正想要的


5
你需要设置框架。 - Gabriel Goncalves
1
非常好的解决方案,特别是当您将视图添加为“子视图控制器”时。 - Richard Topchii
2
这不能与子视图控制器的视图一起使用。更改导航栏的 z 级别(如 Nam 建议的)也适用于子视图控制器。 - Tapani
这似乎比将视图添加到关键窗口更优雅的解决方案。因为这样可以确保添加的视图沿着基础视图的动画移动。如果将视图添加到关键窗口,则不会随动画一起出现,如果要关闭控制器,则必须单独删除它。 - soan saini
这不是弹出窗口,但对于我的情况来说正合适。谢谢)) - Dilshod Zopirov

122

我这里有一个简单优雅的解决方案,对我有用。你可以将导航栏的 Z 轴位置设置为视图下方:

self.navigationController.navigationBar.layer.zPosition = -1;

完成后请务必将其设置回0。


1
这比将叠加视图作为子视图添加到导航控制器的视图中要好得多(顺便说一下,这会导致崩溃)。这个z-position解决方案可能更可靠,并且在未来会引起更少的问题。 - Tapani
对我来说也很好用。提一下zPosition应该被设置回值0会更好(你只是提到完成后应该被设置回去)。而且我的屏幕上还有tabBar(来自TabBarController)。我尝试设置zPosition,但当返回到0值时,tabBar仍然不可见。所以对于tabBar最好使用isHidden属性。 - Libor Zapletal
1
不要使用无障碍功能。在视图上平移不会将其聚焦。 - antonjn
这将把导航控制器设置在所有内容的后面,使其不可见..? - Eric
添加的视图是UIImageView,我们在其中应用了捏合缩放,即使设置了zPosition,导航栏按钮也停止工作。 - DSRawat
显示剩余2条评论

61

检查响应的Swift版本:

Swift 4:

let view = UIView()
view.frame = UIApplication.shared.keyWindow!.frame
UIApplication.shared.keyWindow!.addSubview(view)

Swift 3.1 :

let view = UIView()
view.frame = UIApplication.sharedApplication().keyWindow!.frame 
UIApplication.sharedApplication().keyWindow!.addSubview(view)

7
我可以翻译这段内容。请问是否需要繁体中文或简体中文呢? - Michał Ziobro

27

Dalef 提供了一种很棒的 Swift 解决方案:

self.navigationController?.view.addSubview(view)

24
将您的视图作为 NavigationController 的子视图添加。
[self.navigationController.navigationBar addSubview: overlayView)]

你也可以将它添加到窗口上:

UIView *view = /* Your custom view */;
UIWindow *window = [UIApplication sharedApplication].keyWindow;
[window addSubview:view];

希望这能帮到你.. :)


概述即将到来,但是当添加一个按钮时,无法获取按钮操作或在概述上设置自定义标签文本。 - Vineesh TP
对我来说运行良好: let navigationBar = viewController.navigationController!.navigationBar navigationBar.addSubview(coverView) - Arthur Clemens

18

@Nam的答案非常适用于您只想显示自定义视图,但是如果您的自定义视图需要用户交互,则需要禁用navigationBar的交互。


self.navigationController.navigationBar.layer.zPosition = -1
self.navigationController.navigationBar.isUserInteractionEnabled = false

就像Nam的回答中所说,不要忘记撤销这些更改:

self.navigationController.navigationBar.layer.zPosition = 0
self.navigationController.navigationBar.isUserInteractionEnabled = true


您可以通过扩展程序更好地完成此操作:

extension UINavigationBar {
    func toggle() {
        if self.layer.zPosition == -1 {
            self.layer.zPosition = 0
            self.isUserInteractionEnabled = true
        } else {
            self.layer.zPosition = -1
            self.isUserInteractionEnabled = false
        }
    }
}

仅需像这样使用:

self.navigationController.navigationBar.toggle()

1
完美的解决方案..!! 尝试了很多事情并花了很多时间.. 但这个运行得很好..!! - Dishant
1
救命但优雅的。 - hsing

8

@Nicolas Bonnet的答案的Swift版本:

    var popupWindow: UIWindow?

func showViewController(controller: UIViewController) {
    self.popupWindow = UIWindow(frame: UIScreen.mainScreen().bounds)
    controller.view.frame = self.popupWindow!.bounds
    self.popupWindow!.rootViewController = controller
    self.popupWindow!.makeKeyAndVisible()
}

func viewControllerDidRemove() {
    self.popupWindow?.removeFromSuperview()
    self.popupWindow = nil
}

请注意,窗口必须是一个强属性,因为原始答案会导致窗口立即被释放。


6

有不止一种方法可以实现:

1- 将您的UIView添加到UIWindow而不是添加到UIViewController上。这样它就会在所有内容的顶部。

   [[(AppDelegate *)[UIApplication sharedApplication].delegate window] addSubview:view];

2- 使用自定义过渡,将您的UIViewController保持在后台,透明度为0.5

为此,我建议您查看以下内容:https://github.com/Citrrus/BlurryModalSegue


6

我建议您创建一个新的UIWindow:

UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = viewController;
window.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
window.opaque = NO;
window.windowLevel = UIWindowLevelCFShareCircle;
window.backgroundColor = [UIColor clearColor];

[window makeKeyAndVisible];

然后您可以在另一个UIViewController中管理您的视图。 要删除窗口:

[window removeFromSuperview];
window = nil;

希望这能有所帮助!

3
谢谢,但我认为有些东西丢失了,因为什么都没有显示:/ - Nicolas Roy

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