导航控制器上的透明模态视图


71

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


1
注意:对于2014年,绝对的解决方案是使用全屏容器视图-然后您具有绝对的完全控制权,并且可以执行所需的任何操作(对其进行动画处理,对其进行滑动-任何操作)。在某些情况下,这是最佳解决方案。
Fattie 2014年

Answers:


109

模态视图将覆盖被推到顶部的视图以及导航控制器的导航栏。但是,如果使用-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];

1
非常感谢,我会尝试的。
leafeater

我发现此讨论对于解决此问题非常有用。iphonedevsdk.com/forum/iphone-sdk-development/… 对我有用的方法是手动为子视图的外观设置动画,而不是使用苹果模式视图
CVertex 2010年

4
最简单的方法是将modalView直接添加到navigationController的视图层次中:[self.navigationController.view addSubview:modalView]
Engin Kurutepe 2011年

看看此类别从而简化这种做法,并执行过场动画以及- stackoverflow.com/a/10826685/285694
BadPirate

但是,当我尝试执行此操作时,我的文本字段委托方法根本不起作用...我们对此有解决方案吗?注意:我添加了viewController2.view作为子视图。
iOSManiac

18

最简单的方法是使用modalPresentationStyle财产navigationController(但你必须自己做动画):

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

加工!并涵盖导航栏。
surfrider

秘密成分navigationController.modalPresentationStyle = UIModalPresentationCurrentContext非常感谢
LolaRun 2014年

从标签栏视图控制器显示时不起作用。也完全不能在ios7中工作
Radu Simionescu 2014年

这对我的用例非常有用。对于iOS7,只需切换presentModalViewController到即可presentViewController
约翰·埃克

12

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

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

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

您可能用来显示模式视图的示例代码:

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

我实际上并未-presentModalViewController:animated:与OverlayViewController一起使用,但我希望这会很好。

另请参阅:您的Objective-C单例看起来像什么?


这种方法效果很好-奇怪的是,我在IB中创建了叠加视图并将其view属性设置为隐藏,但是当视图加载(并添加到子视图)时,它始终是隐藏的== NO-我必须最初将其显式设置为YES,例如-viewDidLoad:
petert 2010年

好的,现在我了解了如何加载和初始化NIB。由于叠加视图的设置很简单,因此我现在在类中实现-loadVIew。
petert 2010年

8

我有同样的问题,为了解决该问题,方法是使用addSubview:添加模态视图,并使用UIView的animateWithDuration:delay:options:animations:completion动画化视图层次结构中的更改:

我向包含其他功能的UIViewController(FRRViewController)的子类添加了一个属性和2个方法。我将很快将所有内容发布在gitHub上,但是在那之前,您可以看到下面的相关代码。有关更多信息,您可以查看我的博客:如何显示透明的模式视图控制器

#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;
                         }];
    }


}

7

这是我为解决此问题所做的工作-Google的详细信息,但这种方法对我来说效果很好:

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

效果是:

  • 该视图可以像任何模式视图一样进行动画显示-模式视图的半透明部分可以在现有视图上滑动
  • 动画一停止,背景就会设置为屏幕截图-这使它看起来好像旧视图仍在下面,即使不是。
  • 模态视图的消失动画开始后,图像即被删除。操作系统同时显示了旧的导航视图,因此模式视图可以透明地滑出视线,并且不出所料。

我尝试在自己的叠加视图中设置动画效果,但效果不佳。我发生了车祸,没有任何提示。我没有去追逐它,我确实做了bg view&Works。

模态视图中的代码-我想您可以弄清楚其余的内容,即设置属性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];
}

4
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:newview animated:YES];

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

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


太完美了 比接受的答案容易得多,但仍然给了我想要的结果。
泰德·库尔普

1
不适用于iOS8。背景视图控制器被隐藏,您会看到窗口的背景颜色(本例中为黑色)。
mahboudz 2014年

对于iOS8,您应该为呈现的视图控制器设置模式呈现样式。在下面检查我的答案。
xZenon

4

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

viewController.modalPresentationStyle = 17;

不会删除背景视图。(TWTweetComposeViewController使用它)。

我没有尝试通过此代码通过App Store审查


您是否将模式视图的背景视图设置为透明?我已经尝试过了,并且可以正常工作。我采用了默认的Utility应用程序模板,并将flipsideviewcontroller的背景色更改为clearColor,并将mainviewcontroller中的行从更改controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;controller.modalPresentationStyle = 17;
Mindaugas

1
我可以确认这有效!应用审核可以接受吗?不知道
mahboudz 2014年


3

是的,您必须手动添加视图,如果您想从底部滑入,也必须自己制作动画。

我编写了一个类来执行此操作,并以该类为例编写了半模式日期选择器。

您可以在此博客文章中找到文档,代码在github上



2

我从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过渡样式)未设置为“垂直覆盖”,则可能不需要调用动画方法。


这是一个好主意。出于好奇,如果视图控制器需要支持旋转,是否有办法使该解决方案有效?像这样,下面的半透明弹出窗口和视图控制器会一起旋转吗?
马修·吉林汉姆

我不确定您的应用程序将如何“旋转”。我假设您是说要旋转主视图。在这种情况下,我有2条建议:第一个比较容易:创建第二个视图并将其设置为与主视图相同的大小,但不要将此视图作为主视图的子视图。而是将第二个视图设置为与主视图相同的层次结构级别。下一个重要的事情是将背景图像视图放入第二视图。因此,如果要为主视图设置动画,则第二个视图不会受到影响。第二个建议是:(请参见下一个注释,因为字符限制已超出)
Wayne Liu

第二个建议:仍将此背景作为子视图保留在主视图中。每当旋转主视图时,我都假定您正在更新其“变换”属性并为其添加旋转角度。如果是这种情况,则可以将相同数量的旋转角度“但负”添加到背景图像视图的变换中。这应将背景图像视图保持在其固定位置。
韦恩·刘

2

对于导航或选项卡界面,我最终通过在隐藏或显示基于此非常好的博客文章的视图控制器之前合并了隐藏/未隐藏的叠加视图控制器(请参见:pix0r的答案)来实现此目的。

关于视图控制器,技巧是将其背景视图设置为clearColor,然后可以看到半透明的叠加视图,并且在视图控制器中作为子视图添加的任何视图都在前面,最重要的是不透明。



1

对于iOS 8+,您可以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,请检查此线程


0

通过在视图和导航栏上覆盖一个透明/半透明按钮,可以实现透明/半透明模式视图效果。

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

我发现与UILabel不同,UIButton会捕获鼠标事件-因此提供了正确的模式行为。


0

我刚刚找到了解决方法。只需创建一个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动画:true];


0

如果您需要类似所附屏幕的屏幕,则以下代码可能会对您有所帮助。在此处输入图片说明

编码:

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

0

如果说您想要屏幕覆盖,请使用parentViewController.view,它将放置在导航栏上方++

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


0

这对我有用:

UIViewController *modalViewController = [[UIViewController alloc] init];
modalViewController.view.backgroundColor = [UIColor whiteColor] colorWithAlpha:0.5];
[self showDetailViewController:modalViewController sender:nil];
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.