iOS: How to distinguish between iPhone mode and iPad mode? - ios

I'm building an universal iOS app and some config variables have different value between iPhone mode and iPad mode. Please notice that I said the Mode word.
As I know, users could install an iPhone app in iPad from AppStore, so there is something confused to me about the following code:
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType rangeOfString:#"iPad" options:NSCaseInsensitiveSearch].location != NSNotFound)
{
fCurrentDeviceIsiPad = YES;
// ...
}
else // iPhone? iPod?
{
fCurrentDeviceIsiPad = NO;
// ...
}
If user install & run the iPhone version app in iPad, the fCurrentDeviceIsiPad will be YES (I tested the result with debugging it in my iPad for iPhone version, certainly, I'd changed the deployment device as "iPhone" only first).
So, my question is how could I distinguish the app mode without taking the device type as consideration?
Someone posted something about the OS macros, I've tried them in my app.
#if TARGET_OS_IPHONE
// iPhone specific code
#error A
#elif TARGET_OS_IPAD
// iPad specific code
#error B
#else
// Unknown target
#error C
#endif
This didn't hit my point, either. I switched the debug device to iPhone simulator and iPad simulator, all the compile errors are hit on error A. Did I misunderstand the meaning of these macros?
One more question, this will be the first time I submit an iOS to AppStore and it's an universal app. For the submit process, is it standalone process for uploading iPhone version binary and iPad version binary? You may get my new point: if the answer is yes, it's ok for me to build two versions manually with my custom-macros.
Thanks!
UPDATE: Problem solved!
The code [UIDevice currentDevice].model is NOT the same with the UI_USER_INTERFACE_IDIOM(), the former one always returns the real device type, the latter one is the current running app mode/version.

BOOL isIpad() {
return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
}

Create an NSObject file and name it Common. Delete the .m and delete all the code written in Common.h
Import the Common.h file in .pch file
Put this is the Common.h file
And then you can use it throughout your app.
#define IS_IPHONE (!IS_IPAD)
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone)
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
Thanks.

Related

Preprocessor macro for Apple Watch?

