Problems with multiple xibs for rotation under iOS6 - ios

I need to use different xib files for portrait and landscape. I am not using Auto Layout but I am using iOS6. (See my previous question if you care why.)
I'm following Adam's answer to this question modified with amergin's initWithNib name trick, modified with my own iPhone/iPad needs. Here's my code:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[[NSBundle mainBundle] loadNibNamed:[self xibNameForDeviceAndRotation:toInterfaceOrientation]
owner: self
options: nil];
[self viewDidLoad];
}
- (NSString *) xibNameForDeviceAndRotation:(UIInterfaceOrientation)toInterfaceOrientation
{
NSString *xibName ;
NSString *deviceName ;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
deviceName = #"iPad";
} else {
deviceName = #"iPhone";
}
if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) )
{
xibName = [NSString stringWithFormat:#"%#-Landscape", NSStringFromClass([self class])];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
xibName = [NSString stringWithFormat:#"%#_%#-Landscape", NSStringFromClass([self class]), deviceName];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
NSAssert(FALSE, #"Missing xib");
return nil;
}
}
} else {
xibName = [NSString stringWithFormat:#"%#", NSStringFromClass([self class])];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
xibName = [NSString stringWithFormat:#"%#_%#", NSStringFromClass([self class]), deviceName];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
NSAssert(FALSE, #"Missing xib");
return nil;
}
}
}
}
and of course I'm doing:
- (BOOL) shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
in my view controller and:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown);
}
in my delegate.
I have two problems which may be related. First, the easy one. I do not rotate upside down. I have all all the proper bits turned on in xcode for both iPad and iPhone. This may be a separate issue or it may be the core of my problem.
The real problem is that when I rotate to landscape mode my xib is replace but the view is off by 90 degrees.
Here's what my 2 xib's look like. (I've colored them garishly so you can see that they are different.)
and
and you can see when I run it (initially in Landscape mode) that the landscape xib is correct.
when I rotate to portrait it is also correct
but when I rotate back to landscape the xib is replaced but the view is off by 90 degrees.
What's wrong here?

I've been following probably the same path as Paul Cezanne did last year. Not sure if he tried this or not, but I solved the original issue (stated in this question) by just making my root controller a navigation controller instead of my view controller class. Since I'm using an "empty project" template and XIB files, this meant changing the normal:
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.viewController;
inside AppDelegate.m, to this instead:
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:nil];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navigationController.navigationBar.hidden = YES;
self.window.rootViewController = navigationController;
That is, I just created a generic UINavigationController and set that as the root view controller.
I'm not sure if this will cause other problems, and there is probably a way to figure out (maybe you would need the source code though) what UINavigationController does that UIViewController doesn't. Could be as simple as one extra setNeedsLayout type of call in the right place. If I figure it out, I'll edit this answer for future readers.
Credit goes to Sakti's comments on Easiest way to support multiple orientations? How do I load a custom NIB when the application is in Landscape? which I shouldn't have ignored the first time I read them:
i added the view controller to navigation controller and presented it
which made it work as intended
Edit: Added extra line to example code to hide navigation bar, since most people following this issue will not want that.

