1. 程式人生 > >iOS 判斷系統版本

iOS 判斷系統版本

方案一

double systemVersion = [UIDevice currentDevice].systemVersion.boolValue;

    if (systemVersion >= 7.0) {
        // >= iOS 7.0
    } else {
        // < iOS 7.0
    }

    if (systemVersion >= 10.0) {
        // >= iOS 10.0
    } else {
        // < iOS 10.0
    }

如果只是大致判斷是哪個系統版本,上面的方法是可行的,如果具體到某個版本,如 10.0.1,那就會有偏差。我們知道systemVersion依舊是10.0。

方案二

    NSString *systemVersion = [UIDevice currentDevice].systemVersion;
    NSComparisonResult comparisonResult = [systemVersion compare:@"10.0.1" options:NSNumericSearch];

    if (comparisonResult == NSOrderedAscending) {
        // < iOS 10.0.1
    } else if (comparisonResult == NSOrderedSame) {
        // = iOS 10.0.1
} else if (comparisonResult == NSOrderedDescending) { // > iOS 10.0.1 } 或者 if (comparisonResult != NSOrderedAscending) { // >= iOS 10.0.1 } else { // < iOS 10.0.1 }

有篇部落格提到,這種方法不靠譜。比如系統版本是10.1.1,而我們提供的版本是8.2,會返回NSOrderedAscending,即認為10.1.1 < 8.2 。
其實,用這樣的比較方式NSComparisonResult comparisonResult = [systemVersion compare:@"10.0.1"]

,的確會出現這種情況,因為預設是每個字元逐個比較,即1(0.1.1) < 8(.2),結果可想而知。但我是用NSNumericSearch方式比較的,即數值的比較,不是字元比較,也不需要轉化成NSValue(NSNumber)再去比較。

方案三

if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_7_0) {
    // >= iOS 7.0
} else {
    // < iOS 7.0
}

或者:

if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
    // >= iOS 7.0
} else {
    // < iOS 7.0
}

這些巨集定義是Apple預先定義好的,如下:

#if TARGET_OS_IPHONE
...
#define NSFoundationVersionNumber_iOS_9_4 1280.25
#define NSFoundationVersionNumber_iOS_9_x_Max 1299
#endif

細心的童靴可能已經發現問題了。《驚! Apple沒有提供iOS 10 以後的巨集?》。那我們要判斷iOS10.0以後的版本該怎麼做呢?
有篇部落格中提到,iOS10.0以後版本號提供了,並且逐次降低了,並提供了依據。

#if TARGET_OS_MAC
#define NSFoundationVersionNumber10_1_1 425.00
#define NSFoundationVersionNumber10_1_2 425.00
#define NSFoundationVersionNumber10_1_3 425.00
#define NSFoundationVersionNumber10_1_4 425.00
...
#endif

我想這位童鞋可能沒仔細看, 這兩組巨集是分別針對iPhone和macOS的,不能混為一談的。

所以也只能像下面的方式來大致判斷iOS 10.0, 但之前的iOS版本是可以準確判斷的。

    if (NSFoundationVersionNumber > floor(NSFoundationVersionNumber_iOS_9_x_Max)) {
        // > iOS 10.0
    } else {
        // <= iOS 10.0
    }

方案四

在iOS8.0中,Apple也提供了NSProcessInfo 這個類來檢測版本問題。

@property (readonly) NSOperatingSystemVersion operatingSystemVersion NS_AVAILABLE(10_10, 8_0);
- (BOOL) isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version NS_AVAILABLE(10_10, 8_0);

所以可以這樣檢測:

if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 8, .minorVersion = 3, .patchVersion = 0}]) {
     // >= iOS 8.3
} else {
     // < iOS 8.3
}

用來判斷iOS 10.0以上的各個版本也是沒有問題的,唯一的缺點就是不能準確版本是哪個版本,當然這種情況很少。如果是這種情況,可以通過字串的比較判斷。

方案五

通過判斷某種特定的類有沒有被定義,或者類能不能響應哪個特定版本才有的方法。
比如,UIAlertController 是在iOS 8.0才被引進來的一個類,我們這個依據來判斷版本

    if (NSClassFromString(@"UIAlertController")) {
        // >= iOS 8.0
    } else {
        // < iOS 8.0
    }

說到這裡,就順便提一下在編譯期間如何進行版本控制,依然用UIAlertController 來說明。

NS_CLASS_AVAILABLE_IOS(8_0) @interface UIAlertController : UIViewController

NS_CLASS_AVAILABLE_IOS(8_0) 這個巨集說明,UIAlertController 是在iOS8.0才被引進來的API,那如果我們在iOS7.0上使用,應用程式就會掛掉,那麼如何在iOS8.0及以後的版本使用UIAlertController ,而在iOS8.0以前的版本中仍然使用UIAlertView 呢?

這裡我們會介紹一下在#import <AvailabilityInternal.h> 中的兩個巨集定義:

  • __IPHONE_OS_VERSION_MIN_REQUIRED

  • __IPHONE_OS_VERSION_MAX_ALLOWED

從字面意思就可以直到,__IPHONE_OS_VERSION_MIN_REQUIRED 表示iPhone支援最低的版本系統,__IPHONE_OS_VERSION_MAX_ALLOWED 表示iPhone允許最高的系統版本。

__IPHONE_OS_VERSION_MAX_ALLOWED 的取值來自iOS SDK的版本,比如我現在使用的是Xcode Version 8.2.1(8C1002),SDK版本是iOS 10.2,怎麼看Xcode裡SDK的iOS版本呢?

進入PROJECT,選擇Build Setting,在Architectures中的Base SDK中可以檢視當前的iOS SDK版本。

列印這個巨集,可以看到它一直輸出100200。

__IPHONE_OS_VERSION_MIN_REQUIRED 的取值來自專案TARGETS的Deployment Target,即APP願意支援的最低版本。如果我們修改它為8.2,列印這個巨集,會發現輸出80200,預設為10.2。

通常,__IPHONE_OS_VERSION_MAX_ALLOWED 可以代表當前的SDK的版本,用來判斷當前版本是否開始支援或具有某些功能。而__IPHONE_OS_VERSION_MIN_REQUIRED 則是當前SDK支援的最低版本,用來判斷當前版本是否仍然支援或具有某些功能。

回到UIAlertController 使用的問題,我們就可以使用這些巨集,新增版本檢測判斷,從而使我們的程式碼更健壯。

 - (void)showAlertView {
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Title" message:@"message" delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
    [alertView show];
#else
    if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_8_0) {
        UIAlertController *alertViewController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];

        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
        UIAlertAction *otherAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];

        [alertViewController addAction:cancelAction];
        [alertViewController addAction:otherAction];

        [self presentViewController:alertViewController animated:YES completion:NULL];
    }
#endif
}

方案六

iOS 11.0 以後,Apple加入了新的API,以後我們就可以像在Swift中的那樣,很方便的判斷系統版本了。

if (@available(iOS 11.0, *)) {
    // iOS 11.0 及以後的版本
} else {
    // iOS 11.0 之前
}

參考連結: