4

I'd like to convert my views to work for any orientation (especially since it recommended for iPad). I've been using IB to lay things out, and am not sure how to best proceed.

What I would ideally like is to rotate the view in IB, redo the layout, and save both orientations to the same XIB so that the view automatically knows what to do when the orientation changes. This doesn't seem possible.

I know I can rearrange the views in code when the orientation changes, but then there's not much point in using IB, since one of its main advantages for me has been to separate out all that ugly layout code from my logic.

What do others do for this? Do they just design their views so that the UIViewAutoResizing flags can handle rotations appropriately? Do they have multiple views for each orientation and somehow switch these out smoothly?

Eimantas
  • 48,927
  • 17
  • 132
  • 168
initlaunch
  • 467
  • 5
  • 15
  • possible duplicate of [iphone/ipad orientation handling](http://stackoverflow.com/questions/2815802/iphone-ipad-orientation-handling) – Eimantas Nov 23 '10 at 07:21
  • FWIW, I recommend simply subclassing UIView and implementing -layoutSubviews, and comparing bounds.size.width to bounds.size.height to distinguish between "portrait" and "landscape" (this also makes it more reusable if you decide to embed it in another view). I don't think I could stand managing 16 nibs (8 languages for iPhone and iPad), especially when you have to do layout tweaks for specific languages... – tc. Nov 23 '10 at 07:34
  • Yes this seems to be the best approach. It is too bad though, because I was liking have all my view stuff tucked away inside the nib and easily changeable in IB. oh well. – initlaunch Dec 18 '10 at 20:29

3 Answers3

5

You can make two separate views in the same XIB, and then implement the following code:

To @interface

IBOutlet UIView *landscapeView;
IBOutlet UIView *portraitView;

and to @implementation

- (void) viewDidLoad {
self.view.frame = [[UIScreen mainScreen] applicationFrame];
  landscapeView.autoresizesSubviews = NO;  <- IMPORTANT!
  portraitView.autoresizesSubviews = NO;   <- IMPORTANT!
}
- (BOOL)shouldAutorotateToInt .... {
  if ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight) self.view = landscapeView;
  else if ((interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) self.view = portraitView;
  return YES;
}
Fred
  • 4,195
  • 2
  • 29
  • 42
  • This would mean I would need to have 2 copies of every subview, one belonging to the portrait view, one belonging to the landscape view, right? And if my controller wanted to reference one of the subviews, it would need to thus have 2 references? – initlaunch Nov 23 '10 at 16:00
  • And you would need 2 outlets for all sub views? – TheLearner Apr 12 '12 at 07:48
  • whether in this approach can we maintain state of controls for example i have textfield in my app and user has typed some text in textfield and after that he is changing the orientation. then he need to type text again – ios Nov 23 '12 at 12:13
5

I found the information here (and elsewhere on StackOverFlow on this topic), and it got me to thinking about my previous answer. This just gets tedious if you have lots of top level objects.

Then, it occurred to me that you could create a "helper" object, so I created a project to demonstrate the technique. Basically, the helper has an outlet for each object of interest in the view. The ViewController itself has two Helper objects (one for p, one for l) and you instantiate those in the NIB. On orientation changes, you switch the view controller's helper pointer to the appropriate helper, then update self.view. Its really simple (this text is harder).

So, how would you start? Well it helps if you know a priori that you will need two separate view. Once you know that, take the Helper template in the referenced project below, adapt it, add it to your NIB, and then connect the help to the appropriate objects in the portrait view. The View Controller itself just has a reference to the two helpers, and a "curHelper" pointer. The Helper ivars are public, so the View Controller can reference items like "curHelper->label1.text = ... - the cost is a single pointer dereference (could just use properties too - your call).

Also, you can directly connect IBActions from the objects in either view. So, lets walk through this:

  • create the NIB
  • create a Helper object, and instantiate one in the nib called Portrait
  • add all the IBOutlets to the Helper object, handle then in dealloc, viewWillUnload
  • put IBActions as normal in the View Controller
  • wire up the NIB - Helper->Portrait view outlets, actions to the View Controller
  • get it all working in Portrait
  • add a new Helper object, call it Landscape
  • dup the current portrait view, rotate it, and save it
  • wire up the landscape Helper's outlets to the new landscape view (actions already set for you!)

Obviously you need to from now on make duplicate changes, but in any dual nib scheme you would have to do that. In the above technique, all outlet names stay the same.

The View Controller needs to send "viewDidUnload" to both helpers when it receives it, and to dealloc the Helpers (which then dealloc their outlets).

NOTE: I just put this together to see how it looked. If you want to see the code and a really small demo, you can download it from public dot me dot com slash dhoerl, in the iPhone folder with name Helpers.zip. I'll update this and this text if needed.

David H
  • 40,852
  • 12
  • 92
  • 138
  • This is a pretty good way to deal with the 2 references problem. I still don't feel good about the dual nib approach since you have to have two of every subview in memory (or recreate them on orientation changes), and you couldn't animate the subviews. So far, I am just designing my layout so that autoresizing can handle rotation - a little more limiting, but less work/clutter. – initlaunch Feb 06 '11 at 11:07
  • 1
    Nice approach. (I don't need 15 or more characters to say "Nice approach.") – Mustafa Jun 15 '11 at 07:30
  • 1
    Can you place this Helpers.zip file on your iDisk again please? It sounds massively useful! – TheLearner Apr 10 '12 at 16:09
0

Looking at the above code, using two views - I like it. To respond to the remark of "now I have to maintain two references - well, not there is a work around - use "references" (my term).

Name the views and outlets with prefixes of l and p, then define "pseudo" ivars with the base names you use now. When you first viewDidLoad (or elsewhere) set all "pseudo" ivars to the l or p varient. When you switch orientation, you just set the pseudo ones to the other orientation (along with other ops).

In viewDidUnload and dealloc, only reference the l and p item - the "real" IBOutlets - and simply nil the pseudo ivars out. Make it even more ugly - l_no_no_no_ or something in front of the real IBOutlets so you don't inadvertently use one.

If you make the pseudo ivars properties, use ASSIGN not RETAIN !!!

David H
  • 40,852
  • 12
  • 92
  • 138