IPhone - After dismissing Modal View Controller -

2019-01-31 20:28发布

问题:

When starting the app, if the user doesn't have login information stored, I want to display a modal view controller to force the entry of this information. I found through trial and error, that this had to occur in viewDidAppear of my root view controller. I tried to put it in viewDidLoad and viewWillAppear, but those didn't work unless I assigned the view of the root view controller to the view of the navigation controller used in the modal which then caused other issues...

So I have:

- (void)viewDidAppear:(BOOL)animated
{
     NewAccountViewController *newAccountViewController = [[[NewAccountViewController alloc] initWithNibName:@"NewAccountViewController" bundle:nil] autorelease];

     UINavigationController *accountNavigationController = [[UINavigationController alloc] initWithRootViewController:newAccountViewController];
  [self presentModalViewController:accountNavigationController animated:YES];       
}

And in the newAccountViewController I have a simple navigation item button that dismisses the modal view controller with dismissModalViewController.

This all works and when the modal is dismissed a view in a navigation controller is visible with its navigation item title at the top....

But there is a white gap about the same size as the status bar between the status bar and the top of the blue navigation item bar. If I don't do the modal, then the gap is never there. It only occurs after the modal is presented and dismissed. I've tried doing animated:NO on both the present and dismissModalViewController. I've also tried not using the navigation controller in the modal, and that did nothing as well. Any ideas would be great! Thanks.

回答1:

I had the same problem. My solution was to temporarily close the status bar just before switching views:

- (void) temporarilyHideStatusBar {
  [[UIApplication sharedApplication] setStatusBarHidden:YES];
  [self performSelector:@selector(showStatusBar) withObject:nil afterDelay:0];
}
- (void) showStatusBar {
  [[UIApplication sharedApplication] setStatusBarHidden:NO];
}

// Use these by calling the hide function just before a broken view switch:
[self temporarilyHideStatusBar];
[self doViewSwitch];

// No need to call [self showStatusBar]; explicitly.

I experimented with other code-based solutions, and I like this one the best because it works 100% of the time - some of my frame-based solutions only worked most of the time - and because it has minimal user-visible effects.

I suspect this is an Apple bug, and it would be nice to hear the official word from them on the best workaround.



回答2:

Turns out this was happening because I was calling my modalviewcontroller on the current view controller, but my view controller already had a another view controller loaded as a subview. Once I changed it to make the view controller in the subview, load the modal, then it went away. Thanks for all your help.



回答3:

I ran into the same issue. Not sure what causes it, but I fixed it with the following line of code just after I dismiss my modal view:

[self.view setFrame:CGRectMake(0, 10, self.view.frame.size.width, self.view.frame.size.height)];

Just adjust the Y offset to meet your needs. In another instance I had to make it 20 instead of 10.



回答4:

Just wanted to chime and say I had the exact opposite problem - a white gap at the bottom of the screen. And the fix was also the opposite, I was presenting from a subview, when I needed to be presenting from the parent. Thanks!



回答5:

In case anyone looks at this post (I did today and so others might). I am a newbie to objective c but and you may laugh at my suggestion but here goes.

When running my iPad app the status bar was always overlapping my form. I wasn't happy to use the method for changing the frame coordinate down 20px so went searching.

Found that if I was to assign my custom view controller to the window rootViewController then the status bar overlap problem went away. I haven't tried the modal stuff in this post but hope this helps other newbies who may be wondering how to sole this problem.

This is what a simple AppDelegate didFinishLaunchingWithOptions method would look like:

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.

    TEGGameViewController *gameVC = [[TEGGameViewController alloc] init];

    self.window.rootViewController = gameVC;

    [self.window makeKeyAndVisible];
    return YES;
}


回答6:

OK, I do not know why this happens, it happened to me though and ill tell you how i fixed it. I found that adding a view at 0,0 made it overlap with the status bar...this is weird because before 3.0 I never had projects do this behavior 0,0 was always underneath the status bar. Anyway so I saw that it was overlapping so i started putting my views at 0,20 or however big the status bar is. That was cool until I tried using a modal view controller, when it dismissed i found the gap the size of the status bar (what you are expiriencing), for some reason the modal view thinks 0,0 DOES NOT overlap the status bar and when it dismisses 0,20 (of your previous view) shows up as if (0,0) does not over lap and (0,20) has the extra white gap. Why did this start happening ? No clue. Anyway to fix it i knew i had to fix the whole overlap of the view and status bar at 0,0 thing. I kept trying but nothing, then I decided to try to create a new View based project in xcode (which sets up the initial view for you) and see if their view overlaps (or if they put it at 0,20), what i found is that it DID NOT overlap and 0,0 was the right placing, right below the status bar and not overlapping it. Bizzare? i think so. So what i did on my project was copy the set up they had in the newly created view based project (they set the viewControllers property through Interface Builder, you can create a view based project like I did and just mimic their set up), i found that this fixed the issue, views at 0,0 no longer overlapped the status bar and dismissing modal view controllers no longer left the gap. Dont know if this is your case, but it might be, hopefully this will help you.



回答7:

I had the exact same problem... my solution? Manually set the height of the view you're displaying modally to 480 px in interface builder.

Problem solved.



回答8:

The given answer is hard to understand, more like a vague hint, so it's a try to explain more detailed:

The problem is that the root view controller which viewDidAppear: method is presented in the question looks like to a be simple UIViewController. And how we can see in Apple's documentation for wantsFullScreenLayout:

The default value of this property is NO, which causes the view to be laid out so it does not underlap the status bar

This root view controller presents UINavigationController modally, so presented navigation controller uses not the whole screen. Also it seems that default UINavigationController's value for wantsFullScreenLayout is YES. That's why navigation controller adds the gap — to avoid underlapping the status bar with navigation bar.

