不管是在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:%@ version: %@ ,shortVersionString:%@ ", 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