This is how I do it and it works on iOS 5+:
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(checkBagRotation)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
[self checkBagRotation];
}
- (void)checkBagRotation {
orientation = [UIApplication sharedApplication].statusBarOrientation;
if(orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
[[NSBundle mainBundle] loadNibNamed:#"Controller-landscape"
owner:self
options:nil];
} else {
[[NSBundle mainBundle] loadNibNamed:#"Controller-portrait"
owner:self
options:nil];
}

I'm answering my own question here pasting from the full article on my iOS blog at http://www.notthepainter.com/topologically-challenged-ui/
I had a friend help me out, he used 2 views in one xib file with IBOutlets for portrait and landscape view and he toggled between them the device rotated. Perfect, right? Well, no, when you have 2 views in a XIB you can’t hook up your IBOutlets to both places. I had it working visually but my controls only worked in one orientation.
I eventually came up with the idea of using a orientation master view controller that loaded container view controllers when the device rotated. That worked fine. Lets look at the code:
-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
if (_timerViewController) {
[_timerViewController.view removeFromSuperview];
[_timerViewController willMoveToParentViewController:nil];
[_timerViewController removeFromParentViewController];
self.timerViewController = nil;
}
self.timerViewController = [[XTMViewController alloc] initWithNibName:
[self xibNameForDeviceAndRotation:interfaceOrientation withClass:[XTMViewController class]]
bundle:nil];
// use bounds not frame since frame doesn't take the status bar into account
_timerViewController.view.frame = _timerViewController.view.bounds = self.view.bounds;
[self addChildViewController:_timerViewController];
[_timerViewController didMoveToParentViewController:self];
[self.view addSubview: _timerViewController.view];
}
The addChildViewController and didMoveToParentViewController should be familiar if you read my previous blog entry on Container View Controllers. There are two things to notice above those calls though. I’ll deal with the second one first, I set the child view controller’s frame and bounds from the parents bounds, not frame. This is to take account of the status bar.
And notice the call to xibNameForDeviceAndRotation to load the view controller from its xib file. Lets look at that code:
- (NSString *) xibNameForDeviceAndRotation:(UIInterfaceOrientation)toInterfaceOrientation withClass:(Class) class;
{
NSString *xibName ;
NSString *deviceName ;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
deviceName = #"iPad";
} else {
deviceName = #"iPhone";
}
if( UIInterfaceOrientationIsLandscape(toInterfaceOrientation) ) {
xibName = [NSString stringWithFormat:#"%#-Landscape", NSStringFromClass(class)];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
xibName = [NSString stringWithFormat:#"%#_%#-Landscape", NSStringFromClass(class), deviceName];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
NSAssert(FALSE, #"Missing xib");
return nil;
}
}
} else {
xibName = [NSString stringWithFormat:#"%#", NSStringFromClass(class)];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
xibName = [NSString stringWithFormat:#"%#_%#", NSStringFromClass(class), deviceName];
if([[NSBundle mainBundle] pathForResource:xibName ofType:#"nib"] != nil) {
return xibName;
} else {
NSAssert(FALSE, #"Missing xib");
return nil;
}
}
}
return nil;
}
There’s a lot going on here. Let’s go over it. I first determine if you are on an iPhone or an iPad. The xib files will have iPhone or iPad in their names. Next we check to see if we are in landscape mode. If we are, we build a test string from the class name, using class reflection via NSStringFromClass. Next, we use pathForResource to check to see if the xib exists in our bundle. If it does, we return the xib name. If it doesn’t, we try again also putting the device name into the xib name. Return it if it exists, assert a failure if it doesn’t. Portrait is similar except by convention we don’t put “-Portrait” into the xib name.
This code is useful enough and generic enough that I’ll put it in my EnkiUtils open source project.
Since this is iOS6 we need to put in the iOS6 rotation boilerplate code:
- (BOOL) shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
Curiously we also need to manually call willAnimateRotationToInterfaceOrientation on iPads. iPhones get a willAnimateRotationToInterfaceOrientation automatically but iPads do not.
- (void) viewDidAppear:(BOOL)animated
{
// iPad's don't send a willAnimate on launch...
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self willAnimateRotationToInterfaceOrientation:[[UIApplication sharedApplication] statusBarOrientation] duration:0];
}
}
So, are we finished? Embarrassingly no. You see, when I coded the XTMViewController class I broke the Model-View-Controller design pattern! This is easy to do, Apple already helps us by putting the View and the Controller into the same class. And it is so easy to carelessly mix in Model data in the VC’s .h file. And I had done exactly that. When I run the above code it work brilliantly, I could rotate it all day and the UI was correct in both orientations. But what do you think happened when I rotated the device while my exercise timers were running? Yup, they were all deleted and the UI reset to the initial state. This was not at all what I wanted!
I made a XTMUser class to hold all the timing data, I put all the NSTimers into the XTMOrientationMasterViewController class and then I made a protocol so the XTMOrientationMasterViewController could respond to UI taps in the XTMViewController class.
Then I was done.

Related

Unsure how I'm getting EXC_BAD_ACCESS KERN_INVALID_ADDRESS

I've looked through a ton of other StackOverflow posts about this error and all of them provide very reasonable solutions the problem. In other words, they generally pinpoint something in the code that isn't being auto-retained, but should be and then it subsequently causes a crash.
The problem I'm having is that the line of code that Crashlytics is telling me doesn't seem to have anything that could possibly be dealloc'd.. at least that I know of. Hopefully, you'll be able to see something I'm not seeing.
I'm not able to replicate the crash myself, but Crashlytics tells me I've had 146 of these crashes across 28 different users in the last 3 months.
My MainMenuDrawerViewController is a UITableViewController that sits in my left-side drawer (using MMDrawerController).
The crash happens in -tableView:didSelectRowAtIndexPath: on the following line:
[self updateCenterWithViewControllerIdentifiedBy:#"ReportsViewController"];
The only two objects on that line are self and a string literal, so I don't understand what could possibly be dealloc'd and causing the EXC_BAD_ACCESS.
Here is the overall method (with irrelevant code cut out):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
// removed other case statements
case DrawerRowReports: {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self performSegueWithIdentifier:#"ShowReportList" sender:self];
} else {
[self updateCenterWithViewControllerIdentifiedBy:#"ReportsViewController"];
}
break;
}
// removed other case statements
default:
break;
}
}
The -updateCenterWithViewControllerIdentifiedBy: function instantiates a View Controller from the storyboard using the given identifier, then instantiates an MMNavigationController with the first view controller as the root, then updates the mm_drawerController to put that MMNavigationController into the center position.
I'll include that method as well below, BUT the Crashlytics report doesn't say the bad access happens inside the method, it says it happens at the line above.
- (nullable id) updateCenterWithViewControllerIdentifiedBy:(nullable NSString*)storyboardIdentifier {
return [self updateCenterWithViewControllerIdentifiedBy:storyboardIdentifier withCloseAnimation:YES];
}
- (nullable id) updateCenterWithViewControllerIdentifiedBy:(nullable NSString*)storyboardIdentifier withCloseAnimation:(BOOL)withCloseAnimation {
return [self updatePosition:DrawerCenter withViewControllerIdentifiedBy:storyboardIdentifier withValueDictionary:nil withCloseAnimation:withCloseAnimation];
}
- (nullable id) updatePosition:(DrawerPosition)position withViewControllerIdentifiedBy:(nullable NSString*)storyboardIdentifier withValueDictionary:(nullable NSDictionary*)configDictionary withCloseAnimation:(BOOL)withCloseAnimation {
//BaseViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:storyboardIdentifier];
UIViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:storyboardIdentifier];
if (configDictionary != nil) {
for (NSString *fieldname in [configDictionary allKeys]) {
[viewController setValue:[configDictionary objectForKey:fieldname] forKey:fieldname];
}
}
UINavigationController * nav = [[MMNavigationController alloc] initWithRootViewController:viewController];
if (position == DrawerCenter) {
[self.mm_drawerController setCenterViewController:nav
withCloseAnimation:withCloseAnimation
completion:nil];
} else if (position == DrawerRight) {
[self.mm_drawerController setRightDrawerViewController:nav];
} else if (position == DrawerLeft) {
[self.mm_drawerController setLeftDrawerViewController:nav];
} else {
NSLog(#"unknown drawer position: %ld", (long)position);
}
return viewController;
}

