views:

45

answers:

3

I am running an "iPhone-only" app in the iPad simulator...When the orientation of the device is changed to landscape mode, I have a view controller that kicks in and programmatically loads a WebView. This works swimmingly in the iPhone (no gap on top of landscape view), but when simulating in the iPad, there's a 20px (I think?) gap at the top of the view.

Here's the code in the landscape view controller's viewDidLoad where I load the WebView:

    [super viewDidLoad];

// Initialize webview and add as a subview to LandscapeController's view
CGRect webFrame = [[UIScreen mainScreen] bounds]; // Use bounds to take up entire screen
self.myWebView = [[[UIWebView alloc] initWithFrame:webFrame] autorelease];
self.myWebView.scalesPageToFit = YES;
self.myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
self.myWebView.delegate = self;
[self.view addSubview: self.myWebView]; 

// remove status bar from top of screen
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackOpaque];

Setting the status bar to default, has no influence.

I can't seem to figure out why this would be fine on the iPhone, but emerge on the iPad??? Any ideas? Thanks in advance!

A: 

20 pixels at the top is almost always invariably related to the height of the status bar.

The code you posted looks fine... for setting up the UIWebView. But you're adding it as a subview to your view controller's view. How is that sized? What is its frame?

Shaggy Frog
The frame size is: width=320px, height=460px...which leaves that dastardly 20px gap in the height, I'm guessing...
Sly
A: 

The screen bounds are in screen coordinates. You're adding the webview as a subview of self.view; its frame is in self.view coordinates. You want to fill your view, not the screen (your view is automatically resized by UIViewController/the rest of UIKit, which should end up resizing the web view to to auto-resizing):

self.myWebView = [[[UIWebView alloc] initWithFrame:self.view.bounds] autorelease];

It's not safe to change the status bar in -viewDidLoad. View-loading can happen anywhere (it happens when anything calls viewController.view). I'm also not sure why you're setting the style; you want to set hidden-ness:

  • In -viewWillAppear:, do [application setStatusBarHidden:YES animated:animated];
  • In -viewWillDisappear:, do [application setStatusBarHidden:NO animated:animated];

Finally, you might be seeing different behaviour because the iPad is running OS 3.2.x and the phone is running 3.1.x or 4.x. Additionally, the iPhone-compatibility mode uses a dummy status bar; the "real" status bar always stays at the edges of the screen.

tc.
Thanks tc! Your comment about setStatusBarHidden got me pointed in the right direction!!
Sly
By the way, properly removing the status bar in this way, resulted in both the iPhone and iPad landscape views both containing the white gap in the top of the view. While this didn't fix the core problem, it showed that not calling setStatusBarHidden wasn't the problem.
Sly
It works when I do it...
tc.
A: 

I finally figured this out. The key missing component, which I didn't mention in the original post, is that the view controller that manages landscape is actually implemented as a modal view. (See the View Controller User Guide for code on how to do this) In concept, I have a Portrait view controller. (which is the primary controller) In the Portrait view controller's viewDidLoad I apply for a Notifier that is triggered off of a change in the orientation like so:

- (void)viewDidLoad {
[super viewDidLoad];

// SECTION to setup automatic alternate landscape view on rotation
// Uses a delegate to bring the landscape view controller up as a modal view controller

isShowingLandscapeView = NO;
// Create Landscape Controller programmatically
self.landscapeViewController = [[LandscapeViewController alloc] initWithNibName:@"LandscapeViewController" bundle:[NSBundle mainBundle]];

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(orientationChanged:)
                                             name:UIDeviceOrientationDidChangeNotification
                                           object:nil];

// END SECTION landscape modal view controller

Then, when orientation changes this method is called:

- (void)orientationChanged:(NSNotification *)notification

{ UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;

if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
    // Load Landscape view
    landscapeViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self presentModalViewController:self.landscapeViewController animated:YES];

    isShowingLandscapeView = YES;
}

At the same time I was removing the status bar from the Landscape view controller's viewWillAppear method:

- (void)viewWillAppear:(BOOL)animated

{ // remove status bar from top of screen [[UIApplication sharedApplication] setStatusBarHidden:YES animated:animated];

self.myWebView.delegate = self; // setup the delegate as the web view is shown

}

and this is where the problem is introduced. The Portrait view controller captures the screen dimensions, before transitioning to landscape as a Modal View. Then, viewWillAppear, in the Landscape view controller, removes the status bar.

So, the solution is to move the

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:animated];

statement to the orientationChanged method in the Portrait view controller, BEFORE transitioning to the Landscape Modal View.

- (void)orientationChanged:(NSNotification *)notification

{ UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;

if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
    // remove status bar from top of screen
    // NOTE: this must be declared BEFORE presenting the Modal View!!!! If it's not, the landscape view will
    //       contain an ugly white bar in place of the missing status bar at the top of the view.
    [[UIApplication sharedApplication] setStatusBarHidden:YES];

    // Load Landscape view
    landscapeViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self presentModalViewController:self.landscapeViewController animated:YES];

    isShowingLandscapeView = YES;
}

Note, that as tc helpfully mentioned above, if you want the status bar to appear when orienting back to Portrait, then you need

[[UIApplication sharedApplication] setStatusBarHidden:NO animated:animated];

in the viewWillDisappear method in the Landscape view controller.

Sly