Transparent Modal View on Navigation Controller
Asked Answered
A

19

71

I'm trying to create a transparent modal View on top of my navigation controller. Does anyone know if this is possible?

Anatollo answered 11/5, 2009 at 18:40 Comment(1)
Note: for 2014, an absolute solution is just to use a container view, full screen - and then you have absolute, total control and can do whatever you want (animate it in, slide it - anything). In certain cases, this is the best solution.Clemenciaclemency
M
109

A modal view will cover the view it is pushed on top of as well as the navigation bar for your navigation controller. However, if you use the -presentModalViewController:animated: approach, then once the animation finishes the view just covered will actually disappear, which makes any transparency of your modal view pointless. (You can verify this by implementing the -viewWillDisappear: and -viewDidDisappear: methods in your root view controller).

You can add the modal view directly to the view hierarchy like so:

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

Adding the modalView as a subview to the root view like this will not actually cover the navigation bar, but it will cover the entire view below it. I tried playing around with the origin of the frame used to init the modalView, but negative values cause it to not display. The best method that I found to cover the entire screen besides the status bar is to add the modalView as a subview of the window itself:

TransparentModalViewAppDelegate *delegate = (TransparentModalViewAppDelegate *)[UIApplication sharedApplication].delegate;
[delegate.window addSubview:modalView];
Monochloride answered 13/5, 2009 at 17:10 Comment(4)
I found this discussion quite useful for this problem. iphonedevsdk.com/forum/iphone-sdk-development/… The approach that worked for me was to manually animate the appearance of a subview, instead of using apple modal viewsLiquefy
the easiest method is to add the modalView directly to the view hierarchy of the navigationController: [self.navigationController.view addSubview:modalView]Hixon
Take a look at this category which streamlines this approach, and does the animated transitions as well - https://mcmap.net/q/276343/-how-to-change-modal-background-color-to-transparent-in-objective-cMiterwort
But when I tried to do this, my textfield delegate methods are not at all working...Do we have a solution for this. Note: I added viewController2.view as subview.Heathenism
D
18

The easiest way is to use modalPresentationStyle property of navigationController (but you'll have to make animation by yourself):

self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:modalViewController animated:NO];
modalViewController.view.alpha = 0;
[UIView animateWithDuration:0.5 animations:^{
    modalViewController.view.alpha = 1;
}];
Demography answered 9/9, 2012 at 21:7 Comment(4)
Working! And covers navbar.Plush
The secret ingredient navigationController.modalPresentationStyle = UIModalPresentationCurrentContext Thank you so muchPaynim
not working when presenting from tab bar view controller. Also not working in ios7 at allLabanna
This works great for my use case. For iOS7 simply switch presentModalViewController to presentViewController.Quagmire
T
12

I accomplish this most easily by setting up an "OverlayViewController" that sits above all other subviews of my window or root view. Set this up in your app delegate or root view controller, and make OverlayViewController a singleton so that it can be accessed from anywhere in your code or view controller hierarchy. You can then call methods to show modal views, show activity indicators, etc, whenever you need to, and they can potentially cover any tab bars or navigation controllers.

Sample code for root view controller:

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

Sample code you might use to display your modal view:

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

I haven't actually used -presentModalViewController:animated: with my OverlayViewController but I expect this would work just fine.

See also: What does your Objective-C singleton look like?

Tressatressia answered 11/5, 2009 at 18:45 Comment(2)
This approach works very well - one odd thing, I make my overlay view in IB and set it's view property to hidden, but when the view is loaded (and added to the sub-view) it's always hidden == NO - I have to explicitly set it to YES initially, in say -viewDidLoad:Febri
Okay, now I understand how NIB's are loaded and initialised. Since the overlay view is simple to set up, now I'm implementing -loadVIew in my class.Febri
S
8

I had this same problem and in order to The solution is to add the modal view with addSubview: and animate the change in the view hierarchy with UIView’s animateWithDuration:delay:options:animations:completion:

