6

I have a view controller which is not fullscreen (has a status bar) and want to present a modal view controller which is fullscreen.

If I hide the status bar at the beginning of the animation (parent's viewWillDisappear or modal's viewWillAppear) then for a moment the parent will be visible without a status bar, looking like a bug.

If I do it at the end of the animation (parent's viewDidDisappear or modal's viewDidAppear) then the status bar will be visible for a moment over the modal view, i.e. it won't appear as the modal view "covered it".

Is there a way to do this nicely?

edit:

One possibility would be to create a UIWindow with windowLevel=alert for at least the duration of the animation. The sample iAd ad seems to cover the status bar nicely without another window, so it must be possible somehow.

WrightsCS
  • 50,551
  • 22
  • 134
  • 186
Jaka Jančar
  • 11,386
  • 9
  • 53
  • 74
  • I think that your UIWindow solution is probably the right way to go. We use similar techniques in our apps when we need unusual transitions between view controllers. – Jesse Rusak Dec 18 '11 at 12:48

5 Answers5

4

Another fun little project. This was the best I could come up with. It's not too bad if you don't mind using your own container controller to manage presenting/dismissing view controllers. I try to do things in a general way but this could be rolled into an app w/ the ContainerViewController if desired.

Note that I only implemented the equivalent of UIModalTransitionStyleCoverVertical. You can customize the animation however you like as well.

Relevant animation code:

- (void)presentViewController:(UIViewController *)viewControllerToPresent
{   
    // do nothing if no controller
    if (!viewControllerToPresent) return;

    [__viewControllers addObject:viewControllerToPresent];
    CGRect toFrame = viewControllerToPresent.view.frame;
    toFrame.origin = CGPointMake(0, CGRectGetMaxY(self.view.bounds));
    viewControllerToPresent.view.frame = toFrame;

    [UIView transitionWithView:self.view
                      duration:0.2
                       options:UIViewAnimationOptionTransitionNone
                    animations:^{
                        [[UIApplication sharedApplication] setStatusBarHidden:viewControllerToPresent.wantsFullScreenLayout withAnimation:UIStatusBarAnimationSlide];
                        [self.view addSubview:viewControllerToPresent.view];
                        viewControllerToPresent.view.frame = [UIScreen mainScreen].applicationFrame;
                    }
                    completion:nil];
}

- (void)dismissViewController
{
    // nothing to dismiss if showing first controller
    if (__viewControllers.count <= 1) return;

    UIViewController *currentViewController = [__viewControllers lastObject];
    UIViewController *previousViewController = [__viewControllers objectAtIndex:__viewControllers.count - 2];

    [UIView transitionWithView:self.view
                      duration:0.2
                       options:UIViewAnimationOptionTransitionNone
                    animations:^{
                        [[UIApplication sharedApplication] setStatusBarHidden:previousViewController.wantsFullScreenLayout withAnimation:UIStatusBarAnimationSlide];
                        CGRect toFrame = currentViewController.view.frame;
                        toFrame.origin = CGPointMake(0, CGRectGetMaxY(self.view.bounds));
                        currentViewController.view.frame = toFrame;
                    }
                    completion:^(BOOL finished) {
                        [currentViewController.view removeFromSuperview];
                        [__viewControllers removeLastObject];
                    }];
}
tcurdt
  • 14,518
  • 10
  • 57
  • 72
XJones
  • 21,959
  • 10
  • 67
  • 82
0

I do that in my app with this code:

[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleBlackOpaque];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation: UIStatusBarAnimationSlide ];


DocumentListViewController * dl = [[DocumentListViewController alloc] initWithNibName:@"DocumentListView" bundle:nil] ;
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:dl];
[dl release];

// Go to the list of documents...
[[self.view superview] addSubview:nav.view];

nav.view.alpha = 0.0 ;

[self hideActivityAlert];

[UIView animateWithDuration:1.0 animations:^{
    nav.view.alpha = 1.0; } completion:^(BOOL A){
        [self.view removeFromSuperview];
        [self release];} ];

