6

Background: App has a shake to go home feature. Home view Only supports portrait. If you shake a bit harder than usual, the view that you are on starts to rotate (which is fine) , but then it detects a shake and does a popViewControlller to the home view. When it does this it loads the navigation controller just fine, but the view under (the home content) gets loaded behind the bar and is stretched up (it's basically loading underneath the navigation bar, so it gets stretched up)

The back button handles this just fine from landscape to portrait (since its not mid transitions)

How should I go about handling this orientation change (from the shake) so I can pop back into the root view controller, without the view loading under the navigation bar?

Edit:What's happening is the content thinks that it has the entire view to load, so it stretches itself to take the entire screen, not realizing theres a navigationbar above it. I can tell since the images loading are stretched out

added a bounty of 50.

Edit Here's How I'm detecting Shakes and Popping

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{

    if ( event.subtype == UIEventSubtypeMotionShake )
    {

            UINavigationController *navController = self.navigationController;

            [[self retain] autorelease];
            HomeViewController *home = [[HomeViewController alloc]init];
            [navController popViewControllerAnimated:YES];

            home.title =@"Home View Controller";
            [home release];     
        }

    if ( [super respondsToSelector:@selector(motionEnded:withEvent:)] )
        [super motionEnded:motion withEvent:event];
}

Here's my App Delegate:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    navController = [[UINavigationController alloc]init];
    [self.window addSubview:navController.view];

    HomeViewController *home = [[HomeViewController alloc]init];
    [[self home] setFrame:[[UIScreen mainScreen] applicationFrame]];

I'll include a mockup here.

Normal View:

Normal View

Stretched View After a Shake/Pop:

Streched

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation 
{}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return YES;
}
Sum
  • 4,369
  • 1
  • 22
  • 38
  • How are you creating your views? – jrtc27 Jul 14 '11 at 09:17
  • The Home View (the one underneath the bar, with the content) is being done by a ViewController and a XIB. The UINavigationBar is being created in the App Delegate. – Sum Jul 14 '11 at 20:29
  • Is it the right size for the gap it is going in? – jrtc27 Jul 14 '11 at 21:47
  • Not really. The XIB is configured for 320x480. Are you suggesting I somehow make the view actually 320 x (480-44-Statusbar height?). Right now it works, and it's designed so that it works by allowing some margin room at the bottom to be cut off when its loaded – Sum Jul 14 '11 at 21:48
  • I will have a dig around in IB myself for you over the weekend, as am not at my computer right now. Just clutching at straws at the moment... – jrtc27 Jul 14 '11 at 21:58
  • show the code where you detect shake and pop the view controller... – Swapnil Luktuke Jul 22 '11 at 07:17
  • sure thing! It's up in the original post. – Sum Jul 23 '11 at 01:30
  • Could we possibly get a picture of what is going on so that we can better understand your problem? – Flipper Jul 26 '11 at 04:26
  • Yup. I updated the post with a sample/mockup – Sum Jul 27 '11 at 02:23

4 Answers4

2

Have you tried calling [[UIApplication sharedApplication] setStatusBarOrientation: UIInterfaceOrientationPortrait animated:YES]; in your home view controller? You could also try to place this in where you detect a shake.

Sum
  • 4,369
  • 1
  • 22
  • 38
jrtc27
  • 8,496
  • 3
  • 36
  • 68
  • No, I'll try that soon. Thanks – Sum Jul 14 '11 at 04:16
  • Just tried that. I don't think its the status bar. What's happening is the content thinks that it has the entire view to load, so it stretches itself to take the entire screen, not realizing theres a navigationbar above it. – Sum Jul 14 '11 at 05:04
2

in your home view controller's xib, go to the inspector for the view and set top bar as navigation bar.. and in view did load set self.navigationBarHidden = NO;...

NOTE: there are many thing wrong with the code you've posted.. but none of them causes the orientation problem... in fact this seems to be the only code you need in that method:

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    if (event.subtype == UIEventSubtypeMotionShake)
    {
        [navController popViewControllerAnimated:YES];
    }
}

so you might want to change this code as well..

Swapnil Luktuke
  • 10,385
  • 2
  • 35
  • 58
  • That didn't work. I still get the stretched homeview when I turn back in specific cases – Sum Jul 25 '11 at 22:43
  • Thanks for trying though. I still need most of that code except the title part. – Sum Jul 25 '11 at 22:50
  • Maybe I'm just structuring the creation of the navigation bar wrong. I create it programmatically but I pop back to the homeview controller. Since I am popping to the home view controller, can I create some other transition (fade?) – Sum Jul 25 '11 at 22:59
  • 1
    no you can have only the default animation (or not have any animation at all).... and about the code, you are creating and destroying 'HomeViewController's object for nothing... you realise that the object you create with alloc init is not the 'HomeViewController' you are popping back to right? – Swapnil Luktuke Jul 26 '11 at 06:14
  • Right +1 . That finally got into me. I'm alloc initing for no reason. It's not like a modal/normal view transition. Though I still need UINavigationController *navController = self.navigationController; What about [[Self Retain]autorelease]. That seems frivolous. – Sum Jul 26 '11 at 06:59
  • 1
    that might only make your current view controller stay in memory a little longer.. there can be times when you NEED to autorelease objects but i dont think you need it... your controller wont leak even if you remove that line, if thats why you did it.. – Swapnil Luktuke Jul 26 '11 at 07:39
  • Thanks for the optimization! It's helpful as I finally wrap my head around memory management. – Sum Jul 26 '11 at 08:08