I added a property and 2 methods to a subclass of UIViewController (FRRViewController) that includes other functionalities. I will be publishing the whole stuff on gitHub soon, but until then you can see the relevant code below. For more info, you can check my blog: 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;
                         }];
    }


}
Statesmanship answered 20/10, 2011 at 19:40 Comment(0)
P
7

Here's what I did to solve the problem - Google the details but this approach worked very well for me:

  • Take a screenshot of the underlying view. https://devforums.apple.com/message/266836 - this leads to a ready-made method that returns a UIView for the current screen.
  • Hand the screenshot to the modal view (I used a property)
  • Present the modal view
  • In the modal view controller's viewDidAppear, set the image as UIImageView at index 0. Adjust the vertical position of the image by the height of the status bar.
  • In the modal view controller's viewWillDisappear, remove the image again

The effect is:

  • The view animates in as any modal view does - the semi transparent parts of the modal view glide over the existing view
  • As soon as the animation stops, the background is set to the screenshot - this makes it appear as if the old view is still underneath even though it isn't.
  • As soon as the modal view's disappear animation starts, the image is removed. The OS meanwhile shows the old navigation view so the modal view transparently glides away and out of sight as you'd expect.

I tried animating in my own overlay view but it didn't work very well. I got a crash with no indication as to what has crashed. Rather than chase this down I did the bg view & Works really well.

Code in the modal view - I think you can figure out the rest, namely setting the property 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];
}
Permalloy answered 10/4, 2012 at 7:57 Comment(0)
A
4
self.modalPresentationStyle = UIModalPresentationCurrentContext;
[self presentModalViewController:newview animated:YES];

and make sure you setup the modal view background to be transparent,

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

Aerostatic answered 2/7, 2012 at 7:8 Comment(3)
This was perfect. Way easier than the accepted answer and still gave me the desired result.Saturant
Doesn't work with iOS8. Background viewcontroller gets hidden and you see the window's background color(black in my case).Maegan
For iOS8 you should set modal presentation style for presented view controller. Check my answer below.Runion
C
4

if you set modalPresentationStyle for the modal view controller to:

viewController.modalPresentationStyle = 17;

The view in the background is not removed. (TWTweetComposeViewController use it).

I did not try to pass App Store review with this code though

Cutback answered 25/10, 2012 at 7:47 Comment(2)
did you set background view for the modal view to be transparent? I've tried it right now and it works. I took default Utility application template, and changed flipsideviewcontroller background color to clearColor and changed the line in mainviewcontroller from controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; to controller.modalPresentationStyle = 17;Cutback
I can confirm that this works! App review acceptable? Don't know.Maegan
J
3

This post about displaying a semi-transparent "Loading..." view might give a few pointers on how to proceed.

Juni answered 17/5, 2009 at 14:14 Comment(0)
S
3

Yeah, you have to add the view manually, and if you want to slide in from the bottom or whatever you have to do the animation yourself too.

I wrote a class to do this, and a semi-modal datepicker using that class as an example.

You can find documentation in this blog post, the code is on github

Sparoid answered 19/10, 2010 at 2:57 Comment(0)
F
2

I finally accomplished this, for a navigation or tab bar interface, by combining an overlay view controller (see: pix0r's answer) that's hidden / un-hidden before hiding or showing a view controller based on this very good blog post.

Concerning the view controller, the tip is to make its background view the clearColor, then the semi-transparent overlay view is visible and whatever views are added as subviews in the view controller are in front and most importantly opaque.

Febri answered 26/4, 2010 at 16:38 Comment(0)
K
2

I've been researching this same issue for the past week. I tried all the various answers and examples found in Google and here on StackOverflow. None of them worked that well.

Being new to iOS programming, I wasn't aware of something called UIActionSheet. So if you're trying to accomplish this in order to show a modal overlay of buttons (such as a modal asking someone how they want to share something), just use UIActionSheet.

Here is a webpage that shows an example of how to do this.

Knox answered 18/3, 2012 at 20:40 Comment(0)
C
2

I got this idea from https://gist.github.com/1279713

Prepare: In the modal view xib (or scene using storyboard), I setup the full-screen background UIImageView (hook it with the .h file and give it a property "backgroundImageView") with 0.3 alpha. And I set the view (UIView) background color as plain black.

Idea: Then in "viewDidLoad" of the modal view controller I capture the screenshot from the original status and set that image to the background UIImageView. Set the initial Y point to -480 and let it slide to Y point 0 using 0.4-second duration with EaseInOut animation option. When we dismiss the view controller, just do the reverse thing.

Code for the Modal View Controller Class

.h file:

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

.m file:

- (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) {

    }];
}