I was looking at Apple's Lister (for Apple Watch, iOS, and OS X) sample. The sample performs a test for iOS and OS X:
#import <TargetConditionals.h>
#if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
#import ListerKit;
#elif TARGET_OS_MAC
#import ListerKitOSX;
#endif
However, there is no test for TARGET_OS_WATCH or similar. Grepping for watch in TargetConditionals.h delivers no hits:
$ cat /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
/SDKs/iPhoneOS7.1.sdk/usr/include/TargetConditionals.h | grep -i watch
$
From TargetConditionals.h, I know there are:
These conditionals specify in which Operating System the generated code will
run. The MAC/WIN32/UNIX conditionals are mutually exclusive. The EMBEDDED/IPHONE
conditionals are variants of TARGET_OS_MAC.
TARGET_OS_MAC - Generate code will run under Mac OS
TARGET_OS_WIN32 - Generate code will run under 32-bit Windows
TARGET_OS_UNIX - Generate code will run under some non Mac OS X unix
TARGET_OS_EMBEDDED - Generate code will run under an embedded OS variant
of TARGET_OS_MAC
TARGET_OS_IPHONE - Generate code will run under iPhone OS which
is a variant of TARGET_OS_MAC.
TARGET_IPHONE_SIMULATOR - Generate code for running under iPhone Simulator
Question: Is there a preprocessor for Apple's watch?
I'm tagging with ios, but I'm not sure that's the correct OS for this question.
The list below was compiled from iPhone's TargetConditionals.h. The Simulator and OS X are similar (they just have different bits set to 1):
#define TARGET_OS_MAC 1
#define TARGET_OS_WIN32 0
#define TARGET_OS_UNIX 0
#define TARGET_OS_EMBEDDED 1
#define TARGET_OS_IPHONE 1
#define TARGET_IPHONE_SIMULATOR 0
Questions: Does the watch use TARGET_OS_EMBEDDED? Does the watch omit TARGET_OS_IPHONE?
You can find all kind of target conditionals in the TargetConditionals.h (cmd + shift + o and type TargetConditionals.h).
In this list you can find a list like this and many more useful defines.
Currently it does contain TARGET_OS_WATCH since WatchOS 2. For WatchOS 1 it was not possible to run custom code on the watch so it was not needed back then since everything ran on the phone itself.
#define TARGET_OS_MAC 1
#define TARGET_OS_WIN32 0
#define TARGET_OS_UNIX 0
#define TARGET_OS_IPHONE 1
#define TARGET_OS_IOS 0
#define TARGET_OS_WATCH 1
#define TARGET_OS_TV 0
#define TARGET_OS_SIMULATOR 0
#define TARGET_OS_EMBEDDED 1
Swift Addition
#if os(watchOS)
[Watch code]
#else
[Code for iOS, appleTV, or any else clause]
#endif
Some other valid values: iOS, OSX, tvOS
A small explanation about this and more http://nshipster.com/swift-system-version-checking/
At the bottom of this document
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_15#Build Configurations
Under the section 'Build Configurations' you can find a (hopefully) up to date list with all these values that are currently available
As of watchOS 2.0, you can run native code on the watch, so this is a more relevant question.
I'm using the first early beta of watchOS 2, so this may change, but right now, TARGET_OS_WATCH is set to 1 on watchOS.
(Also, be careful: TARGET_OS_IPHONE is also set to 1 on watchOS, though TARGET_OS_IOS is 0.)
There is no WatchKit or app extension target conditional. But you can create your own per-target conditionals that you use in the same way.
If you look in the "Build Settings" section for any target, there's a section called "Other C Flags". Add an entry for the WatchKit target. If you add something like -DMY_WATCHKIT_FLAG=1, you can then do #if MY_WATCHKIT_FLAG in code.
Make your custom flag, well, custom. It's not impossible that Apple might add a flag in the future called something like TARGET_WATCH_APP or whatever. Use a prefix on the flag name to make it specific to you.
With the current WatchKit SDK, all code in a Watch application runs on the phone it’s paired with, so there’s no point at which your preprocessor is going to encounter code that’s going to run on the Watch and thus not much use for a macro to tell it what to do when it does. The code in the ListerWatch target of the sample you linked to will run as an extension on the iPhone and talk to its watch UI via WatchKit.

iOS Simulator iOS Version

Is there a way to detect what iOS simulator you are running. i.e. The difference between running 5.1 vs. 6.1.
Using [[UIDevice currentDevice] systemVersion] return x86 so i can't determine the difference between the 6.1 simulator and the 7.0 simulator.
You should take a look at the Availability
The relevant declarations are all in
Availability.h
AvailabilityInternal.h
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0
// Here we go with iOS 6++
#else
// This is the old way
#endif
[[UIDevice currentDevice] systemVersion] should give you the information you need. However, that is not something that should be used for logic. It is designed to be a user-presentable string.
If you want to make logic decisions, you should do it based on capabilities. For example, check for the existence of potentially-weak symbols at runtime before using them rather than making decisions based on the system version.
That's easy!
Swift version:
if #available(iOS 11.0, *) {
} else {
}
Objective-C version:
if (#available(iOS 11.0, *)) {
} else {
}

Support of several iOS - application development

