50

The reason I need to find out is that on an iPad, a UIPickerView has the same height in landscape orientation as it does in portrait. On an iPhone it is different. The iPad programming guide introduces an "idiom" value to UIDevice:

    UIDevice* thisDevice = [UIDevice currentDevice];
    if(thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
    {
        // iPad
    }
    else
    {
        // iPhone
    }

which works OK while you're in iPad (3.2) but not iPhone (3.1.3) - so it looks like there also needs to be an ifdef to conditionally compile that check, like:

#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
        UIDevice* thisDevice = [UIDevice currentDevice];
        if(thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
        {
            // etc.
        }
#endif

To me that's starting to look very clumsy. What's a better way?

skaffman
  • 398,947
  • 96
  • 818
  • 769
Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114

10 Answers10

64

Checking at runtime (your first way) is completely different from #if at compile time. The preprocessor directives won't give you a universal app.

The preferred way is to use Apple's Macro:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
     // The device is an iPad running iPhone 3.2 or later.
}
else
{
     // The device is an iPhone or iPod touch.
}

Use 3.2 as the base SDK (because the macro is not defined pre 3.2), you can target prior OS versions to get it running on the iPhone.

Eiko
  • 25,601
  • 15
  • 56
  • 71
38

I'm answering this now (and at this late date) because many of the existing answers are quite old, and the most Up Voted actually appears to be wrong according to Apples current docs (iOS 8.1, 2015)!

To prove my point, this is the comment from Apples header file (always look at the Apple source and headers):

/*The UI_USER_INTERFACE_IDIOM() macro is provided for use when
  deploying to a version of the iOS less than 3.2. If the earliest
  version of iPhone/iOS that you will be deploying for is 3.2 or
  greater, you may use -[UIDevice userInterfaceIdiom] directly.*/

Therefore, the currently APPLE recommended way to detect iPhone vs. iPad, is as follows:

1) (DEPRECATED as of iOS 13) On versions of iOS PRIOR to 3.2, use the Apple provided macro:

// for iPhone use UIUserInterfaceIdiomPhone
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)

2) On versions of iOS 3.2 or later, use the property on [UIDevice currentDevice]:

// for iPhone use UIUserInterfaceIdiomPhone
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
Cliff Ribaudo
  • 8,932
  • 2
  • 55
  • 78
  • If you jump to the definition of UI_USER_INTERFACE_IDIOM(), you will see that it uses the [[UIDevice currentDevice] userInterfaceIdiom] property. `static inline UIUserInterfaceIdiom UI_USER_INTERFACE_IDIOM() { return ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] ? [[UIDevice currentDevice] userInterfaceIdiom] : UIUserInterfaceIdiomPhone);` – KarenAnne Apr 16 '15 at 08:26
  • Yah! That's how I found the comment in their docs which I quoted in this answer :) – Cliff Ribaudo Apr 16 '15 at 11:18
  • What I mean is you can use `UI_USER_INTERFACE_IDIOM()` even in iOS 3.2 or later because it is the same thing as `[UIDevice currentDevice].userInterfaceIdiom]`. Right? – KarenAnne Apr 21 '15 at 02:09
  • Yes, you can use it. But why use the macro when you can use what it calls directly? – Cliff Ribaudo Apr 21 '15 at 10:40
  • What I mean is that in your example, you call the macro prior to 3.2, and call it directly in 3.2 later. There's no difference. – KarenAnne Apr 28 '15 at 09:18
  • The point of my answer is this: >= 3.2, the pre-processor simply replaces the macro with a call to [UIDevice currentDevice].userInterfaceIdiom which you can call directly. If you don't care about versions of iOS prior to 3.2, you don't need the macro and it makes your code clearer to not use it and make the call directly. – Cliff Ribaudo Apr 28 '15 at 10:37
  • Ahh I see thanks. I have a question. Is it more optimized to call it directly instead of the macro? Thank you! – KarenAnne Apr 29 '15 at 04:13
  • 2
    Optimized... not really. Clearer about what you are actually using, yes. – Cliff Ribaudo Apr 29 '15 at 14:45
  • 2
    UI_USER_INTERFACE_IDIOM() is deprecated in IOS 13 use 2nd option. – Mihir Oza Nov 19 '19 at 13:50