2

I have come across this issue with underlapping the navigation bar. I am not sure what causes it but you can work around it by calling,

[[self loadingView] setFrame:[[UIScreen mainScreen] applicationFrame]];

after the problem view is added to window in the application delegate.

railwayparade
  • 5,154
  • 1
  • 39
  • 49
  • Can't seem to integrate it. What exactly is LoadingView? Would I say homeviewcontroller instead? – Sum Jul 26 '11 at 06:48
  • I have a feeling that this will work, but I can't seem the integrate it without getting an error. – Sum Jul 26 '11 at 07:00
  • That line should be executed when your homeviewcontroller is added to the window assuming its the root view controller. So it should go in the AppDelegate.m after [window addsubview] – railwayparade Jul 26 '11 at 07:27
  • Added code to the OP. It still crashes. Think I'm doing something a little weird – Sum Jul 26 '11 at 08:02
  • 1
    should be [[[self home] view] setFrame:[[UIScreen mainScreen] applicationFrame]]; – railwayparade Jul 26 '11 at 08:19
  • Nope. 2011-07-26 [50378:10d03] -[AppDelegate home]: unrecognized selector sent to instance 0x70bcd90 2011-07-26 [50378:10d03] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AppDelegate home]: unrecognized selector sent to instance 0x70bcd90' – Sum Jul 26 '11 at 08:53
  • ah its a local variable sorry, how about [[home view] setFrame:[[UIScreen mainScreen] applicationFrame]]; – railwayparade Jul 26 '11 at 08:57
  • Thanks. The code works, but I still get the same issue. I might just use of my TSI's to get to the bottom of this. I tried calling this in the second view as well. Still no luck. Thanks though. I'm not sure whether to accept your answer or not. – Sum Jul 26 '11 at 22:53
2

I'm a bit puzzled by your code so I'd really suggest starting from the beginning. As Lukya mentioned, there's no reason to recreate the HomeViewController. I'm also baffled by the "[[self retain] autorelease];" bit. That shouldn't be necessary unless you're doing something incorrectly elsewhere.

So I would start with this... In application:didFinishLaunchingWithOptions: do something like this:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    {
        HomeViewController *home = [[[HomeViewController alloc] init] autorelease];
        UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:home] autorelease];
        [self.window addSubview:navController.view];
    }

The window will retain a your nav controller and the nav controller will retain your HomeViewController.

Then in motionEnded:withEvent: do something like:

    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (event.subtype == UIEventSubtypeMotionShake)
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }

That should be it.

If that does not work then can you give any other info? For example, does HomeViewController implement (and return YES) in shouldAutorotateToInterfaceOrientation:? If so, can you return no so it doesn't rotate since your first line says "Home view Only supports portrait"?

Edit: An example of willRotateToInterfaceOrientation:duration: and didRotateFromInterfaceOrientation: as well.

In the header for whatever controller you're detecting shakes in add a boolean:


    BOOL isRotating;

In your implementation file add the two UIViewController methods we want to override -- something like:


    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
        [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
        isRotating = YES;
    }

    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
        [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
        isRotating = NO;
    }

Now, do something like this for your event handler:

    - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
    {
        if (event.subtype == UIEventSubtypeMotionShake && !isRotating)
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
Amro
  • 4,683
  • 1
  • 16
  • 9
  • Sure. I'll try that. HomeViewController Returns NO for the should AutoRotate. I'll try to start over with that now. – Sum Jul 26 '11 at 23:37
  • Tried that as well. Again no luck. – Sum Jul 26 '11 at 23:40
  • I'm can provide any other information necessary to get to the bottom of this, but what else is there. this really should be within the delegate and the views – Sum Jul 26 '11 at 23:51
  • Is your navigation bar set to transparent, by chance? If so, make it opaque. – Amro Jul 27 '11 at 00:16
  • No, I don't think so. It's set up to have an image background. – Sum Jul 27 '11 at 01:02
  • 1
    Ok, I have another thought. Implement `- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration` and `- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation`. In the former, set a "rotating" boolean in your HomeViewController to YES. Set it to NO in didRotate. Check for it in `motionEnded:withEvent:` and don't pop if it's set to YES. Probably a good idea to call the superclass implementation of both in each as well. – Amro Jul 27 '11 at 02:05
  • I mean to implement this in whatever controller you're listening to the shake event in. If I'm not mistaken that's HomeViewController. – Amro Jul 27 '11 at 02:09
  • Nope, I detect shakes in the second view and then pop them back over. I've added code in the original question (in a few seconds) – Sum Jul 27 '11 at 02:13
  • Implement it in the view you're detecting the shakes in. The idea is to not pop the view controller off the stack if the view is rotating. – Amro Jul 27 '11 at 02:15
  • hmm. I like that. Thats a great idea. So we'll want to delay the pop if they are in the middle of a transition. I posted the code, but being as new as I am, I'm not totally sure how to do this. I'd appreciate an example greatly – Sum Jul 27 '11 at 02:18
  • 1
    Not delay -- just ignore. If they're shaking then it doesn't matter because another shake event will probably come through. I added to my existing answer above. – Amro Jul 27 '11 at 02:26
  • Alright, I see. My shakes are slow and make my elbow go from 90 to 180, but we'll discourage that here. – Sum Jul 27 '11 at 02:45
  • It works! I'm going to try this on my device to make sure its still usable, but this looks like the solution I was trying to get at. Thanks! – Sum Jul 27 '11 at 02:51