I have been pulling my hair out one by one for 3 days now and still can't get the autoresizing on orientation changes to behave correctly :( I will try to describe the whole context, hope I won't forget anything important, if so just ask !
I plan to support all orientations on my App (only for some of the views, not all). So first thing I did was to override shouldAutorotateToInterfaceOrientation: for the right viewControllers. I then opened IB to set the autoresizingMask for all the view elements.
I tested it with a simple view containing only a nav bar, a search bar and a UILabel in the middle : it works perfectly. Now I'm trying to make it work for a much more complex view : a 'fake' TabBarController embedded within a Navigation controller (using a trick from this blog). The tricky part is, the 3 'tab bar views' don't display in the whole space. Typically the 'parent' view (the one acting as a tab bar controller) contains a 'header' which never changes, and a UIView below which displays the 3 'child' views as subviews. The 'parent' view can scroll up and down when the height of one of the 3 'child' view is too big (I calculate and set the scrollView contentSize dynamically). The 4 views (parent & 3 childs) have their own UIViewController and .xib files. Eventually, its supposed to look like the 'Profile' page in the facebook app.
All of the tab bar views should be able to adapt to orientation changes. So I overrided shouldAutorotateToInterfaceOrientation: and set the auroresizingMask for the 4 of them.
At this stage, at first glance, it works, all views seem to resize correctly when I turn the device. But after a few tries I spotted 2 bugs :
The scroll view doesn't resize its height at all ! I didn't spot it right away cause I was resizing it manually each time I changed view. But when the current view is small enough to fit in the window without scrolling (so scrolling isn't possible), and I change orientation to Landscape, the scrollView doesn't resize its height and I become able to scroll to the extent of the scrollView in Portrait mode. I set all views with different background colors so I can tell that its width seems to change.
The second one is a bit more convoluted, say I am in the first view in portrait mode, I switch to the second view, go to Landscape mode, and the switch back to the first view (still in Landscape mode) : the part of the view corresponding to the child view isn't resized... I suppose some kind of event isn't sent to it but can't spot where.
That's it, any suggestion will be greatly appreciated ! If you need any more precisions just ask.
As asked, here is some code from the concerned classes. The TabBarController is quite big so I selected revelent methods :
ProfileRootViewController.h
@interface ProfileRootViewController : UIViewController <UITabBarDelegate, UIScrollViewDelegate> {
// Profile view own content
IBOutlet UIScrollView *scrollView;
IBOutlet UIView *tabView;
IBOutlet UIImageView *portraitView;
IBOutlet PopularityStarsView *starsView;
IBOutlet UILabel *screenNameLabel;
IBOutlet UITextView *wantedTextView;
// An array containing all the views of the tab bar
NSArray *viewControllers;
// The 3 items we need for the tab bar
IBOutlet UITabBar *tabBar;
IBOutlet UITabBarItem *wallItem;
IBOutlet UITabBarItem *infoItem;
IBOutlet UITabBarItem *albumsItem;
// The currently selected view
UIViewController *selectedViewController;
// The CBIPhoneUser which profile is being displayed
CBIPhoneUser *currentUserProfile;
// LATER : The Wall of the user being displayed
BOOL wallViewLoaded;
// LATER : The Albums of the user being displayed
BOOL albumsViewLoaded;
}
@property(nonatomic, retain) UIScrollView *scrollView;
@property(nonatomic, retain) UIView *tabView;
@property(nonatomic, retain) UIImageView *portraitView;
@property(nonatomic, retain) PopularityStarsView *starsView;
@property(nonatomic, retain) UILabel *screenNameLabel;
@property(nonatomic, retain) UITextView *wantedTextView;
@property(nonatomic, retain) NSArray *viewControllers;
@property(nonatomic, retain) UITabBar *tabBar;
@property(nonatomic, retain) UITabBarItem *wallItem;
@property(nonatomic, retain) UITabBarItem *infoItem;
@property(nonatomic, retain) UITabBarItem *albumsItem;
@property(nonatomic, retain) UIViewController *selectedViewController;
@property(nonatomic, retain) CBIPhoneUser *currentUserProfile;
@property(nonatomic) BOOL wallViewLoaded, albumsViewLoaded;
-(void)displayWallView;
-(void)displayInfoView;
-(void)displayAlbumsView;
-(void)displayLoadingView;
@end
ProfileRootViewController.m
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
// Set the title for the 3 tab bar buttons
wallItem.title = NSLocalizedString(@"Profile_tabBar_wall", @"Title for the 'Wall' tab bar button");
infoItem.title = NSLocalizedString(@"Profile_tabBar_info", @"Title for the 'Info' tab bar button in the Profile view");
albumsItem.title = NSLocalizedString(@"Profile_tabBar_albums", @"Title for the 'Albums' tab bar button (leads to a list of albums)");
// Configure the image view to have rounded corners
portraitView.layer.cornerRadius = 5.0;
portraitView.layer.masksToBounds = YES;
// If the Wall view is already loaded, display it
if (wallViewLoaded) {
[self displayWallView];
}
// Else, display the "Loading view"
else {
[self displayLoadingView];
}
// The selected tab bar item should still be the Wall item
tabBar.selectedItem = wallItem;
// Set the size of the content of the Scroll view for it to know when to stop scrolling
CGSize scrollViewSize = CGSizeMake(self.view.frame.size.width , self.tabView.frame.origin.y + self.tabView.frame.size.height);
scrollView.contentSize = scrollViewSize;
[super viewDidLoad];
}
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
BOOL needLoadingView = YES;
if (item == wallItem) {
if (wallViewLoaded) {
[self displayWallView];
needLoadingView = NO;
}
// TODO ? : If the webService has not begun loading, call it
}
else if (item == infoItem) {
if (currentUserProfile != nil) {
[self displayInfoView];
needLoadingView = NO;
}
}
else if (item == albumsItem) {
AlbumsSelectionViewController *tmp = (AlbumsSelectionViewController*)[viewControllers objectAtIndex:2];
if (tmp.albumsArray != nil) {
[self displayAlbumsView];
needLoadingView = NO;
}
// TODO ? : If the webService has not begun loading, call it
}
// If the view wasn't ready yet, display the loading view
if (needLoadingView) {
[self displayLoadingView];
}
}
-(void)displayWallView {
UIViewController *wallViewController = [viewControllers objectAtIndex:0];
if (self.selectedViewController != nil) {
[self.selectedViewController.view removeFromSuperview];
}
// Call viewWillAppear method manualy before adding a subview
[wallViewController viewWillAppear:YES];
[self.tabView addSubview:wallViewController.view];
self.selectedViewController = wallViewController;
}
-(void)displayInfoView {
InfoViewController *infoViewController = (InfoViewController*)[viewControllers objectAtIndex:1];
if (self.selectedViewController != nil) {
[self.selectedViewController.view removeFromSuperview];
}
infoViewController.parentController = self;
[infoViewController resizeParentScrollViewWithOrigin:self.tabView.frame.origin andSize:infoViewController.infosTable.frame.size];
// Call viewWillAppear method manualy before adding a subview
[infoViewController viewWillAppear:YES];
[self.tabView addSubview:infoViewController.view];
self.selectedViewController = infoViewController;
}
-(void)displayAlbumsView {
AlbumsSelectionViewController *albumsViewController = [viewControllers objectAtIndex:2];
if (self.selectedViewController != nil) {
[self.selectedViewController.view removeFromSuperview];
}
albumsViewController.parentController = self;
[albumsViewController resizeParentScrollViewWithOrigin:self.tabView.frame.origin andSize:albumsViewController.albumsSelectionTable.frame.size];
// Call viewWillAppear method manualy before adding a subview
[albumsViewController viewWillAppear:YES];
[self.tabView addSubview:albumsViewController.view];
self.selectedViewController = albumsViewController;
}
-(void)displayLoadingView {
PageLoadingViewController *loadingViewController = [viewControllers objectAtIndex:3];
if (self.selectedViewController != nil) {
[self.selectedViewController.view removeFromSuperview];
}
loadingViewController.parentController = self;
[loadingViewController resizeParentScrollViewWithOrigin:self.tabView.frame.origin andSize:self.tabView.frame.size];
// Call viewWillAppear method manualy before adding a subview
[loadingViewController viewWillAppear:YES];
[self.tabView addSubview:loadingViewController.view];
// Set the size of the content of the Scroll view for it to know when to stop scrolling
self.selectedViewController = loadingViewController;
}
The last 3 classes contains very little code relating to resizing, they just all inherit from the following class, and they call resizeParentScrollViewWithOrigin:andSize: just once at the end of viewDidLoad.
NestedTabViewController.h
@interface NestedTabViewController : UIViewController {
ProfileRootViewController *parentController;
}
@property(nonatomic, retain) ProfileRootViewController *parentController;
- (void) resizeParentScrollViewWithOrigin:(CGPoint)origin andSize:(CGSize)theSize;
@end
NestedTabViewController.m
- (void) resizeParentScrollViewWithOrigin:(CGPoint)origin andSize:(CGSize)theSize {
NSLog(@"resizing scroll view to : X=%f, Y=%f, W=%f, H=%f", origin.x, origin.y, theSize.width, theSize.height);
parentController.scrollView.contentSize = CGSizeMake(theSize.width, origin.y + theSize.height);
}
Cheers
PB