9

I would like to have a modal view with a transparent background color, so that the view behind it can be seen by user.

BadPirate
  • 25,802
  • 10
  • 92
  • 123
Rizki
  • 281
  • 1
  • 6
  • 19
  • 1
    you can use the [UIcolor clearcolor]; – Raxit Dec 27 '10 at 09:13
  • What modal dialog are you using? – Eiko Dec 27 '10 at 09:16
  • @Raxit: when i set the background color with clear color that not make we can see the view behind the modal. – Rizki Dec 27 '10 at 10:07
  • @BoltClock: yes, this is for iOS @Eiko: what do you mean with What modal dialog am i using? – Rizki Dec 27 '10 at 10:08
  • In your question you have mentioned "I want the view behind modal can be seen by user." so is it right ? or you want to say something different. Please describe what exactly you wanted to do ? – Raxit Dec 27 '10 at 10:24
  • @Raxit: I used form sheet modal so the modal is beetween transparent view. From that transparent view, we can look the view behind. So, I want my modal can look like that transparent view. When I used clear color that just diplay grey color not the view behind the modal. – Rizki Dec 28 '10 at 03:15
  • possible duplicate of [Transparent Modal View on Navigation Controller](http://stackoverflow.com/questions/849458/transparent-modal-view-on-navigation-controller) – Ben Regenspan Jan 31 '13 at 23:22

5 Answers5

10

Okay, so presentModalViewController doesn't offer this behavior... However it is still possible. I've created a category that works for me (and hopefully you). As an added bonus, it also prevents crashes related to dismissing and presenting modal views at the same time!

Header file:

//
//  UIViewController+overView.h
//  Created by Kevin Lohman on 5/30/12.
//

#import <UIKit/UIKit.h>

@interface UIViewController (OverView)
- (void)presentOverViewController:(UIViewController *)modalViewController animated:(BOOL)animated;
- (void)dismissOverViewControllerAnimated:(BOOL)animated;
@end

Implementation File:

//
//  UIViewController+overView.m
//  Created by Kevin Lohman on 5/30/12.
//

#import "UIViewController+overView.h"

@implementation UIViewController (OverView)

#define kUIViewControllerOverViewDismissNotification @"OverViewDismissNotification"

const float kUIViewControllerOverViewAnimationDuration = 0.75;
const NSInteger kUIViewControllerOverViewTag = 8008135; // Arbitrary number, so as not to conflict
- (void)overViewDismissed
{
    [self autorelease];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:kUIViewControllerOverViewDismissNotification object:self.view];
}

- (void)presentOverViewController:(UIViewController *)modalViewController animated:(BOOL)animated
{
    UIView *toView = self.view;

    CGRect finalRect = CGRectIntersection([[UIScreen mainScreen] applicationFrame], self.view.frame); // Make sure it doesn't go under menu bar
    modalViewController.view.frame = finalRect;
    modalViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    modalViewController.view.tag = kUIViewControllerOverViewTag+modalViewController.modalTransitionStyle; // Hiding some info here :)

    if(animated)
    {
        switch(modalViewController.modalTransitionStyle)
        {
            // Currently only cross dissolve and cover vertical supported... if you add support let me know.
            case UIModalTransitionStyleCrossDissolve:
            {
                float beforeAlpha = modalViewController.view.alpha;
                modalViewController.view.alpha = 0;
                [toView addSubview:modalViewController.view];
                [UIView animateWithDuration:kUIViewControllerOverViewAnimationDuration animations:^{
                    modalViewController.view.alpha = beforeAlpha;
                }];
                break;
            }
            case UIModalTransitionStyleCoverVertical:
            default:
            {
                modalViewController.view.frame = CGRectMake(modalViewController.view.frame.origin.x, modalViewController.view.frame.size.height, 
                                                            modalViewController.view.frame.size.width, modalViewController.view.frame.size.height);
                [toView addSubview:modalViewController.view];
                [UIView animateWithDuration:kUIViewControllerOverViewAnimationDuration animations:^{
                    modalViewController.view.frame = finalRect;
                }];
                break;
            }
        }
    }
    else {
        [toView addSubview:modalViewController.view];
    }

    [modalViewController retain]; // Keep it around until we dismiss it.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(overViewDismissed) name:kUIViewControllerOverViewDismissNotification object:modalViewController.view]; // Release will happen when this notification is posted
}

NSInteger transitionStyleForTag(tag)
{
    if (tag >= kUIViewControllerOverViewTag && tag <= kUIViewControllerOverViewTag+UIModalTransitionStylePartialCurl)
    {
        return tag-kUIViewControllerOverViewTag;
    }
    else {
        return -1; // Not a Over View
    }
}

