15

I am trying to find out PPI(Pixels Per Inch) in iOS.

I couldn't find any direct way to query this like we do for display size

UIScreen.mainScreen().bounds

There is a way to do it by multiplying scale with standard generic PPI for iPhone(163) or iPad(132) but it's not accurate.

If the formula is right then PPI of iPhone 6 plus is 489 but in reality the PPI is 401 Here is the reference

For now it seems like hardcoding is the way to go.

But I'd like to do it programmatically using a formula.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Durai Amuthan.H
  • 31,670
  • 10
  • 160
  • 241

4 Answers4

9

I have just ported and updated one of my old ObjC libraries to Swift. You can use it or take parts of the code you need. Get it here: https://github.com/marchv/UIScreenExtension.

The library uses UIScreen.main.nativeScale to convert from Pixels Per Inch (PPI) to Points Per Inch.

Install the library using Cocoapods and then import it:

import UIScreenExtension

And then make use of it:

if let pointsPerCentimeter = UIScreen.pointsPerCentimeter {
   // code
}
Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35
  • 6
    It is worth mentioning that UIScreenExtension misreports PPI for iPad 2 and iPad Mini 1st generation. These should be 132 and 163 respectively as those two devices were not @2x retina. – step_jac Nov 23 '17 at 20:37
  • 2
    @step_jac I first misunderstood you comment - better get some coffee before reading early in the morning :D OK I can see the problem now - will fix it ASAP - thanks for pointing it out – Jens Schwarzer Nov 24 '17 at 07:21
3

I believe there is no public API to get either PPI or physical size of a screen.

The only way is to hardcode list of devices with their physical sizes and/or PPI's (and you can get a device type out of UIDevice class).

BTW. Here is the question which is pretty much ask the same thing (different way): How do ruler apps stay accurate on all devices?

Community
  • 1
  • 1
Victor Ronin
  • 22,758
  • 18
  • 92
  • 184
  • I guess we can the physical size of screen in points `[UIScreen mainScreen].bounds.size` but how many pixels per point can be found out for all the devices except iPhone 6 plus because it can't be found out like normal PPI * scale because iPhone 6 plus & iPhone 6s plus has introduced new Screen called Retina HD...so I think the best way would be check if the device is iPhone 6 plus then return 401 and for the rest of the devices we can go ahead with the formula I guess... – Durai Amuthan.H Apr 24 '16 at 14:07
  • A way to detect iPhone 6 & iPhone 6s programatically (http://stackoverflow.com/a/26400686/730807) – Durai Amuthan.H Apr 24 '16 at 14:08
1

Having searched for an answer for the same problem, it became clear that there is no simple answer, and as previously stated, this needs to be hardcoded for devices. The most informative website with device sizes and PPI that I have found is at https://www.ios-resolution.com.

Laying this out in a spreadsheet, it became clear that device PPI can be determined from screen point and pixel sizes. It's not perfect but it works for all devices I have tested except iPad Minis (but I didn't need them included for my app).

    // MARK: Screen Points Per Inch (PoPI)
// ----------------------------------------------------------------------------------------------------------------------------------
func screenPtsPerInch() -> Double {
    
    // Screen points per inch are derived from https://www.ios-resolution.com
    // For Pixels per Inch, multiply PoPI by UIScreen().scale
            
    // Almost all of the current iPads are simply 132.0 popi
    // iPad mini's are 163.0 but I have not found a way to differentiate them!
    
    // iPad
    guard UIDevice.current.userInterfaceIdiom == .phone else { return 132.0 }
    
    // iPhone
    guard let screen = self.window?.windowScene?.screen else { return 152.67 }
    let points       = screen.bounds
    let pixels       = screen.nativeBounds

    // Pixel width across screen (doesn't change with device rotation)
    let pixelWidth   = pixels.width
    
    // Point width and height across screen
    var screenWidth  = points.width
    var screenHeight = points.height
    if screenWidth > screenHeight {     // Points change with screen rotation
        screenWidth  = points.height
        screenHeight = points.width
    }

    print("Pixel  Width:  \(pixelWidth)")
    print("Screen Width:  \(screenWidth)")
    print("Screen Height: \(screenHeight)")
    
    if pixelWidth < 1100 {
        switch (screenWidth, screenHeight) {
            case (320.0, 480.0):
                return 163.0
            case (320.0, 568.0):
                return 163.0
            case (375.0, 667.0):
                return 163.0
            case (375.0, 812.0):
                return 158.67
            case (414.0, 736.0):
                return 133.67
            default:
                return 163.0
        }
    } else {
        switch (screenWidth, screenHeight) {
            case (375.0, 812.0):
                return 152.67
            case (390.0, 844.0):
                return 153.33
            case (393.0, 852.0):
                return 153.33
            case (414.0, 896.0):
                return 152.67
            case (428.0, 926.0):
                return 152.67
            case (430.0, 932.0):
                return 153.33
            default:
                return 152.67
        }
    }
}

This function returns points per inch. I you want pixels per inch then multiply these by the device scale factor.

    // MARK: Screen Device Scale (pixels/point)
// ----------------------------------------------------------------------------------------------------------------------------------
func screenScale() -> Double {
    guard let screen = self.window?.windowScene?.screen else { return 2.0 }
    print("Native Scale:  \(screen.nativeScale)")
    return screen.nativeScale
}
Golompse
  • 81
  • 2
  • 5
0

iPhone Plus has scale 3, but nativeScale is 2.6.

UIKit samples this content down to fit the actual screen dimensions. Metal or OpenGL ES contents should be rendered at the precise dimensions.

int screenPPI() {
    return [[UIScreen mainScreen] nativeScale] * ((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 132 : 163);
}
Evgeny Karpov
  • 2,386
  • 26
  • 16