不管是在Android開發還是iOS開發過程中,有時候我們需要經常根據設備的一些狀態或信息進行不同的設置和性能配置,例如橫豎屏切換時,電池電量低時,內存不夠時,網絡切換時等等,我們在這時候需要進行一些友好的提示和保護設備的一些設置。在Android開發中我們可以通過DeviceUtil這個工具類來獲取設備網絡狀態、電池電量等各種狀態信息,那么在iOS開發中,我們是否也能獲取到設備的各種狀態信息呢?答案是肯定的,本文就主要來學習一下在iOS開發過程中如何獲取到設備的各種狀態信息。
在iOS中要獲取設備的狀態信息,主要涉及到三個類:UIDevice、NSbundle和NSlocale。這三個類分別對應不同的信息:
- UIDevice是設計到設備的狀態信息最多最常用的一個類,主要用於獲取類函數及狀態通知,可以檢測手機電量、定位、感應、機型、當前系統版本等等。
- NSbundle是一個目錄,其中包含了程序會使用到的資源,這些資源包含了圖像、聲音、編譯好的代碼,通過這些亦可獲取一些應用信息。
- NsLocale可以獲取用戶的本地化信息,如貨幣、語言、國家、數字、日期格式、地理位置顯示等等。
一 UIDevice
通過UIDevice可以獲取到的信息非常多,UIDevice類展示了一些關鍵的特定於設備的屬性,包括使用的iPhone ,Ipad或iPod Touch型號、設備名稱、以及OS名稱和版本。他是一種一站式解決方案,用於提取出某些系統詳細信息。每個方法都是一個實例方法,他們是使用UIDevice單例通過[UIDevice currentDevice]調用的。
1.1 通過UIDevice獲取設備基本狀態
對於通過UIDevice獲取到的設備狀態信息如下圖所示,具體獲取方法參見:史上最全的iOS各種設備信息獲取總結(iPhone8/iPhone X 已更新)
1.2 UIDevice中對狀態信息的監控
UIDevice中對設備的方向、電池狀態、電量以及距離傳感器等信息都能進行獲取,有時候我們需要對相應的狀態進行監控,以便在狀態發生改變時我們采取相應的措施。要對一些狀態進行監控,顯然最好的辦法就是通過通知的方法進行操作,在狀態變化時發出通知,然后我們采取對應的方法。下面是UIDevice中提供的通知類型。
//設備方向改變時發送的通知 UIKIT_EXTERN NSString *const UIDeviceOrientationDidChangeNotification; //電池狀態改變時發送的通知 UIKIT_EXTERN NSString *const UIDeviceBatteryStateDidChangeNotification NS_AVAILABLE_IOS(3_0); //電量改變時發送的通知 UIKIT_EXTERN NSString *const UIDeviceBatteryLevelDidChangeNotification NS_AVAILABLE_IOS(3_0); //距離傳感器狀態改變時發送的通知 UIKIT_EXTERN NSString *const UIDeviceProximityStateDidChangeNotification NS_AVAILABLE_IOS(3_0);
其中,設備方向的枚舉選項有以下幾種:
typedef NS_ENUM(NSInteger, UIDeviceOrientation) { UIDeviceOrientationUnknown, UIDeviceOrientationPortrait, // home鍵在下 UIDeviceOrientationPortraitUpsideDown, // home鍵在上 UIDeviceOrientationLandscapeLeft, // home鍵在右 UIDeviceOrientationLandscapeRight, // home鍵在左 UIDeviceOrientationFaceUp, // 屏幕朝上 UIDeviceOrientationFaceDown // 屏幕朝下 };
電池狀態的枚舉選項有一下幾種:
typedef NS_ENUM(NSInteger, UIDeviceBatteryState) { UIDeviceBatteryStateUnknown, UIDeviceBatteryStateUnplugged, // 放電狀態 UIDeviceBatteryStateCharging, // 充電未充滿狀態 UIDeviceBatteryStateFull, // 充電已充滿 };
電池電量是一個float類型的值,從0.0 ~ 1.0表示電池電量的百分比:
@property(nonatomic, readonly) float batteryLevel; //電池電量
距離傳感器的狀態是用一個BOOL類型的值來表示手機是離用戶近還是遠:
//A Boolean value indicating whether the proximity sensor is close to the user (YES) or not (NO) @property(nonatomic, readonly) BOOL proximityState;
當我們需要對某一個信息進行監控時,我們需要進行三步:添加狀態通知--> 開啟監控開關 --> 完成監控動作(調用方法)。
- 添加狀態通知:即將某種狀態的監控信息添加到通知中心。
//添加設備方向的監控通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(change) name:UIDeviceOrientationDidChangeNotification object:nil]; //添加距離狀態的監控通知 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(notice) name:UIDeviceProximityStateDidChangeNotification object:nil];
- 開啟監控開關:iOS開發中,UIDevice的每一個狀態通知都對應有一個開關來控制是否開啟對應的監控和通知,我們需要打開對應狀態的開關。
//打開設備方向監測,這是用方法控制 [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; //不需要時可以關閉設備方向監控 [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; //打開電池狀態和電池電量監測開關,不需要時可以關閉 [UIDevice currentDevice].batteryMonitoringEnabled=YES; //打開手機距離傳感器監測開關,不需要時可以關閉 [UIDevice currentDevice].proximityMonitoringEnabled=YES;
此外,對於設備方向監控, UIDevice還提供了一個BOOL類型的只讀屬性來獲取當前監控狀態:
//A Boolean value that indicates whether the receiver generates orientation notifications (YES) or not (NO) @property(nonatomic, readonly, getter=isGeneratingDeviceOrientationNotifications) BOOL generatesDeviceOrientationNotifications;
- 完成監控動作:就是所監控的狀態發生變化時采取的動作,也就是在第一步添加通知時的 selector:@selector(change) 中方法的完成,這樣當監控的狀態發生變化是就會自動調用對用的方法執行。
//設備方向改變時調用該方法 -(void)change{ NSLog(@"change"); } //設備離用戶的距離狀態發生變化時調用該方法 -(void)notice{ if ([UIDevice currentDevice].proximityState) { NSLog(@"近距離"); }else{ NSLog(@"遠距離"); } }
二 設備上安裝的App信息
在開發過程中,有時候我們需要了解設備上安裝了那些App,以及是否安裝了一些特定的App以方便我們進行開發,最近我們公司的OA項目中就需要將特定類型的OA信息可以轉發到微信、QQ上,這時候我們就需要判斷設備上是否安裝了對應的App,然后進行相關的操作。因此,在這種情況下,獲取設備上是否安裝了特定的App以及設備上安裝了那些App則顯得比較重要了。
那么如何判斷我們的iOS設備上是否安裝了特定的App呢?有兩種方案:
- 直接判斷是否安裝了特定的App
- 先獲取到iOS設備上安裝的所有App的清單,然后判斷是否有特定的App
2.1 直接判斷iOS設備是否安裝了特定的app
這個方法其實是比較簡單的,但是你需要知道該軟件的URL Schemes,知道軟件的URL Schemes可以使用openUrl來獲取ios是否安裝了某款軟件,比如這樣 [[UIApplication sharedApplication] canOpenURL:@"damon://"] ,會返回一個bool值,為true則安裝了,否則沒有安裝。擴展iOS軟件之間的調用:IOS的軟件之間的調用(URL Schemes)
方法很簡單,但是問題了,我們要如何獲取到特定App的URL Schemes呢?下面兩個步驟帶大家一起學會如何獲取:
- 獲取app的url schemes 的方法 :把相應的 app 的 ipa 安裝文件下載下來,把文件 .ipa 的后綴改成 .zip,然后解壓,打開 Payload/xxx.app/Info.plist 這個文件,找到 URL types 下的 URL Schemes 下的數組對應的值就是這個 app 的 URL Scheme 了
- 簡單驗證一個 URL Scheme 是否正確的方法:在真機設備(此設備要安裝了待驗證的 app)里面打開 Safari,然后在地址欄中鍵入該應用的 URL Scheme,后加 ://,比如 Weico 的,在地址欄中鍵入 weico:// ,然后點擊確定,如果能正常調用出 Weico,即代表這個 URL Scheme 正確可用
- 部分URL schemes名單:https://www.zhihu.com/question/19907735,僅做參考,這個不保證正確性
我們判斷某個特定的App是否被安裝一般都是用來進行跳轉或打開的,如果安裝了,我們該如何實現跳轉呢?首先,我們需要將檢測的UrlScheme添加到白名單即可,添加方法:info.plist 增加LSApplicationQueriesSchemes (array類型),把要檢測的app的UrlScheme加進去。
最后,我們在代碼中調用Application 的canopenUrl 的方法判斷設備時候有對應的應用 程序,返回YES表示已安裝了該app ,代碼如下:
if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"IOSDevApp://"]]) {
//說明此設備有安裝app }else{ //說明此設備沒有安裝app }
2.2 獲取iOS設備上安裝的所有App清單
要獲取iOS設備上安裝所有App清單以及一些必要信息,實際上這一個是不允許的,因為涉及到個人隱私問題。但是我們可以通過反射的方法開進行獲取。一般的方法是:
-(void)getAllApp { Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)]; NSArray *allApplications = [workspace performSelector:@selector(allApplications)];//這樣就能獲取到手機中安裝的所有App NSLog(@"設備上安裝的所有app:%@",allApplications); }
打印出來的結果類似如下形式:
設備上安裝的所有app:( "<LSApplicationProxy: 0x12e563310> com.apple.Passbook <file:///Applications/Passbook.app>", "<LSApplicationProxy: 0x12e563bb0> com.apple.GameController <file:///Applications/GameController.app>", )
從打印結果看出,我們上一步所獲取到的allApplications數組中的元素是一個LSApplicationProxy類型的,我們要把這個轉換成字符串, 這個字符串中不只包含了App的bundle ID,還包含了安裝的路徑,因為這個路徑不唯一不確定,所以我們判斷是否安裝了某個App只需判斷這個字符串中的bundle ID,所有我們還要對字符串做處理。前提是要知道你要判斷的這個app的Bundle ID 是什么。
//解析數據,獲取每一個app信息總的 Class LSApplicationProxy_class = object_getClass(@"LSApplicationProxy"); for (LSApplicationProxy_class in appList) { //這里可以查看一些信息 NSString *bundleID = [LSApplicationProxy_class performSelector:@selector(applicationIdentifier)]; NSString *version = [LSApplicationProxy_class performSelector:@selector(bundleVersion)]; NSString *shortVersionString = [LSApplicationProxy_class performSelector:@selector(shortVersionString)]; NSLog(@"bundleID:%@\n version: %@\n ,shortVersionString:%@\n", bundleID,version,shortVersionString); }
從所有安裝App信息中判斷是否安裝了特定App的方法:
-(void)isInstallLDApp:(NSArray *)allAPP { NSInteger zlConnt = 0; for (NSString *appStr in allAPP) { NSString *app = [NSString stringWithFormat:@"%@",appStr];//轉換成字符串 NSRange range = [app rangeOfString:@"LdWBrowserIPhone"];//是否包含這個bundle ID if (range.length > 1){ zlConnt ++; } } if (zlConnt >= 1) { NSLog(@"已安裝"); }else{ NSLog(@"沒有安裝"); } }
2.3 總結
- 方法一的優點:效率高,代碼量小 ,但前提是要先知道要判斷的app 的UrlSchemes
- 方法二的優點:完美解決iOS9的canopenurl 白名單的限制;
缺點: 遍歷的過程中可能會消耗性能, App Store審核可能會被拒,前提是要知道你要判斷的這個app的Bundle ID 是什么
三 NSbundle和NSlocale
bundle是一個目錄,其中包含了程序會使用到的資源. 這些資源包含了如圖像,聲音,編譯好的代碼,nib文件(用戶也會把bundle稱為plug-in). 對應bundle,cocoa提供了類NSBundle.一個應用程序看上去和其他文件沒有什么區別. 但是實際上它是一個包含了nib文件,編譯代碼,以及其他資源的目錄. 我們把這個目錄叫做程序的main bundle。通過這個路徑可以獲取到應用的信息,例如應用名、版本號等。通過NSBundle可以獲得這幾個信息:
//app應用相關信息的獲取 NSDictionary *dicInfo = [[NSBundle mainBundle] infoDictionary]; // CFShow(dicInfo); NSString *strAppName = [dicInfo objectForKey:@"CFBundleDisplayName"]; NSLog(@"App應用名稱:%@", strAppName); NSString *strAppVersion = [dicInfo objectForKey:@"CFBundleShortVersionString"]; NSLog(@"App應用版本:%@", strAppVersion); NSString *strAppBuild = [dicInfo objectForKey:@"CFBundleVersion"]; NSLog(@"App應用Build版本:%@", strAppBuild);
NSLocale可以獲取用戶的本地化信息設置,例如貨幣類型,國家,語言,數字,日期格式的格式化,提供正確的地理位置顯示等等。下面的代碼獲取機器當前語言和國家代碼。通過NSLocale可以這樣使用:
NSArray *languageArray = [NSLocale preferredLanguages]; NSString *language = [languageArray objectAtIndex:0]; NSLog(@"語言:%@", language);//en NSLocale *locale = [NSLocale currentLocale]; NSString *country = [locale localeIdentifier]; NSLog(@"國家:%@", country); //en_US