views:

2400

answers:

3

I have a UIViewController that I'm using to control a "pop-up" view for viewing images throughout my application. It supports autorotation, as it automatically sizes the image to fit properly regardless of orientation. This works perfectly, but only the first time I initialize and display the view controller. When it closes, I am removing the UIView from my view hierarchy and releasing the view controller - but the next time I instantiate and add it to my view hierarchy, it stops receiving the -shouldAutorotateToInterfaceOrientation messages when the phone is rotated.

This is how I instantiate and display it:

popupVC = [[PopupVC alloc] init];
[popupVC viewWillAppear:NO];
[[[UIApplication sharedApplication] keyWindow] addSubview:popupVC.view];
[popupVC viewDidAppear:NO];

this is how I remove/release it when it's finished:

[popupVC viewWillDisappear:NO];
[popupVC.view removeFromSuperview];
[popupVC viewDidDisappear:NO];
[popupVC release];
popupVC = nil;

I've tried looping through [[UIApplication sharedApplication] keyWindow] subviews to see if somehow my popup view isn't on top, but it always is. And it has a different address each time so I do know that it's a different instance of the view controller class.

As requested, here is the complete loadView method from PopupVC:

- (void)loadView {

    UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    myView.backgroundColor = self.overlayColor;
    myView.autoresizesSubviews = NO;
    myView.hidden = YES;
    myView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.view = myView;
    [myView release];

    _isVisible = NO;

    UIView *myMaskView = [[UIView alloc] initWithFrame:self.view.bounds];
    myMaskView.backgroundColor = [UIColor clearColor];
    myMaskView.clipsToBounds = YES;
    myMaskView.hidden = YES;
    myMaskView.autoresizesSubviews = NO;
    myMaskView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [self.view addSubview:myMaskView];
    self.imageMaskView = myMaskView;
    [myMaskView release];

    UIImageView *myImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
    myImageView.center = self.view.center;
    myImageView.hidden = NO;
    myImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
    [self.imageMaskView addSubview:myImageView];
    self.imageView = myImageView;
    [myImageView release];

    UIButton *myImageButton = [UIButton buttonWithType:UIButtonTypeCustom];
    myImageButton.frame = self.view.frame;
    myImageButton.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleHeight;
    [myImageButton addTarget:self action:@selector(clickImage:) forControlEvents:UIControlEventTouchUpInside];
    [self.imageMaskView addSubview:myImageButton];
    self.imageButton = myImageButton;

    UIActivityIndicatorView *myActivityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    myActivityView.hidden = YES;
    [self.view addSubview:myActivityView];
    myActivityView.center = self.view.center;
    self.activityView = myActivityView;
    [myActivityView release];
}
+3  A: 

The shouldAutorotateToInterfaceOrientation handler isn't the place for custom code to respond to rotation events. It's purpose is just to tell the OS that your view controller can rotate. If you want to have custom code to handle the rotation events you should overide - didRotateFromInterfaceOrientation or one of the other similar callback methods depending on your needs:

  • willRotateToInterfaceOrientation:duration:
  • willAnimateRotationToInterfaceOrientation:duration:
  • didRotateFromInterfaceOrientation:
  • willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
  • didAnimateFirstHalfOfRotationToInterfaceOrientation:
  • willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:

See Autorotating Views in the developer docs.

Jason Jenkins
Good point - although I am not responding to the event here, I am debugging this the wrong way. Thanks for pointing that out.
pix0r
Although now that I look I do have some debugging code in `-didRotateFromInterfaceOrientation:` and it's not being fired either.
pix0r
How about trying to have your view controller override `canBecomeFirstResponder` to return YES, do `[self becomeFirstResponder]` in `viewDidAppear:animated` and a corresponding `[self resignFirstResponder]` in `viewWillDisappear:animated`. Does that make any difference?
Jason Jenkins
Sadly no - my view controller never receives `-canBecomeFirstResponder:`. (But it sounded promising!)
pix0r
How is your view controllers view being created? Can you share the code in your PopupVC's loadView method? Also, does your view own the entire screen or is it some sort of modal popup window overlaying some other view?
Jason Jenkins
The view covers the entire screen. I've added the `loadView` method from `PopupVC` (see original question).
pix0r
+2  A: 

I think that might be a bug in the new OS 3.0. A workaround to this would be to use NSNotificationCenter after turning on beginGeneratingDeviceOrientationNotifications.

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

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

-(void) receivedRotate: (NSNotification *) notification {
    DebugLog(@"ORIENTATION CHANGE");

    UIDeviceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];

     if(interfaceOrientation == UIDeviceOrientationPortrait) {
      self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(0));
      self.view.bounds = CGRectMake(0, 0, 320, 480);
     }
     else if(interfaceOrientation == UIDeviceOrientationLandscapeLeft) {
      self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
      self.view.bounds = CGRectMake(0, 0, 480, 320);
     }
     else if(interfaceOrientation == UIDeviceOrientationLandscapeRight) {
      self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(-90));
      self.view.bounds = CGRectMake(0, 0, 480, 320);
     }
}
Jon
A: 

My problem is that I get the rotation event for my rootViewController, but as soon as I launch my second viewController, it won't get any signals except motion (shaking) and touch. Is that the same issue as the original post -- it is certainly similiar??

I followed-up with above recommendation and I don't have the selector coded correctly so it throws an exception as soon as I try to rotate. Meanwhile, I found this other discussion on the web which confirms that the problem was introduced in 3.0. link text

mobibob