I believe this to be a bug in iOS5. I have done some experimenting with presenting modal view controllers from other modals and it seems the second modal NEVER dims the screen underneath. I even setup a test project that allowed you to launch endless modals from each other and it seem every other modal doesn't dim or block touches as expected.
A quick NSLog on the UIWindow subviews shows us that while the drop shadow is being added appropriately the dimmingView is not. I'm working on a way to show my own dimming view. Will update this answer when I've found a way.
Window Subviews: (
"<UILayoutContainerView: 0xf63e3c0; frame = (0 0; 768 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0xf645640>>",
"<UIDimmingView: 0xf683990; frame = (0 0; 768 1024); opaque = NO; layer = <CALayer: 0xf6836d0>>",
"<UIDropShadowView: 0xf683130; frame = (64 64; 640 896); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0xf6831c0>>",
"<UIDropShadowView: 0x292110; frame = (74 242; 620 540); transform = [0, 1, -1, 0, 0, 0]; autoresize = LM+RM+TM+BM; layer = <CALayer: 0x292150>>"
)
Solution Two:
So in my final solution for animated look. Also I got to thinking about my first solution and its technically possible this would piss off Apple and cause a rejection since UIDimmingView is undocumented and we "touch it." I add a UIView with a background color the alpha we want to my viewController. then I animated it when I present the modal and I reverse the animation when the delegate of the second modal gets called. It looks pretty good to me. Maybe some timing and alpha tweaks to get it JUST right but its working and looks nice.
- (void)viewDidLoad
{
dim = [[UIView alloc] init];
[dim setBackgroundColor:[UIColor colorWithWhite:0.0 alpha:0.35]];
[dim setAlpha:0.0];
[dim setUserInteractionEnabled:NO];
[self.view addSubview:dim];
}
- (void)presentModal
{
[self.view bringSubviewToFront:dim];
[dim setFrame:self.view.frame];
[UIView animateWithDuration:0.25 animations:^{
[dim setAlpha:1.0];
}];
}
- (void)modalDelegateFinished
{
[UIView animateWithDuration:0.25 animations:^{
[dim setAlpha:0.0];
}];
}
Solution One:
Alright so this works but it isn't as animated as I'd like. However it does reuse whats already there so theres probably a plus for that.
- (void)viewDidAppear:(BOOL)animated
{
// Add a gesture to dismiss the view if tapped outside.
UIGesture *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedOutsideView:)];
[gesture setNumberOfTapsRequired:1];
[gesture setCancelsTouchesInView:NO];
[self.view.window addGestureRecognizer:gesture];
// Move the dimmingview to just below the dropshadow.
UIView *dim = [self.view.window.subviews objectAtIndex:1];
[self.view.window insertSubview:dim atIndex:2];
}
- (void)tappedOutsideView:(UITapGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded) {
CGPoint location = [sender locationInView:nil];
if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) {
// remove the gesture on the window
[self.view.window removeGestureRecognizer:sender];
// Move the dimmingview back where it belongs
UIView *dim = [self.view.window.subviews objectAtIndex:2];
[self.view.window insertSubview:dim atIndex:1];
}
}
}
Also as a failsafe its probably a good idea to the same stuff in the viewDidDisappear. My done button calls tappedOutside view so I know the gesture and dimmingView are always put right. But if you didn't do it that way you could put it in the viewDidDisappear.
- (void)viewDidDisappear:(BOOL)animated
{
// remove the gesture on the window
for (UIGestureRecognizer *gesture in self.view.window.gestureRecognizers) {
[self.view.window removeGestureRecognizer:gesture];
}
// Move the dimmingview back where it belongs
UIView *dim = [self.view.window.subviews objectAtIndex:2];
[self.view.window insertSubview:dim atIndex:1];
}