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
}