So there are several ways to solve it:

1) Present navigation controller with wantsFullScreenLayout property set to NO. (Or to present ~fresh UIViewController with a UINavigationController's view as a subview)

2) Change the rootViewController property of UIWindow to navigation controller manually, so navigation controller is presented on the whole window. Can use it e.g. when the first screen is kind of disposable pin/password input, and we can easily drop it after successful login and change for navigation controller.



回答9:

I am running into the same problem. I admit that I may have taken a nontraditional route, embedding a tab bar in a NavController, and now I'm kinda seeing why they say not to do it. Well, whatever, it makes sense for my app, even if it is a total pain to implement.

My problem is that once I pushed a Modal VC onto a TabController subview (which is, in turn, a subview of the overarching NavController View, which has control of the top NavBar... which turns into a delegation nightmare) and then dismiss the MVC, the view that comes back is pushed up under the NavBar. Doesn't happen when I add or push any other subviews, just with MVCs.

[self.view setFrame:(CGRect)] does seem to do the trick, if I call it right after [self dismissModal...]. However, it has to be called on all subviews of my tab bar controller, or even the other lists are shunted up under the bar. Poopy.

Makes things a headache, but not unsolvable. But I agree, this is a new bug with 3.x (or maybe it's one of those double-edged "new features"), and it's downright annoying. I'm downloading 3.1.2 SDK now, and hopefully it addresses this. Gonna comment my [setFrame] lines and see what happens. But hopefully, if it's not solved in this version, it will be soon. Until then, it seems that the solution is a lot of (admittedly hackerish and repetitive) code.

EDIT: Nope, updating didn't help. Poop. Back to the code I go, to hard-define the position of every screen. Awesome. Hopefully the size of Nav and Tab Bars doesn't ever change.

EDIT 2: Hey! I solved it! (i R prowd.) Seems the problem was that, when my MVC was dismissed, my view got passed back to the tab bar controller, which had "forgotten" it had a nav bar above it. Don't tell me how, I'm probably way out of documented territory by now. But, I solved my particular problem by simply redefining the [subview.view setFrame:0,0,width, height] after returning from editing. Not sure why this made any difference, but in the app it did, because no longer is the first one and a half rows of my table view sneaking up under the nav bar. So I really have no advice, just find the right place to put

[(appropriateViewController).view setFrame:CGRectMake(0,0,self.view.frame.size.width, self.view.frame.size.height];

Not sure why it works but it does. Isn't that the worst kind of error?



回答10:

I used @Mark Hammonds answer, but simply reset the frame's offset to 0, ie:

// this won't be called on the parent view if a modal dialog is dismissed. [self.parentViewController viewWillAppear: animated]; 
// actually dismiss the modal dialog. 
[[TOSplitViewController active] dismissModalViewControllerAnimated: animated]; 
// fix the mysterious white bar that appeared on the top. 
[[TOSplitViewController active].view setFrame:CGRectMake(0,0, [TOSplitViewController active].view.frame.size.width, [TOSplitViewController active].view.frame.size.height)];

As you might guess, this was while trying to dismiss a modal dialog presented on top of a split view on an iPad application.

I did try @Tyler's answer, and it didn't work for me.

This on iOS4.1, targeting iPad 3.2.



回答11:

I've run into this a few times and did the workaround (re-setting the frame) because of time pressure, but the last time I ran into it I found a real fix. Instead of creating your top level view controller in code (if it is), try having it be created in the UIWindow and IBOutlet it to the AppDelegate instead.

Worked for me.



回答12:

I find that both presenting and dismissing modal view controllers cause this, especially when you want to do custom layouting in -viewDidLoad (which some say you shouldn't, but I've never found a better, working way). What I do is:

[self.view addSubview:self.viewController1.view]; // I suppose this layouts all (sub)views.
[self.viewController1.view removeFromSuperview]; 

Now you can present as such:

    self.viewController1.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    [self.viewController2 presentModalViewController:self.viewController1 animated:YES];

without risking viewController1 to be laid out in a foul way until transition done, then snapping to wherever it should be. The same applies to dismissing. Works for me on iOS 3.0 though 5.0, iPhone and iPad.



回答13:

I found a more "elegant" way of doing this. If you add a top view to your window, which is the same size as the window, and add subviews to that top view instead of the window, all subviews will be offset by 20 pixels in the right direction, status bar visibile, no gaps anywhere and even rotation will work correctly. I tested it with iPad and iPhone 4.3 and 5.0... What's the catch? ;)



回答14:

You could probably place this code when you dismiss the UIImagePickerController.

self.view.frame = CGRectMake(0, 0, 320, 480);


回答15:

For me, my modal view controller was a MoviePlayer and when it would be presented, it seemed that the status bar would be redrawn. When the view was dismissed, this second status bar would somehow create a 20px offset at the bottom of my screen, just above the tabbar controller.

To fix this, when the modal view controller was presented, I would hide the status bar. When the movie had finished playing and the modal view controller was going to be dismissed, I made the status bar visible.

- (void) moviePlayerLoadStateChanged:(NSNotification*)notification 
{
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}


- (void) moviePlayBackDidFinish:(NSNotification*)notification 
{
[[UIApplication sharedApplication] setStatusBarHidden:NO];
}


回答16:

I've had the same problem and here is my solution. Just add this to your AdDelegate:

- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
    if (!willLeave)
        [[UIApplication sharedApplication] setStatusBarHidden:YES animated:YES];
    return YES;
}

- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
    [[UIApplication sharedApplication] setStatusBarHidden:NO animated:YES];
}

It's very simple; when the ad appears I hide the status bar and when the ad disappears I show it again. I hope the answer helps some people.