【问题标题】:Transparent Modal View on Navigation Controller导航控制器上的透明模式视图
【发布时间】:2010-10-25 08:50:44
【问题描述】:

我正在尝试在我的导航控制器上创建一个透明的模态视图。有谁知道这是否可能?

【问题讨论】:

  • 注意:对于 2014 年,一个绝对的解决方案就是使用容器视图,全屏 - 然后你就拥有绝对的、完全的控制权,并且可以做任何你想做的事情(动画,滑动它 - 任何东西)。在某些情况下,这是最好的解决方案。

标签: iphone


【解决方案1】:

我通过设置位于我的窗口或根视图的所有其他子视图之上的“OverlayViewController”来最轻松地完成此操作。在您的应用程序委托或根视图控制器中进行设置,并使 OverlayViewController 成为单例,以便可以从代码或视图控制器层次结构中的任何位置访问它。然后,您可以在需要时调用方法来显示模式视图、显示活动指示器等,它们可能会覆盖任何标签栏或导航控制器。

根视图控制器的示例代码:

- (void)viewDidLoad {
  OverlayViewController *o = [OverlayViewController sharedOverlayViewController];
  [self.view addSubview:o.view];
}

您可以用来显示模态视图的示例代码:

[[OverlayViewController sharedOverlayViewController] presentModalViewController:myModalViewController animated:YES];

我实际上并没有将-presentModalViewController:animated: 与我的 OverlayViewController 一起使用,但我希望这会正常工作。

另请参阅:What does your Objective-C singleton look like?

【讨论】:

  • 这种方法效果很好 - 一件奇怪的事情,我在 IB 中创建了覆盖视图并将其视图属性设置为隐藏,但是当视图被加载(并添加到子视图)时,它总是hidden == NO - 我必须首先明确地将其设置为 YES,例如 -viewDidLoad:
  • 好的,现在我了解了如何加载和初始化 NIB。由于覆盖视图很容易设置,现在我在我的类中实现 -loadVIew。
【解决方案2】:

模态视图将覆盖它被推送到顶部的视图以及导航控制器的导航栏。但是,如果您使用 -presentModalViewController:animated: 方法,那么一旦动画完成,刚刚覆盖的视图实际上会消失,这会使您的模态视图的任何透明度变得毫无意义。 (您可以通过在根视图控制器中实现 -viewWillDisappear: 和 -viewDidDisappear: 方法来验证这一点)。

您可以像这样将模态视图直接添加到视图层次结构中:

