I had to dig around quite a bit but found the answer (I'd like to shout out to user6788419 whose right answer was berried deep in another thread).
First of all, the above code is correct. Checking if the safe area's bottom inset is larger than zero will accurately identify iPhones with a notch (as of this writing). So, this is correct:
if (@available( iOS 11.0, * )) {
if ([[[UIApplication sharedApplication] keyWindow] safeAreaInsets].bottom > 0) {
// iPhone with notch
}
}
However, it matters where you put the above statement in your code because UIWindow is not available until the first run loop has concluded. That means, if you check the notch in your viewDidLoad or before it concluded (in your init for example), the bottom inset will always be zero.
If you, similarly to me, need this check to setup your main view, you can just move all your setup into separate function (such as postViewDidLoad) and call it once viewDidLoad has concluded:
[self performSelector:@selector(postViewDidLoad) withObject:nil afterDelay:0.0f];
Or, alternatively, you enclose it:
dispatch_async(dispatch_get_main_queue(), ^{
// Check notch here
});
UPDATE: code for iOS 13 and up (does the same thing). "areaPosition" is a string argument when you call the function. "top" checks for the notch, everything else checks for the presence of the bottom home indicator.
- (UIWindow *) keyWindow {
UIWindow *foundWindow = nil;
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
foundWindow = window;
break;
}
}
return foundWindow;
}
- (BOOL) checkSafeArea:(NSString *)areaPosition {
if (@available(iOS 13.0, *)) {
if ([areaPosition isEqualToString:@"top"]) {
return [self keyWindow].safeAreaInsets.top > 20.0f;
} else {
return [self keyWindow].safeAreaInsets.bottom > 0.0f;
}
} else {
if ([areaPosition isEqualToString:@"top"]) {
return [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.top > 20.0f;
} else {
return [[[UIApplication sharedApplication] delegate] window].safeAreaInsets.bottom > 0.0f;
}
}
return NO;
}