- (void)dismissOverViewControllerAnimated:(BOOL)animated
{
    UIView *overView = transitionStyleForTag(self.view.tag) >= 0 ? self.view : nil; // Can dismiss ourselves
    for(UIView *subview in self.view.subviews)
    {
        if(transitionStyleForTag(subview.tag) >= 0)
            overView = subview; // Keep going, lets dismiss last presented first
    }
    if(!overView) return; // None to dismiss

    if(animated)
    {
        switch(transitionStyleForTag(overView.tag))
        {
            // Currently only cross dissolve and cover vertical supported... if you add support let me know.
            case UIModalTransitionStyleCrossDissolve:
            {
                float beforeAlpha = overView.alpha;
                [UIView animateWithDuration:kUIViewControllerOverViewAnimationDuration animations:^{
                    overView.alpha = 0;
                } completion:^(BOOL finished) {
                    [overView removeFromSuperview];
                    overView.alpha = beforeAlpha;
                    [[NSNotificationCenter defaultCenter] postNotificationName:kUIViewControllerOverViewDismissNotification object:overView];
                }];
                break;
            }
            case UIModalTransitionStyleCoverVertical:
            default:
            {
                [UIView animateWithDuration:kUIViewControllerOverViewAnimationDuration animations:^{
                    overView.frame = CGRectMake(0, overView.frame.size.height, overView.frame.size.width, overView.frame.size.height);
                } completion:^(BOOL finished) {
                    [overView removeFromSuperview];
                    [[NSNotificationCenter defaultCenter] postNotificationName:kUIViewControllerOverViewDismissNotification object:overView];
                }];
                break;
            }
        }
    }
    else {
        [overView removeFromSuperview];
        [[NSNotificationCenter defaultCenter] postNotificationName:kUIViewControllerOverViewDismissNotification object:overView];
    }
}
@end

And then to use it, simply use presentOverViewController instead of presentModalViewController and dismissOverViewController instead of dissmissModalViewController.

There are a couple of limitations:

  1. You have to present to the root-most view controller, so if you want to cover the whole screen and you've got a VC inside of a UINavigationController, present it to the navigation controller.
  2. May be some issues with rotation (I didn't see any) on older iOS builds (4.0 or so)
  3. Currently only handles Cross Dissolve and Cover Vertical animations.
BadPirate
  • 25,802
  • 10
  • 92
  • 123
  • Nice and tidy solution, thanks! Two observations though. First, notification's handler `overViewDismissed` releases self instead of the modal view controller and that leads to crash. Remove the notification code addObserver/postNotification altogether and manage modal view controller's memory outside present/dismiss methods. Second, `[[UIScreen mainScreen] applicationFrame]` does not always return the correct frame size. I use `self.view.superview.frame` instead (see [this answer](http://stackoverflow.com/a/4052442/22764)). – Vladimir Grigorov Jan 14 '13 at 09:33
  • @VladimirGrigorov hey thanks for the note, it does seem to have some bad logic there. I don't remember which codebase I put this in, but imagine I fixed it locally... if you are still compiling / running a fixed version of it somewhere, could you edit the answer based on your findings? I could change it (is it just change [self autorelease] to [self.parentViewController autorelease] ? Risky for me to make the change blindly :) – BadPirate Jan 15 '13 at 19:27
  • I have already removed the notification code as I stated in my previous comment and that version has passed testing... – Vladimir Grigorov Jan 18 '13 at 15:13
  • There's also a this library that can do this: https://github.com/martinjuhasz/MJPopupViewController – Shiki Mar 09 '13 at 00:48
9

The answer for me was one line of code, added to the parent view controller before presenting the modal view controller:

self.modalPresentationStyle = UIModalPresentationCurrentContext;

This will stop the parent view from being removed once the modal view has been added.

jowie
  • 8,028
  • 8
  • 55
  • 94
  • 6
    But this works self.navigationController.modalPresentationStyle = UIModalPresentationCurrentContext; – surfrider Feb 05 '13 at 09:54
  • Did you set it on the parent view controller? And did you then use the command `presentViewController:animated:completion:`? I have to admit I have only tried this on iPad and not iPhone, but I don't see that would make a difference. – jowie Feb 05 '13 at 14:20
3

iOS doesn't support transparency when presenting a view modally.

machunter
  • 967
  • 1
  • 11
  • 27
0

Probably you want to add a UIView as an overlay to your current view but do it fullscreen with a transparent or semi-transparent background. See the first answer to this question:

disable elements on UIView behind modal UIView

Community
  • 1
  • 1
DoctorG
  • 1,168
  • 7
  • 11
0

You need to work it backwards, sort of. Load your modal view, then add all your elements on it "manually" as Subview, this way they wont be hidden behind. Yes, it is a lot of coding, but can be done. I did it for playing movies and having extra elements on the screen by setting smaller frame size for movie player and throwing some buttons and textviews around it as Subviews. B

Boris Gafurov
  • 1,427
  • 16
  • 28