With the release of iOS6, I am going to update my application using the new feature of iOS6, such as UICollectionView.
When I will deploy it on App Store, my application will be only available for devices on iOS6.
Question: How can I let the devices on iOS 5.1 using the older release which do not use UICollectionView?
I could go more deeper by asking: What is the strategy to upgrade an application without loosing the users which have not yet upgrade their OS?
Thank you for your advice.
You could check for availability of new iOS6 classes with Class tmp = NSClassFromString(#"UIActivityViewController");
if (tmp) {//use iOS6 class}else{//do something else}. Also, you could use some macros, like __IPHONE_5_0, put some code between #ifndef or #if defined(...). Good Luck!
This one will work perfectly:
NSString * systemVersion = [[UIDevice currentDevice] systemVersion];
if ([self.systemVersion compare:#"5.0" options:NSNumericSearch] != NSOrderedAscending)
{
osHigherThaniOS5 = TRUE;
}
if ([self.systemVersion compare:#"6.0" options:NSNumericSearch] != NSOrderedAscending)
{
osHigherThaniOS6 = TRUE;
}
Just determine the system version and set your conditions accordingly :)

#define based upon platform [iPhone or iPad]

I'm trying to make my iPhone app compatible with the iPad.
In a header file I set up some constants.
Because of the larger screen I want some constants used for images to be larger on the iPad than on to the iPhone.
I found some suggestions on the internet to accomplish this:
#if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define imgAmcWidth 656.0f
#define imgAmcHeight 36.0f
#else
#define imgAmcWidth 240.0f
#define imgAmcHeight 20.0f
#endif
This seems to satisfy my needs.
Unfortunately xcode 4 fails to compile this giving an error: 'Token "[" is not valid in preprocessor..' [LLVM GCC 4.2].
What am I doing wrong?
While probably not the most elegant solution but to prevent a major rewrite of the code I decided to use the following trick:
#define iPad UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
#define imgAmcWidth (iPad ? 639.0f : 240.0f)
// etc..
UI_USER_INTERFACE_IDIOM and UIUserInterfaceIdiomPad are not preprocessor things. They are part of iOS, so you should do:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
<define your constants here>
} else {
<define your constants here>
}
See also this if you plan to support iOS versions previous to 3.2

Is there a specific Xcode compiler flag that gets set when compiling for iPad?

Is there a specific Xcode compiler flag that gets set when compiling for iPad?
I want to conditionally compile iPad vs iPhone/iPod Touch code for example:
#ifdef TARGET_IPAD
code for iPad
#else
code for iPhone
#endif
I know there is already TARGET_OS_IPHONE and TARGET_CPU_ARM in TargetConditionals.h but anything that easily and specifically targets iPad?
-Rei
The correct API to use for run-time checking of iPad vs. iPhone/iPad Touch is:
BOOL deviceIsPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
The UIDevice header filer also includes a convenient macro, UI_USER_INTERFACE_IDIOM(), which will be helpful if your deployment target is < iPhone 3.2.
#define UI_USER_INTERFACE_IDIOM() ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)] ? [[UIDevice currentDevice] userInterfaceIdiom] : UIUserInterfaceIdiomPhone)
So you could just say, negatively:
BOOL deviceIsPad = (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone);
Instead of using compile-time flags, you should use run-time check e.g. use NSClassFromString to see if a class exists because the same app can be installed on both devices.
And because of the possibility of running the app in both devices, there isn't a built-in compile-time check whether it targets iPad or not.
Currently I didn’t find anything that would let you check if you are on an iPad, but I’m also not sure if Apple recommends runtime checks. Here’s an excerpt from Apple:
In addition to your view controllers, any classes that are shared between iPhone and iPad devices need to include conditional compilation macros to isolate device-specific code. Although you could also use runtime checks to determine if specific classes or methods were available, doing so would only increase the size of your executable by adding code paths that would not be followed on one device or the other. Letting the compiler remove this code helps keep your code cleaner.
However, there is no place I could find more specific information about conditional compilation macros.
For multiple targets sharing the same project/code, I'm doing this by editing the C flags for my iPad target.
With the [myapp]-iPad target chosen as the active target, pick Project -> Edit Active Target [myapp]-iPad. Search for "c flags" and double-click. Add a flag for "-D TARGET_IPAD". Now the symbol TARGET_IPAD will be defined only for your iPad target.
Of course, this only works if you're using separate targets for iPad and iPhone. If you're running the same binary on both, obviously there's nothing the compiler can do for you. (However, the 3.2 SDK as of the end of January doesn't even support Universal apps yet.)
(Edited; I was confused about the terminology of "Universal" apps etc.)
Or -> just to be sure
-(BOOL)isDeviceAniPad
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
BOOL deviceIsPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
return deviceIsPad;
#endif
return NO;
}
I think this will do
-(BOOL)isDeviceAniPad
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
#endif
return NO;
}

Resources