UIView *modalView =
    [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
modalView.opaque = NO;
modalView.backgroundColor =
    [[UIColor blackColor] colorWithAlphaComponent:0.5f];

UILabel *label = [[[UILabel alloc] init] autorelease];
label.text = @"Modal View";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[label sizeToFit];
[label setCenter:CGPointMake(modalView.frame.size.width / 2,
                                modalView.frame.size.height / 2)];
[modalView addSubview:label];

[self.view addSubview:modalView];

像这样将 modalView 作为子视图添加到根视图中实际上不会覆盖导航栏,但会覆盖其下方的整个视图。我尝试使用用于初始化 modalView 的框架的原点,但负值会导致它不显示。我发现除了状态栏之外覆盖整个屏幕的最佳方法是将modalView添加为窗口本身的子视图:

TransparentModalViewAppDelegate *delegate = (TransparentModalViewAppDelegate *)[UIApplication sharedApplication].delegate;
[delegate.window addSubview:modalView];

【讨论】:

  • 非常感谢,我试试看。
  • 我发现这个讨论对于这个问题非常有用。 iphonedevsdk.com/forum/iphone-sdk-development/… 对我有用的方法是手动为子视图的外观设置动画,而不是使用苹果模式视图
  • 最简单的方法是将modalView直接添加到navigationController的视图层次中:[self.navigationController.view addSubview:modalView]
  • 看看这个类别,它简化了这种方法,并且动画转换也一样 - stackoverflow.com/a/10826685/285694
  • 但是当我尝试这样做时,我的文本字段委托方法根本不起作用......我们有解决方案吗?注意:我添加了 viewController2.view 作为子视图。
【解决方案3】:

这篇关于displaying a semi-transparent "Loading..." view 的帖子可能会就如何进行提供一些指导。

【讨论】:

    【解决方案4】:

    您可以通过在视图和导航栏上叠加一个透明/半透明按钮来实现透明/半透明模态视图效果。

    您可以通过 UINavigationController 的 navigationBar 属性访问导航栏。

    我发现 UIButton 与 UILabel 不同,它会捕获鼠标事件——因此会给出正确的模态行为。

    【讨论】:

      【解决方案5】:

      对于导航或标签栏界面,我终于实现了这一点,方法是结合一个覆盖视图控制器(参见:pix0r 的答案),该控制器在隐藏或显示基于这个非常好的 blog post 的视图控制器之前隐藏/取消隐藏。

      关于视图控制器,提示是将其背景视图设置为clearColor,然后半透明覆盖视图可见,并且在视图控制器中作为子视图添加的任何视图都在前面,最重要的是不透明。

      【讨论】:

        【解决方案6】:

        是的,你必须手动添加视图,如果你想从底部滑入或者你必须自己做动画。

        我编写了一个类来执行此操作,并使用该类作为示例的半模态日期选择器。

        你可以在this blog post找到文档,代码是on github

        【讨论】:

          【解决方案7】:

          我遇到了同样的问题,为了解决这个问题,解决方案是使用 addSubview: 添加模态视图,并使用 UIView 的 animateWithDuration:delay:options:animations:completion: 动画视图层次结构中的变化:

          我向一个包含其他功能的 UIViewController (FRRViewController) 子类添加了一个属性和 2 个方法。我将很快在 gitHub 上发布所有内容,但在那之前你可以看到下面的相关代码。更多信息,您可以查看我的博客:How to display a transparent modal view controller

          #pragma mark - Transparent Modal View
          -(void) presentTransparentModalViewController: (UIViewController *) aViewController 
                                               animated: (BOOL) isAnimated 
                                              withAlpha: (CGFloat) anAlpha{
          
              self.transparentModalViewController = aViewController;
              UIView *view = aViewController.view;
          
              view.opaque = NO;
              view.alpha = anAlpha;
              [view.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                  UIView *each = obj;
                  each.opaque = NO;
                  each.alpha = anAlpha;
              }];
          
              if (isAnimated) {
                  //Animated
                  CGRect mainrect = [[UIScreen mainScreen] bounds];
                  CGRect newRect = CGRectMake(0, mainrect.size.height, mainrect.size.width, mainrect.size.height);
          
          
                  [self.view addSubview:view];
                  view.frame = newRect;
          
                  [UIView animateWithDuration:0.8
                                   animations:^{
                                       view.frame = mainrect;
                                   } completion:^(BOOL finished) {
                                       //nop
                                   }];
          
              }else{
                  view.frame = [[UIScreen mainScreen] bounds];
                  [self.view addSubview:view];
              }
          
          
          
          
          
          
          }
          
          -(void) dismissTransparentModalViewControllerAnimated:(BOOL) animated{
          
              if (animated) {
                  CGRect mainrect = [[UIScreen mainScreen] bounds];
                  CGRect newRect = CGRectMake(0, mainrect.size.height, mainrect.size.width, mainrect.size.height);
                  [UIView animateWithDuration:0.8
                                   animations:^{
                                       self.transparentModalViewController.view.frame = newRect;
                                   } completion:^(BOOL finished) {
                                       [self.transparentModalViewController.view removeFromSuperview];
                                       self.transparentModalViewController = nil;
                                   }];
              }
          
          
          }
          

          【讨论】:

            【解决方案8】:

            过去一周我一直在研究同样的问题。我尝试了在 Google 和 StackOverflow 上找到的所有各种答案和示例。它们都没有那么好用。

            作为 iOS 编程新手,我不知道名为 UIActionSheet 的东西。因此,如果您尝试完成此操作以显示按钮的模态覆盖(例如模态询问某人他们想如何共享某些内容),只需使用UIActionSheet

            这里是a webpage that shows an example of how to do this

            【讨论】:

              【解决方案9】:

              这是我为解决问题所做的 - 谷歌详细信息,但这种方法对我来说非常有效:

              • 截取底层视图的屏幕截图。 https://devforums.apple.com/message/266836 - 这会导致一个现成的方法返回当前屏幕的 UIView。
              • 将屏幕截图交给模态视图(我使用了一个属性)
              • 呈现模态视图
              • 在模态视图控制器的 viewDidAppear 中,将图像设置为索引 0 处的 UIImageView。通过状态栏的高度调整图像的垂直位置。
              • 在模态视图控制器的 viewWillDisappear 中,再次移除图像

              效果是:

              • 视图的动画效果与任何模态视图一样 - 模态视图的半透明部分滑过现有视图
              • 一旦动画停止,背景就会设置为屏幕截图 - 这使得它看起来好像旧视图仍在下方,即使它不是。
              • 模式视图的消失动画一开始,图像就会被移除。同时,操作系统会显示旧的导航视图,因此模态视图会像您期望的那样透明地滑出并消失在视线之外。

              我尝试在自己的叠加视图中制作动画,但效果不佳。我遇到了崩溃,没有任何迹象表明发生了什么崩溃。我没有追究这一点,而是做了 bg 视图并且效果非常好。

              模态视图中的代码 - 我想你可以弄清楚其余部分,即设置属性 modalView.bgImage...

              - (void)viewDidAppear:(BOOL)animated {
                  // background
                  // Get status bar frame dimensions
                  CGRect statusBarRect = [[UIApplication sharedApplication] statusBarFrame];
                  UIImageView *imageView = [[UIImageView alloc] initWithImage:self.bgImage];
                  imageView.tag = 5;
                  imageView.center = CGPointMake(imageView.center.x, imageView.center.y - statusBarRect.size.height);
                  [self.view insertSubview:imageView atIndex:0];
              }
              
              - (void)viewWillDisappear:(BOOL)animated {
                  [[self.view viewWithTag:5] removeFromSuperview];
              }
              

              【讨论】:

                【解决方案10】:

                我从https://gist.github.com/1279713得到这个想法

                准备: 在模态视图 xib(或使用情节提要的场景)中,我设置了 0.3 alpha 的全屏背景 UIImageView(将其与 .h 文件挂钩并为其赋予属性“backgroundImageView”)。我将视图 (UIView) 背景颜色设置为纯黑色。

                想法: 然后在模态视图控制器的“viewDidLoad”中,我从原始状态捕获屏幕截图并将该图像设置为背景 UIImageView。将初始 Y 点设置为 -480,并使用 EaseInOut 动画选项使用 0.4 秒的持续时间让它滑动到 Y 点 0。当我们关闭视图控制器时,只需做相反的事情。

                模态视图控制器类的代码

                .h 文件:

                @property (weak, nonatomic) IBOutlet UIImageView *backgroundImageView;
                - (void) backgroundInitialize;
                - (void) backgroundAnimateIn;
                - (void) backgroundAnimateOut;
                

                .m 文件:

                - (void) backgroundInitialize{
                    UIGraphicsBeginImageContextWithOptions(((UIViewController *)delegate).view.window.frame.size, YES, 0.0);
                    [((UIViewController *)delegate).view.window.layer renderInContext:UIGraphicsGetCurrentContext()];
                    UIImage * screenshot = UIGraphicsGetImageFromCurrentImageContext();
                    UIGraphicsEndImageContext();
                    backgroundImageView.image=screenshot;
                }
                - (void) backgroundAnimateIn{
                    CGRect backgroundImageViewRect = backgroundImageView.frame;
                    CGRect backgroundImageViewRectTemp = backgroundImageViewRect;
                    backgroundImageViewRectTemp.origin.y=-480;
                    backgroundImageView.frame=backgroundImageViewRectTemp;
                
                    [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationCurveEaseInOut animations:^{
                        backgroundImageView.frame=backgroundImageViewRect;
                    } completion:^(BOOL finished) {
                
                    }];
                }
                - (void) backgroundAnimateOut{
                    CGRect backgroundImageViewRect = backgroundImageView.frame;
                    backgroundImageViewRect.origin.y-=480;
                
                    [UIView animateWithDuration:0.4 delay:0.0 options:UIViewAnimationCurveEaseInOut animations:^{
                        backgroundImageView.frame=backgroundImageViewRect;
                    } completion:^(BOOL finished) {
                
                    }];
                }
                

                在 viewDidLoad 中,只需调用:

                [self backgroundInitialize];
                [self backgroundAnimateIn];
                

                在我们关闭模态视图控制器的任何地方,我们调用:

                [self backgroundAnimateOut];
                

                请注意,这将始终为背景图像设置动画。所以如果这个模态视图控制器的过渡风格(或者segue过渡风格)没有设置为“Cover Vertical”,你可能不需要调用动画方法。

                【讨论】:

                • 这是个好主意。出于好奇,如果视图控制器需要支持旋转,是否有办法使这个解决方案工作?如题,下面的半透明弹窗和视图控制器会一起旋转吗?
                • 我不确定您的应用将如何“旋转”。我假设您是说要旋转主视图。如果是这种情况,我有 2 条建议:首先更简单:创建第二个视图并将其设置为与主视图相同的大小,但不要将此视图作为主视图的子视图。相反,将此第二个视图设置在与主视图相同的层次结构级别。下一个重要的事情是将背景图像视图放入第二个视图中。因此,如果您为主视图设置动画,则第二个视图不受影响。第二个建议是:(超过字符限制,请参阅下一条评论)
                • 第二个建议:仍然在主视图中保留这个背景作为子视图。每当您旋转主视图时,我假设您正在更新其“变换”属性并为其添加旋转角度。如果是这种情况,那么您可以将相同数量的“但负”旋转角度添加到背景图像视图的变换中。这应该使背景图像视图保持在其固定位置。
                【解决方案11】:
                self.modalPresentationStyle = UIModalPresentationCurrentContext;
                [self presentModalViewController:newview animated:YES];
                

                并确保将模态视图背景设置为透明,

                self.view.background = .... alpha:0.x;

                【讨论】:

                • 这太完美了。比接受的答案容易得多,但仍然给了我想要的结果。
                • 不适用于 iOS8。背景视图控制器被隐藏了,你会看到窗口的背景颜色(在我的例子中是黑色)。
                • 对于 iOS8,您应该为 presented 视图控制器设置模态演示样式。在下面检查我的答案。
                【解决方案12】:

                最简单的方法是使用 navigationControllermodalPresentationStyle 属性(但您必须自己制作动画):

                self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
                [self presentModalViewController:modalViewController animated:NO];
                modalViewController.view.alpha = 0;
                [UIView animateWithDuration:0.5 animations:^{
                    modalViewController.view.alpha = 1;
                }];
                

                【讨论】:

                • 工作!并覆盖导航栏。
                • 秘诀 navigationController.modalPresentationStyle = UIModalPresentationCurrentContext 非常感谢
                • 从标签栏视图控制器呈现时不工作。也根本无法在 ios7 中工作
                • 这非常适合我的用例。对于 iOS7,只需将 presentModalViewController 切换为 presentViewController
                【解决方案13】:

                如果您将模态视图控制器的 modalPresentationStyle 设置为:

                viewController.modalPresentationStyle = 17;

                后台的视图不会被移除。 (TWTweetComposeViewController 使用它)。

                我并没有尝试使用此代码通过 App Store 审核

                【讨论】:

                • 您是否将模态视图的背景视图设置为透明?我现在已经尝试过了,它可以工作。我采用了默认的 Utility 应用程序模板,并将 Flipsideviewcontroller 背景颜色更改为 clearColor,并将 mainviewcontroller 中的行从 controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; 更改为 controller.modalPresentationStyle = 17;
                • 我可以确认这行得通!应用审查可以接受吗?不知道。
                【解决方案14】:

                我刚刚找到了解决方法。只需创建一个 1X1 的 UIViewController 并将其添加到您的父视图控制器。并在那个 UIViewController 中显示透明的模态视图控制器。

                在 viewDidLoad 上;

                self.dummyViewController = [[UIViewController alloc] init]; [self.dummyViewController.view setFrame:CGRectMake(0, 0, 1, 1)]; [self.view addSubView:self.dummyViewController.view];

                当你需要打开一个transparentViewController时;

                [self.dummyViewController presentModalViewController:yourTransparentModalViewController animated:true];

                【讨论】:

                  【解决方案15】:

                  我创建了开放源库MZFormSheetController 以在其他 UIWindow 上显示模式表单。您可以使用它来呈现透明度模态视图控制器,甚至可以调整呈现的视图控制器的大小。

                  【讨论】:

                    【解决方案16】:

                    如果你需要一个类似附件的屏幕,下面的代码可以帮助你。

                    代码:

                    MyViewController * myViewController = [[MyViewController alloc] initWithNibName:nibName bundle:nil];
                    UINavigationController * myNavigationController = [[UINavigationController alloc] initWithRootViewController: myViewController];
                    myNavigationController.modalPresentationStyle = UIModalPresentationPageSheet;
                    [self presentModalViewController: myNavigationController animated:YES];
                    

                    【讨论】:

                      【解决方案17】:

                      如果说你想要一个屏幕覆盖,使用 parentViewController.view,它会放在导航栏上方 ++

                      MyCustomViewController* myOverlayView = [[MyCustomViewController alloc] init]; [self.parentViewController.view addSubview:myOverlayView];

                      【讨论】:

                        【解决方案18】:

                        这对我有用:

                        UIViewController *modalViewController = [[UIViewController alloc] init];
                        modalViewController.view.backgroundColor = [UIColor whiteColor] colorWithAlpha:0.5];
                        [self showDetailViewController:modalViewController sender:nil];
                        

                        【讨论】:

                          【解决方案19】:

                          对于 iOS 8+,您可以为 presented 视图控制器使用 UIModalPresentationOverCurrentContext 演示样式,以轻松实现所需的行为。

                          UIViewController *viewController = [[UIViewController alloc] init];
                          viewController.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.9f];
                          viewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;
                          [self presentViewController:viewController animated:YES completion:nil];
                          

                          如果您还需要支持 iOS 7 - 请查看this thread

                          【讨论】:

                            猜你喜欢
                            • 2021-11-21
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2017-06-16
                            • 1970-01-01
                            • 2013-09-24
                            • 1970-01-01
                            相关资源
                            最近更新 更多