17

I like my isPad() function. Same code but keep it out of sight and in only one place.

Community
  • 1
  • 1
progrmr
  • 75,956
  • 16
  • 112
  • 147
15

My solution (works on 3.2+):

#define IS_IPHONE (!IS_IPAD)
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone)

then,

if (IS_IPAD)
    // do something

or

if (IS_IPHONE)
    // do something else
lewisanderson
  • 231
  • 2
  • 7
  • this is nice one and we can #define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) #define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) this is also we can do right...thanks again lewisanderson – Bala Oct 11 '13 at 05:44
  • 1
    This isn't the best logic to use, because an iPod should really be grouped with an iPhone in most situations, not an iPad. – Nate Nov 30 '14 at 03:32
5

In Swift use userInterfaceIdiom instance property as-

if UIDevice.current.userInterfaceIdiom == .phone {
     print("iPhone")
 }

& For other devices -

  switch UIDevice.current.userInterfaceIdiom {
    case .pad:
        print("iPad")
    case .phone:
        print("iPhone")
    case .tv:
        print("TV")
    case .carPlay:
        print("carPlay")
    default: break;
  }
Jack
  • 13,571
  • 6
  • 76
  • 98
1
extension UIDevice {
   
   static var isIPad: Bool {
      return UIDevice.current.userInterfaceIdiom == .pad
   }
}

Usage:

if UIDevice.isIPad {
    // Perform iPad related logic here
}
Suhit Patil
  • 11,748
  • 3
  • 50
  • 60
  • wouldn't it make more sense to either a) make this a static var (since you're not really accessing self, but rather whatever instance is .current) or b) replace UIDevice.current with self? – Travis Griggs Apr 13 '21 at 15:44
0

If 1- you already have the app installed into your device, 2- you change its build settings to be a 'Universal' app, 3- install the app to your device on top of the pre-existing app (without deleting the previous one)

You might find that the solutions provided here to detect iPhone/iPad do not work. First, delete the app that was 'only' for iPad/iPhone and install it fresh to your device.

Topsakal
  • 447
  • 5
  • 11
0
BOOL isIpad()
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        return YES;
    }
    return NO;
}
andyqee
  • 3,175
  • 3
  • 21
  • 24
0

Put this method in your App Delegate so that you can call it anywhere using [[[UIApplication sharedApplication] delegate] isPad]

-(BOOL)isPad
{
    BOOL isPad;
    NSRange range = [[[UIDevice currentDevice] model] rangeOfString:@"iPad"];
    if(range.location==NSNotFound)
    {
        isPad=NO;


    }
    else {
        isPad=YES;
    }

    return isPad;
}
Sanniv
  • 1,892
  • 1
  • 17
  • 21
  • 1st: The `UI_USER_INTERFACE_IDIOM()` method is the way to go. 2nd: your devices model won't change, so save yourself some cpu cycles and cache the result in a static variable and use `dispatch_once`. – stigi Sep 05 '13 at 08:55
  • Putting a new function somewhere in your application makes all of the callers dependent on whatever class you put that in. Simply calling one of the API functions seems better. – Pat Jun 06 '14 at 21:56
0

If you are using features that are not backwards compatible, I found the best way for me is to create a #define in the pre-compiled header. Example:

#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_3_2
#define USING_4_X
#endif

Then in your code, you can do this:

BOOL exists = NO;
#ifdef USING_4_X        
exists = [SomeObject someMethod:[url lastPathComponent]];
#else
exists = [SomeObject someMethod:[[url path] lastPathComponent]];
#endif
Damon
  • 1
  • 1