UISplitviewController and different UIKeyCommands depending on master, detail or both being on screen

I want to include some UIKeyCommands in my app. My app consists of one UISplitViewController that forces the master to be always visible on iPad full screen. On smaller screen it works like it normally would.
Now, I've implemented some UIKeyCommands in the MasterViewController and some in the DetailViewController. However, the app will only show those in DetailViewController. So I put all of them in the RootSplitViewController, but that will show all of them, even when the MasterViewController is hidden in iOS 9's splitview.
What I want though, is for it to show all when the app is fullscreen on iPad and thus the MasterViewController is forced on screen together with the DetailViewController. And when the view is small (ie 50-50) and the MasterViewController is hidden, I want it to only show those of the window that's on screen.
Any ideas on how to achieve this?
In the end I managed to do this - although in a not-so-pretty way.
The UIKeyCommands are added to the RootSplitViewController.
- (NSArray *)keyCommands {
if (self.view.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassRegular) {
return #[
[UIKeyCommand keyCommandWithInput:#"r" modifierFlags:UIKeyModifierCommand action:#selector(changeRestaurant:) discoverabilityTitle:#"Change restaurant"],
[UIKeyCommand keyCommandWithInput:#"t" modifierFlags:UIKeyModifierCommand action:#selector(changeTable:) discoverabilityTitle:#"Change table"]
];
} else {
if (self.masterIsVisible == YES) {
return #[
[UIKeyCommand keyCommandWithInput:#"t" modifierFlags:UIKeyModifierCommand action:#selector(changeRestaurant:) discoverabilityTitle:#"Change restaurant"]
];
} else {
return #[
[UIKeyCommand keyCommandWithInput:#"t" modifierFlags:UIKeyModifierCommand action:#selector(changeTable:) discoverabilityTitle:#"Change table"]
];
}
}
}
Those methods call the actual methods in the specific UIViewController.
- (void)changeRestaurant:(id)sender {
UINavigationController *nav = (UINavigationController *)[self.viewControllers objectAtIndex:0];
RestaurantController *master = [nav.viewControllers objectAtIndex:0];
[master changeRestaurant];
}
- (void)changeTable:(id)sender {
UINavigationController *nav = (UINavigationController *)[self.viewControllers objectAtIndex:1];
TableController *detail = [nav.viewControllers objectAtIndex:0];
[detail changeTable:sender];
}
In order for this to work I added a BOOL to the UISplitViewController.
#interface RootSplitViewController : UISplitViewController
#property (nonatomic) BOOL masterIsVisible;
#end
Which is then called in the MasterViewController.
- (void)viewDidDisappear:(BOOL)animated {
RootSplitViewController *rootView = (RootSplitViewController *)self.splitViewController;
rootView.masterIsVisible = NO;
}
- (void)viewDidAppear:(BOOL)animated {
RootSplitViewController *rootView = (RootSplitViewController *)self.splitViewController;
rootView.masterIsVisible = YES;
}
I know this might not be the pretties method, but it works. If anyone knows a better way to do it, I'd love to hear your feedback.

disable rotation in single View iOS

I am trying to disable rotation for just a single viewController in iOS, i have see some questions asked for auto rotate but none for this.
I want to lock the orientation that View B opens into For ex: opens in landscape, it can only be landscape, or opens in portrait, it can only be portrait. While still allowing view A to be whatever orientation it wants.
EDIT
\n
This is how i call view B
[self.mediaFocusController showImageFromURL:url fromView:self.view withThumb:thumbUrl];
This is how it enters:
if (self.targetViewController) {
[self willMoveToParentViewController:self.targetViewController];
if ([UIView instancesRespondToSelector:#selector(setTintAdjustmentMode:)]) {
self.targetViewController.view.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
[self.targetViewController.view tintColorDidChange];
}
[self.targetViewController addChildViewController:self];
[self.targetViewController.view addSubview:self.view];
if (self.snapshotView) {
[self.targetViewController.view insertSubview:self.snapshotView belowSubview:self.view];
[self.targetViewController.view insertSubview:self.blurredSnapshotView aboveSubview:self.snapshotView];
}
}
else {
// add this view to the main window if no targetViewController was set
if ([UIView instancesRespondToSelector:#selector(setTintAdjustmentMode:)]) {
self.keyWindow.tintAdjustmentMode = UIViewTintAdjustmentModeDimmed;
[self.keyWindow tintColorDidChange];
}
[self.keyWindow addSubview:self.view];
if (self.snapshotView) {
[self.keyWindow insertSubview:self.snapshotView belowSubview:self.view];
[self.keyWindow insertSubview:self.blurredSnapshotView aboveSubview:self.snapshotView];
}
}
I have NavigationController set up but this view is different
Trying this code (I found that in https://stackoverflow.com/a/17377221/2040992):
- (BOOL)shouldAutorotate
{
id currentViewController = self.topViewController;
if ([currentViewController isKindOfClass:[DetailViewController class]])
return NO;
return YES;
}

ZBarReaderViewController reader view changes orientation for iOS6 & iOS7 even after restricting it by _reader.supportedOrientationsMask

I am using ZBarSDK for QR Code scanning feature. I want to use this only in PORTRAIT mode only. As per the documentation I set it up with below code line:
_reader.supportedOrientationsMask = ZBarOrientationMask(UIInterfaceOrientationPortrait);
As expected it works well with iOS 5 but with the same code this view changes orientation for iOS 6 & 7. Is supportedOrientationsMask only works with < iOS 6? Is there any other way to force this ZBar reader camera view to work only in Portrait mode? Thanks in advance
Here more details with Code:
if(_reader) // first check `_reader` is created or not?
{
[_reader.readerView stop]; // then stop continue scanning stream of "self.ZBarReaderVC"
for(UIView *subViews in _reader.view.subviews) // remove all subviews
[subViews removeFromSuperview];
[_reader.view removeFromSuperview];
_reader.view = nil;
}
_reader = [ZBarReaderViewController new];
_reader.readerDelegate = self;
_reader.supportedOrientationsMask = ZBarOrientationMask(UIInterfaceOrientationPortrait);
ZBarImageScanner *scanner = _reader.scanner;
// EXAMPLE: disable rarely used I2/5 to improve performance
[scanner setSymbology: ZBAR_I25
config: ZBAR_CFG_ENABLE
to: 0];
[_reader.view setFrame:CGRectMake(0, _topbar.frame.size.height, self.view.bounds.size.width, self.view.bounds.size.height-_topbar.frame.size.height)];
_reader.cameraOverlayView = [self CommomOverlay];
_reader.showsZBarControls=NO;
// present and release the controller
[self presentModalViewController: _reader
animated: NO];
Let me know in case more details required.
Finally found the solution.
The problem was like this:
ZbarViewController *reader was presented from my current view controller and it's portrait support property was not working somehow.
_reader.supportedOrientationsMask = ZBarOrientationMask(UIInterfaceOrientationPortrait);
What i did to resolve this issue is I created TBZbarViewController the new class which was inheriting the ZbarViewController class and placed the below method.
-(BOOL)shouldAutorotate{
return NO;
}
Then I used the TBZbarViewController *reader to present from My controller which solved the issue and it's working in Portrait mode only as needed.
Thanks.
I did like this, and is working for all iOS versions :
Step 1 : Set your Device Orientation
Step 2 : Add this code into you implementation (.m) file.
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_6_0
- (BOOL) shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationMaskPortrait;
}
#endif
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}

iOS Controllers for universal (iPhone/iPad) app

I am just working on iOS app and I want to make it universal for both iPhones and iPads. This is done and works without any problems:
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController_iPhone = [[ViewController_iPhone alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
self.viewController_iPad = [[ViewController_iPad alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
if (self.viewController_iPhone == nil)
self.window.rootViewController = self.viewController_iPad;
else
self.window.rootViewController = self.viewController_iPhone;
There is a view for each controller (ViewController_iPad.xib, ViewController_iPhone.xib). It doesn't matter which view is loaded in my problem. In a view there is a subview added (UIScrolView). And in this ScrollView there are two views from xib:
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"SubView1" owner:self options:nil];
UIView *view = [nibContents objectAtIndex:0];
view.frame = CGRectMake(2, 0, scrollView.frame.size.width - 2, scrollView.frame.size.height);
[scrollView addSubview:view];
nibContents = [[NSBundle mainBundle] loadNibNamed:#"SubView2" owner:self options:nil];
view = [nibContents objectAtIndex:0];
view.frame = CGRectMake(scrollView.frame.size.width + 2 , 0, scrollView.frame.size.width - 4, scrollView.frame.size.height);
[scrollView addSubview:view];
(This code is in iPad/iPhone controller). Still everything is OK. But I don't know how to set owners (in IB) of these subviews that are shown in ScrollView. These subviews are in ScrollView which is in a main view so I want to set owners of these subviews as iPad/iPhone controller. But as a owner can be only one class. Can you tell me how to set owners if I have two main controllers and I don't know which one will be loaded in runtime. Thank you.
EDIT: I have another question: I have ViewController_iPhone. It has a View property and this property is assigned to the "root" view in the main view in ViewController_iPhone (.xib). Can I assign this view property also to subview view? Because I got EXC_BAD_ACCESS error if I assign view property of ViewController_iPhone to a "root" view of subview in IB.
Looks like you need to use a class cluster. This will abstract the iPhone/iPad instantiation, so you don't explicitly need to instantiate one of the two.
You can read a bit about class clustering in the Apple documentation:
http://developer.apple.com/library/ios/#documentation/general/Conceptual/DevPedia-CocoaCore/ClassCluster.html
It boils down to creating a master view controller which will handle the allocation of iPhone or iPad subclasses based on the current device.
You should override the ViewController alloc class method:
+ (id)alloc {
NSString *classString = NSStringFromClass([self class]);
NSString *append = nil;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
append = #"_iPhone";
} else {
append = #"_iPad";
}
NSString *subClassString = [classString stringByAppendingString:append];
id subClass = NSClassFromString(subClassString);
id object;
if (subClass && ![self isKindOfClass:[subClass class]]) {
object = [subClass alloc];
} else {
object = [super alloc];
}
return object;
}
This way you can just allocate the ViewController class and at runtime the correct class definition will be used to instantiate your view controller.
This will allow you to use the ViewController class as the owner in IB provided that you create an abstraction of iPhone and iPad interfaces and define it in their super class.

Resources