The status bar is presented shoftly while the animation occurs.

You have to be sure that the first view, when status bar is going hidden will fill the space. Use the property autoresizingMask with proper value.

Gabriel
  • 3,319
  • 1
  • 16
  • 21
  • Gabriel, but this is a fade, not a slide animation, if I understand correctly. Perhaps I should have been more specific. – Jaka Jančar Dec 11 '11 at 17:25
  • Ok, now I understand what you want. You want the modal view to appear with the standard animation from the bottom and covering the status bar. But I think that is not possible as status bar is for application and not for a view. So, the most you can do is, as in my code, hide the status bar in the same animation. Then the bar hides animated while the modal view appears. – Gabriel Dec 11 '11 at 20:04
0

Here's a solution that seems to work. You can derive the viewcontroller you want to present modally from my TSFullScreenModalViewController, or you can just implement the code right in the view controller itself.

@interface TSFullScreenModalViewController : UIViewController
{
    UIWindow*   _window;
}

- (void) presentFullScreenModal;

@end

@implementation TSFullScreenModalViewController

- (void) viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear: YES];

    [_window resignKeyWindow];
    [_window release];
    _window = nil;
}

- (void) presentFullScreenModal
{
    UIViewController* rvc = [[UIViewController new] autorelease];
    rvc.view.backgroundColor = [UIColor clearColor];

    _window = [[UIWindow alloc] initWithFrame: [UIScreen mainScreen].bounds] ;
    _window.windowLevel = UIWindowLevelStatusBar+1;
    _window.backgroundColor = [UIColor clearColor];
    _window.rootViewController = rvc;
    [_window makeKeyAndVisible];

    [UIApplication sharedApplication].statusBarHidden = YES;
    [rvc presentModalViewController: self animated: YES];
    [UIApplication sharedApplication].statusBarHidden = NO;
}

@end

Derive your modal view controller, like this:

@interface MyModalViewController : TSFullScreenModalViewController
{
}

- (IBAction) onDismiss:(id)sender;

@end

Use it from another view controller, like this:

- (IBAction) onShowModal:(id)sender
{
    MyModalViewController* mmvc = [[MyModalViewController new] autorelease];
    [mmvc presentFullScreenModal];
}

Finally, dismiss your view controller as you normally would:

- (IBAction) onDismiss:(id)sender
{
    [self dismissModalViewControllerAnimated: YES];
}
TomSwift
  • 39,369
  • 12
  • 121
  • 149
  • I already wrote in my question about the UIWindow approach, but I don't want to use it since there are a bunch of other problems with having a window at status bar or alert levels. I've checked an iAd doesn't use another window. – Jaka Jančar Dec 17 '11 at 11:24
  • so your new requirement is not to use a window that sits above the status bar. it would have been nice to know this before I spent time figuring out a solution. – TomSwift Dec 18 '11 at 21:39
  • Tom, I've added that 2 days before your answer. – Jaka Jančar Dec 22 '11 at 09:08
0

Might be a bit of a hack but have you considered:

  1. Take a screenshot programatically of the first view with the status bar (see this SO question)
  2. Create a new view which displays the image you just took in fullscreen (using UIImage's initWithFrame)
  3. Hide the status bar
  4. Present the modal view controller

Then to dismiss the modal view, just reverse the steps.

EDIT:

Won't work for this because you can't take screenshots of the status bar.

Community
  • 1
  • 1
lmirosevic
  • 15,787
  • 13
  • 70
  • 116
  • You cannot make a screenshot of the status bar. UIGetScreenImage() used to do it, but you can't do it anymore. Status bar is it's own window, and you can't get a reference to it, afaik. – Jaka Jančar Dec 18 '11 at 06:51
0

It could be as simple as delaying the presentation of your modalViewController using performSelector:withDelay:

Tell the status bar to animate out and then launch the modal controller with the right delay so it coincides with the status bar animation.

Dancreek
  • 9,524
  • 1
  • 31
  • 34