#if TARGET_OS_IPHONE with iPhone and iPad - iphone

i have a problem, i want add different import file for iPhone and iPad, but for iPad doesnt' work, this is how i do:
#if TARGET_OS_IPHONE
#import "MyView_iPhone.h"
#elif TARGET_OS_IPAD
#import "MyView_iPad.h"
#endif
when in the code then i write for example:
MyView_iPhone *iphone = [MyView_iPhone alloc] init];
works, but:
MyView_iPad *iphone = [MyView_iPad alloc] init];
doens't work, give me an error, because doens't see the MyView_iPad.h, how i can do?
this is the error:
Unknown receiver 'MyView_iPad'; did you mean 'MyView_iPhone'?

<TargetConditionals.h> does not actually define a TARGET_OS_IPAD. You can't know at compile time whether you're executing for iphone or ipad! That is something you should check at runtime, importing both views and doing something like:
UIView *iphone;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
iphone = [[MyView_iPad alloc] init];
}
else{
iphone = [[MyView_iPhone alloc] init];
}

Related

NSInternalInconsistencyException - Could not load NIB in bundle:

I am trying to bundle all my .xib resources into a "ViewController.bundle" to ship along with "MyLibrary.framework". If I just place all the .xib files in the framework project, everything is working fine, but if I put them inside "ViewController.bundle", everything break apart
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'Could not load NIB in bundle:
'NSBundle </Users/administrator/Library/Application Support/iPhone Simulator/5.0/
Applications/8AD68FD1-158E-4926-AE03-8FEF870E7012/ios.app/ViewController.bundle>
(not yet loaded)' with name 'FirstViewController_iPhone''
*** First throw call stack:
(0x160e052 0x1071d0a 0x15b6a78 0x15b69e9 0x3ff838 0x2a6e2c 0x2a73a9 0x2a75cb 0x2c2b89 0x2c29bd
0x2c0f8a 0x2c0d7e 0x2c0a22 0x2bfd88 0x5e3ac 0x2df8 0x2e34 0x160fec9 0x1e45c2 0x1e455a 0x289b76
0x28a03f 0x2892fe 0x209a30 0x209c56 0x1f0384 0x1e3aa9 0x1eedfa9 0x15e21c5 0x1547022 0x154590a
0x1544db4 0x1544ccb 0x1eec879 0x1eec93e 0x1e1a9b 0x2452 0x2385)
I have tried various ways:
1. Creating the "ViewController.bundle" in Finder, place all the .xib there, and drag to xcode
2. Create a new Target for the framework project, choose "Bundle" under "Framework & Library" in "OS X" section (Note: it has to be OSX since I am using xcode 4.6 which do not have "Bundle" under its "Framework & Library" - it used to have, and I used to create bundle that contains .png resources using this wizard)
Under both circumstances, I have made sure that the "ViewController.bundle" exist in "Build Phase" >> "Copy Bundle Resources" in my target app.
I also tried including "ViewController.bundle" under "Target Dependencies" of my Framework project, but all my efforts resulted in the same crash.
I also tried various ways to load the .xib, to no avail. However, the following code that load a .png resource from the same "ViewController.bundle" works perfectly:
// Loading .png resource works perfectly
// UIImage* closeImage = [UIImage imageNamed:#"ViewController.bundle/first.png"];
// NSString *szBundlePath = [[NSBundle mainBundle] pathForResource:#"ViewController" ofType:#"Bundle"];
// UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"showView" message:szBundlePath delegate:nil cancelButtonTitle:#"ok" otherButtonTitles:nil];
// UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(220, 10, 40, 40)];
// [imageView setImage:closeImage];
// [alertView addSubview:imageView];
// [alertView show];
// return;
Using the same code to load the xib failed:
NSBundle *bViewController = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:#"ViewController" withExtension:#"bundle"]];
if ( [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone ) {
self.m_pFirstViewController = [[[FirstViewController alloc] initWithNibName:#"FirstViewController_iPhone" bundle:bViewController] autorelease];
self.m_pSecondViewController = [[[SecondViewController alloc] initWithNibName:#"SecondViewController_iPhone" bundle:bViewController] autorelease];
} else {
self.m_pFirstViewController = [[[FirstViewController alloc] initWithNibName:#"ViewController.bundle/FirstViewController_iPad" bundle:nil] autorelease];
self.m_pSecondViewController = [[[SecondViewController alloc] initWithNibName:#"ViewController.bundle/SecondViewController_iPad" bundle:nil] autorelease];
}
/** It crash in this line!! Note however, both m_pFirstViewController and m_pSecondViewController is allocated and not nil */
self.viewControllers = #[self.m_pFirstViewController, self.m_pSecondViewController];
/** never reach here already crash */
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:self animated:YES completion:^{
NSLog( #"presented!" );
}];
I have spent the last 8 hours debugging this, googling and tried various suggestions from SO, but to no avail... any help is greatly appreciated! Thanks!
The 2nd way that I tried is the closest... Then modify the OSX bundle to iOS bundle in the Build Settings. Apparently, there are several steps that I missed. I followed the tutorial and sample project by Matt Galloway here:
http://www.galloway.me.uk/tutorials/ios-library-with-resources/
and now everything works as intended.
Thanks everyone for your help!

iPhone 5 device modifier for dynamic xib loading

I'm working on a universal iOS app and wanting to use different xib files for iPhone 5. The way it currently works is by automatically selecting the file with the right device modifier (as specified here: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/LoadingResources/Introduction/Introduction.html#//apple_ref/doc/uid/10000051i-CH1-SW2). However there's no mention there of how to handle the iPhone 5. I'm surprised because I'm under the impression they'd prefer developers to create a different/unique experience on the 5 instead of just auto scaling...
Anyone aware of a modifier that works? (eg myxib~iphone5.xib) It would be more convenient than having to handle the device detection and switching myself.
Cheers!
There's no naming convention unfortunately, but there's a way. This is what I'm using:
I have a global .h file called GlobalConstants that I put all my #define macros in. There I have this:
static BOOL is4InchRetina()
{
if ((![UIApplication sharedApplication].statusBarHidden && (int)[[UIScreen mainScreen] applicationFrame].size.height == 548) || ([UIApplication sharedApplication].statusBarHidden && (int)[[UIScreen mainScreen] applicationFrame].size.height == 568))
return YES;
return NO;
}
then in any of my view controller classes - I override the init method like this:
#import GlobalConstants.h
-(id) init {
if(is4InchRetina()) self = [super initWithNibName:#"myNibName5" bundle:[NSBundle mainBundle]];
else self = [super initWithNibName:#"myNibName" bundle:[NSBundle mainBundle]];
return self
}

MonoTouch - How do I declare UIPopoverController only when using an iPad device?

As I understand from reading documentation, UIPopoverControllers are only supported on the iPad. Therefore if you try to declare a variable as a UIPopoverController and run the app in the iPhone simulator or on an iPhone, you get an error such as:
UIPopoverController initWithContentViewController:] called when not running under UIUserInterfaceIdiomPad
So I have a universal monotouch app I am trying out, where I would like to use a UIPopoverController when the user is using an iPad, for the iPhone I have another solution.
This is how I am declaring it at the moment, but obviously running on the iPhone does not work, and I get the above error message.
public partial class IOPSCalculatorViewController : UIViewController
{
static bool UserInterfaceIdiomIsPhone {
get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; }
}
static bool UserInterfaceIdiomIsIPAD {
get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad; }
}
UIPopoverController DetailViewPopover = new UIPopoverController(new PopoverContentViewController());
}
How can I only declare the:
UIPopoverController DetailViewPopover = new UIPopoverController(new PopoverContentViewController());
part if the device is an iPad? I need the UIPopoverController to be in the public partial class so that I can access it publically in other places.
Instead of declaring and allocating in one line just split it in two lines. E.g.
UIPopoverController DetailViewPopover = null;
if (IOPSCalculatorViewController.UserInterfaceIdiomIsIPAD) {
DetailViewPopover = new UIPopoverController (new PopoverContentViewController ());
}
That will also work if DetailViewPopover is a (public) field instead of an instance variable and, as long as the UIPopoverController constructor is not invoked, you won't be getting the error.
You need to find out what is your current device and write code for iphone and iPad as well. here is a snap of code that I've used in my case.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
//Do your coding here
}
else {
if(![popoverController isPopoverVisible]){
contact = [[[ContactViewController alloc] initWithNibName:#"ContactViewController_iPad" bundle:nil] autorelease];
popoverController = [[[UIPopoverController alloc] initWithContentViewController:contact] retain];
[popoverController setPopoverContentSize:CGSizeMake(400.0f, 400.0f)];
[popoverController presentPopoverFromRect:CGRectMake(230, 860, 320.0f, 320.0f) inView:self.view permittedArrowDirections:UIPopoverArrowDirectionDown animated:YES];
[popoverController release];
}else{
[popoverController dismissPopoverAnimated:YES];
}
}
hope this will help you. Happy Coding!!!

how can I check if a Gyroscope is present on device?

Just wondering can I check if the device (iPhone, iPad, iPod i.e. iOS devices) has a Gyroscope ?
- (BOOL) isGyroscopeAvailable
{
#ifdef __IPHONE_4_0
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
BOOL gyroAvailable = motionManager.gyroAvailable;
[motionManager release];
return gyroAvailable;
#else
return NO;
#endif
}
See also my this blog entry to know you can check for different capabilities in iOS devices
http://www.makebetterthings.com/blogs/iphone/check-ios-device-capabilities/
CoreMotion's motion manager class has a property built in for checking hardware availability. Saurabh's method would require you to update your app every time a new device with a gyroscope is released (iPad 2, etc). Here's sample code using the Apple documented property for checking for gyroscope availability:
CMMotionManager *motionManager = [[[CMMotionManager alloc] init] autorelease];
if (motionManager.gyroAvailable)
{
motionManager.deviceMotionUpdateInterval = 1.0/60.0;
[motionManager startDeviceMotionUpdates];
}
See the documentation for more info.
I believe the answers from #Saurabh and #Andrew Theis are only partially correct.
This is a more complete solution:
- (BOOL) isGyroscopeAvailable
{
// If the iOS Deployment Target is greater than 4.0, then you
// can access the gyroAvailable property of CMMotionManager
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
BOOL gyroAvailable = motionManager.gyroAvailable;
[motionManager release];
return gyroAvailable;
// Otherwise, if you are supporting iOS versions < 4.0, you must check the
// the device's iOS version number before accessing gyroAvailable
#else
// Gyro wasn't available on any devices with iOS < 4.0
if ( SYSTEM_VERSION_LESS_THAN(#"4.0") )
return NO;
else
{
CMMotionManager *motionManager = [[CMMotionManager alloc] init];
BOOL gyroAvailable = motionManager.gyroAvailable;
[motionManager release];
return gyroAvailable;
}
#endif
}
Where the SYSTEM_VERSION_LESS_THAN() is defined in this StackOverflow answer.

Universal iOS app crashing on iPhone/iTouch 3.1.3 due to UIPopoverController

I just updated my app so that it's a universal app. In doing so I added support for UIPopoverController in a certain place. Now the app seems to be crashing on 3.1.3 iPhone/iTouch devices:
OS Version: iPhone OS 3.1.3 (7E18)
Report Version: 104
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x00000001, 0xe7ffdefe
Crashed Thread: 0
Dyld Error Message:
Symbol not found: _OBJC_CLASS_$_UIPopoverController
What I don't get is that I'm only trying to call UIPopoverController if the hardware is an iPad:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:btc];
CGSize popoverSize = { 300.0, 500.0 };
popover.delegate = self;
popover.popoverContentSize = popoverSize;
self.bmPopover = popover;
[popover release];
[self.bmPopover presentPopoverFromBarButtonItem:self.bmBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
} else {
[self presentModalViewController:nav animated:YES];
}
I do have an iVar and a property of type UIPopoverController declared but I wouldn't have expected this to cause an issue at runtime if I didn't actually try to call methods in the class.
What am I supposed to do to make sure that the system doesn't try to link with UIPopoverController at runtime when this isn't supported?
For Universal app you should not only check if this is ipad or does this class exists but you should link UIKit as Weak reference a not default ( strong ), to do this:
get to target info
select general
in linked libraries change UIKit required to UIKit weak
Have fun making universal apps :]
Even though this code would most likely never run on the iPhone, it will still be linked and thus you are receiving the error. Before instantiating, you need to check if the class exists. You can modify your code above to the following which will fix it.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
Class UIPopoverControllerClass = NSClassFromString(#"UIPopoverController");
if (UIPopoverControllerClass != nil) {
UIPopoverController *popover = [[UIPopoverControllerClass alloc] initWithContentViewController:btc];
CGSize popoverSize = { 300.0, 500.0 };
popover.delegate = self;
popover.popoverContentSize = popoverSize;
self.bmPopover = popover;
[popover release];
[self.bmPopover presentPopoverFromBarButtonItem:self.bmBarButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
} else {
[self presentModalViewController:nav animated:YES];
}
Also, you could weak link against the UIKit framework, which would also solve the problem. I prefer the code solution as it is more safe.
You need to stop the compiler errors by referencing the class from a string. This should be used in tandem with UI_USER_INTERFACE_IDIOM() and will compile on an SDK that does not know about UIPopoverController. Here is an example:
Class popClass = NSClassFromString(#"UIPopoverController");
if(popClass) {
id infoPop = [[popClass alloc] initWithContentViewController:popViewController];
[infoPop presentPopoverFromRect:CGRectMake(20, 70, 10, 10) inView:self.view permittedArrowDirections:4 animated:YES];
}
The easy answer:
In your Xcode project, Select the top level project icon. Do a Get Info, go to the BUILD panel. set Configurations to All Configurations, set Show: to ** All Settings**
Set the iOS Deployment Target to iOS 3.1.
Recompile. Your program is failing because the minimum OS is set too high, so the loader can't resolve the symbol UIPopoverController. The change I just walked you through makes that symbol weak: it will resolve to NULL, but at least your program will load.
Are you loading a NIB file that implicitly creates a UIPopoverController? That's another way to crash.
I found it useful to add this, so that I could use UI_USER_INTERFACE_IDIOM() on pre-3.2 systems.
#ifndef __IPHONE_3_2 // if iPhoneOS is 3.2 or greater then __IPHONE_3_2 will be defined
typedef enum {
UIUserInterfaceIdiomPhone, // iPhone and iPod touch style UI
UIUserInterfaceIdiomPad, // iPad style UI
} UIUserInterfaceIdiom;
#define UI_USER_INTERFACE_IDIOM() UIUserInterfaceIdiomPhone
#endif // ifndef __IPHONE_3_2