Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

frame size does not change in landscape mode

I have a UINavigationController that can rotate to landscape mode and that it initializes a new UIViewController and pushes it on the stack. On the UIViewController's viewDidLoad I just print the self.view.frame.size.

If the UINavigationController is in Portrait mode, the View Controller will print {320, 460}. However, when the Navigation Controller is in Landscape, the frame size is exactly the same. (To clarify, in the second case the UIViewController is initialized while the phone is already in landscape mode.)

Shouldn't the frame size of the view be rotated? And if not how I can I find the correct frame size without hardcoding the numbers?

like image 266
user130444 Avatar asked Jun 29 '09 13:06

user130444


3 Answers

You can't rely on the frame in landscape mode; you have to use a combination of bounds and center; frame is generated using a combination of those, and when there's a non-identity transform applied (as there is in landscape), it gets a little weird.

like image 103
Ben Gottlieb Avatar answered Sep 23 '22 20:09

Ben Gottlieb


first you need to set your view to resize automatically with a proper autoresizingMask. with this your view will adapt to the size of the controller itself.

you can check this yourself with an NSLog. But don't put it in loadView, this is too early. Put this in viewWillAppear.

If you set the autoresizingMask of your view with Interface Builder you should turn off the Simulated Interface Elements in the Attributes inspector. If any of these is on you can't change the autoresizingMask in the Size inspector.

like image 24
denis2342 Avatar answered Sep 21 '22 20:09

denis2342


This is a pain and it's still true in iOS 4.2 for iPads. The way I solved this is to subclass the UIView associated with the UIViewController. I did this in interface builder but I suppose one could also do this in code somehow. In interface builder select the UIView in the UIViewController then hit the (i) icon in the upper right of the Inspector window. Under class identity hit the popup and chose the UIView subclass below.

The approach is that this UIView subclass overrides the layoutSubviews method, finds the next UIViewController, determines if it implements a reactToLayout method (which is a method that you have to implement in the UIViewController subclass for the view controller of this view). If the reactToLayout method exists in the first UIViewController found, it is invoked.

The reactToLayout method in the view controller then does whatever one needs to do which it will be able to do successfully since the view's frame is set up properly by this time (unlike in ViewDidLoad, viewWillAppear, or even viewDidAppear). I have a method that I call anytime the orientation or frame changes. It's a pain but I store the last frame laid out and last orientation laid out in internal variables of the view controller. The internal layout for new orientation or frame change method compares these to the view's current frame and requested or current orientation so that it doesn't unnecessarily layout stuff over and over.

Here's the code:

UILayoutSubviewsView.h

#import <UIKit/UIKit.h>

@interface UILayoutSubviewsView : UIView {
}

@end

UILayoutSubviewsView.m

#import "UILayoutSubviewsView.h"

// Create this to avoid a warning that this method does not exist for UIViewControllers
// this is OK since we check to see that it does exist before invoking it
@interface UIViewController(UndocumentedMethodForUIViewController)
-(void) reactToLayout;
@end

@implementation UILayoutSubviewsView

// Pass this up to our view controller if it supports the reactToLayout method
// (this is the whole reason for the class)
-(void) layoutSubviews {
  [super layoutSubviews];

  // Look for the first next responder that is a UIViewController
  UIViewController *ourViewController = nil;
  id myNextResponder = [self nextResponder];
  while (myNextResponder != nil && ourViewController == nil) {
    if ([myNextResponder isKindOfClass:[UIViewController class]]) {
      ourViewController = myNextResponder;
    }
    else {
      myNextResponder = [myNextResponder nextResponder];
    }    
  }

  // If we got a view controller, then see if it supports the reactToLayout method
  if (ourViewController != nil) {
    if ([ourViewController respondsToSelector:@selector(reactToLayout)]) {

      // Invoke the view controller's reactToLayout method
      [ourViewController reactToLayout];

    }
  }

}

@end

YourViewController.h

#import <UIKit/UIKit.h>

@interface YourViewController : UIViewController {
  CGRect lastLayedOutFrame;
  UIInterfaceOrientation lastLayedOutOrientation;
}

#pragma mark -
#pragma mark Instance Methods
-(id) init;
-(void) reactToLayout;

@end

YourViewController.m

#import "YourViewController.m"

#pragma mark Private Interface Category
@interface YourViewController()
-(void) setViewForCurrentFrameAndRequestedOrientation:(UIInterfaceOrientation) interfaceOrientation;
@end

@implementation YourPadViewController

-(id) init {

  // First our super then set ourselves up
  if (self = [super initWithNibName:@"YourViewController" bundle:nil]) {

    // Initialize some basic stuff
    lastLayedOutFrame = CGRectZero;
    lastLayedOutOrientation = UIDeviceOrientationUnknown;

  }

  return self;
}

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

  // Make sure we're showing the right stuff in the right place
  [self setViewForCurrentFrameAndRequestedOrientation:UIDeviceOrientationUnknown];

}

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

  // Make sure we're showing the right stuff in the right place
  [self setViewForCurrentFrameAndRequestedOrientation:UIDeviceOrientationUnknown];

}

-(void) reactToLayout {

  // Make sure we're showing the right stuff in the right place
  [self setViewForCurrentFrameAndRequestedOrientation:UIDeviceOrientationUnknown];

}

#pragma mark -
#pragma mark Rotation Support
-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation {
  return YES;
}

// This is called right before the actual rotation
-(void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation duration:(NSTimeInterval) duration {
  [super willAnimateRotationToInterfaceOrientation:interfaceOrientation duration:duration];

  // Make sure we're showing the right stuff in the right place
  [self setViewForCurrentFrameAndRequestedOrientation:interfaceOrientation];

}

// Make the necessary adjustments for the different view orientations
-(void) setViewForCurrentFrameAndRequestedOrientation:(UIInterfaceOrientation) interfaceOrientation {

  // Set up the requested orientation (need this to handle the Unknown case)
  UIInterfaceOrientation requestedOrientation;
  if (interfaceOrientation != UIDeviceOrientationUnknown) {
    requestedOrientation = interfaceOrientation;
  }
  else {
    requestedOrientation = [[UIDevice currentDevice] orientation];
  }

  // See if we have anything to do
  if (!(CGRectEqualToRect(self.view.frame, lastLayedOutFrame) && lastLayedOutOrientation == requestedOrientation)) {

    // Do whatever needs to be done

    // Record our last layed out frame and orientation
    lastLayedOutFrame = self.view.frame;
    lastLayedOutOrientation = requestedOrientation;

  }

}
like image 34
SpareTime Avatar answered Sep 20 '22 20:09

SpareTime