In viewDidLoad, simply call:

[self backgroundInitialize];
[self backgroundAnimateIn];

In anywhere we dismiss the modal view controller, we call:

[self backgroundAnimateOut];

Please note that this will ALWAYS animate the background image. So if this modal view controller transition style (or the segue transition style) is not set to "Cover Vertical", you may not need to call the animation methods.

Choli answered 23/4, 2012 at 1:20 Comment(3)
This is a good idea. Out of curiosity, would there be a way to make this solution work if the view controller needed to support rotation? As in, the semi-transparent popup and view controller underneath would rotate together?Houseboat
I am not sure how your app would "rotate". I assume you are saying to rotate the main view. If this is the case, I have 2 suggestions: First is easier: create a second view and set it as the same size as the main view but DO NOT put this view as a subview of the main view. Instead, Set this 2nd view at the same hierarchy level as the main view. The next important thing is to put the background image view into this 2nd view. So if you are animate the main view, the 2nd view is not affected. The 2nd suggestion is: (see next comment as the character limitation exceeds)Choli
2nd suggestion: Still keep this background in the main view as a subview. Whenever you are rotating the main view, I assume you are updating its "transform" property and add the rotation angle to it. If this is the case, then you can add the same amount "BUT NEGATIVE" of the rotation angle to the background image view's transform. this should keep the background image view sit at its fixed position.Choli
R
1

I've created open soruce library MZFormSheetController to present modal form sheet on additional UIWindow. You can use it to present transparency modal view controller, even adjust the size of the presented view controller.

Rutheruthenia answered 10/8, 2013 at 17:59 Comment(0)
R
1

For iOS 8+ you can use UIModalPresentationOverCurrentContext presentation style for presented view controller to easy achieve desired behavior.

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

If you also need to support iOS 7 - check this thread.

Runion answered 16/9, 2015 at 19:39 Comment(0)
A
0

You can achieve transparent/semi-transparent modal view effect by overlaying a transparent/semi-transparent button on both the view and the navigation bar.

You can access the navigation bar through the navigationBar property of the UINavigationController.

I found that UIButton unlike UILabel will trap mouse events - hence giving the correct modal behavior.

Alkalinity answered 29/1, 2010 at 16:29 Comment(0)
R
0

I just found a workaround for that. Just create a 1X1 of UIViewController and add it to your parent view controller. And show the transparent modal view controller in that UIViewController.

on viewDidLoad;

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

when you need to open a transparentViewController;

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

Rheo answered 29/10, 2012 at 10:39 Comment(0)
A
0

If you need a screen like the attached one, the below code may help you.enter image description here

The code:

MyViewController * myViewController = [[MyViewController alloc] initWithNibName:nibName bundle:nil];
UINavigationController * myNavigationController = [[UINavigationController alloc] initWithRootViewController: myViewController];
myNavigationController.modalPresentationStyle = UIModalPresentationPageSheet;
[self presentModalViewController: myNavigationController animated:YES];
Anodic answered 28/2, 2014 at 13:5 Comment(0)
D
0

If say you want a screen overlay, use the parentViewController.view, it will place above navigation bar ++

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

Downwards answered 18/10, 2014 at 14:2 Comment(0)
P
0

This worked for me:

UIViewController *modalViewController = [[UIViewController alloc] init];
modalViewController.view.backgroundColor = [UIColor whiteColor] colorWithAlpha:0.5];
[self showDetailViewController:modalViewController sender:nil];
Pestilent answered 26/4, 2015 at 11:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.