I'm beginning to try and use IB where previously I used code. I have created nibs for iPad/iPhone Portrait/Landscape so I have four possible views. All have files owner set to UIViewController, all have an object added from the palette set to my NSObject subclass "TargetObject". No problem setting IBActions/IBOutlets, just ctrl-drag as usual to create the code in one nib, then go and right-click the object and drag the actions/outlets to the right controls in the other nibs so that the connection exists in all nibs. The idea is that the app delegate kicks off the appropriate view controller for the current device and then that view controller loads the right view depending on orientation.
(Maybe with autoresizing mastery this approach is not necessary - I want to know if it is possible to do it this way because getting the layouts to rearrange themselves just right just with those IB controls is very, very frustrating.)
The idea behind these one-to-many views and target objects is that I can use them anywhere in the app. For example, where I have something displayed which is an object in core data and I would like to allow detail view/edit in multiple places in the app. It seems like a very MVC way to do things.
The problem comes in loadView of the view controller when I do:
NSArray *portraitViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Portrait" owner:self options:nil];
portraitWeekView = [portraitViewArray objectAtIndex:0];
NSArray *landscapeViewArray = [[NSBundle mainBundle] loadNibNamed:@"Weekview_iPad_Landscape" owner:self options:nil];
landscapeWeekView = [landscapeViewArray objectAtIndex:0];
// this object is intended to be a common target for portrait and landscape arrays.
weekViewTarget = [portraitViewArray objectAtIndex:1];
UIInterfaceOrientation interfaceOrientation = self.interfaceOrientation;
if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
self.view = portraitWeekView;
else
self.view = landscapeWeekView;
As you guessed, everything is fine in portrait orientation but in landscape any interaction with the target object crashes. The weekViewTarget object refers to an object created with the portrait view, so the landscape view is looking for a different instance that hasn't been held with a reference. In the landscape view _viewDelegate is nil - it is private so I can't fix it up in code.
I can hold onto the landscape target object with a reference too, but then there will be problems whenever the orientation changes - in case of any stateful controls like UITableView the datasource/delegate will change resulting in strange user experience on rotation. Unless I ensure the target object implements only simple actions and uses a separate, shared object to implement datasource/delegate objects for any table views or text views required.
One thing I have thought of is to make the target a singleton. Seems like a hack and here will be times that can't work.
What's the right way to create multiple views implemented as nibs using a common target object to do stuff to the model?
progress - things begin to work if I give each target a pointer to its own type:
WeekViewTarget *forwardTarget;
and let the real handler's forwardTarget = nil
while the unused handler's forwardTarget is set as follows:
weekViewTarget = [portraitViewArray objectAtIndex:1];
forwardedWeekViewTarget = [landscapeViewArray objectAtIndex:1];
forwardedWeekViewTarget.forwardTarget = weekViewTarget;
and then do the following in each IBAction:
- (IBAction)timeBlockTapped:(id)sender {
if(forwardTarget != nil) {
[forwardTarget performSelector:@selector(timeBlockTapped:) withObject:sender];
} else {
NSLog(@"Got a tap event with sender %@", [sender description]);
}
}
Still doesn't feel right though.