iOS中检测硬件和传感器
首先要知道,你需要查看所需的硬件或传感器是否存在,而不是假设设备有哪些功能。举个例子,你不能假设只有iPhone才有麦克风,而应该使用API来查看麦克风是否存在。下面这段代码的第一个优势在于,它能自动兼容将来推出的新设备和外接麦克风。
第二个优势呢?这段代码只有一行。
检查麦克风可用性的正确方法
1 2 3 4 |
- (BOOL) microphoneAvailable { AVAudioSession *session = [AVAudioSession sharedInstance]; return session.inputIsAvailable; } |
对于麦克风,你还需要检测输入设备变化的提醒。也就是当用户插入麦克风时,除了在viewDidAppear
中做相应的修改外,还要激活UI上的Record按钮。听起来挺酷,不是吗?下面就是具体的实现方法。
检查是否插入麦克风
1 2 3 4 5 6 7 8 9 10 11 12 |
void audioInputPropertyListener(void* inClientData, AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData) { UInt32 isAvailable = *(UInt32*)inData; BOOL micAvailable = (isAvailable > 0); //加入更新UI的代码 } - (void)viewDidLoad { [super viewDidLoad]; AudioSessionAddPropertyListener( kAudioSessionProperty_AudioInputAvailable, audioInputPropertyListener, nil); } |
这里,你要做的就是为kAudioSessionProperty_AudioInputAvailable
增加一个属性监听器,然后在回调中检查它的值。
只要增加很少几行代码,就能够写出正确的设备检测代码了。下一步,你需要扩展这段代码,从而支持其他的硬件和传感器。
使用AudioSessionPropertyListeners
与观察NSNotification
事件很像。当你向一个类中增加一个属性监听器时,需要负责在适当的时候移除它。在前面这个例子中,由于在viewDidLoad
中增加了属性监听器,所以需要在didReceiveMemoryWarning
方法中移除它。
1. 检测摄像头类型
iPhone最初只有一个摄像头,后来在iPhone 4中增加了一个前置摄像头。iPod touch直到第四代才有摄像头。iPhone 4有前置摄像头,iPad 1(比iPhone 4大)却没有摄像头,而后来的iPad 2同时有了前置和后置摄像头。所有这些都意味着你不应该在假设设备功能的前提下编写代码。实际上使用API更方便。
UIImagePickerController
类含有检测源类型可用性的类方法。
检测是否存在摄像头
1 2 3 4 |
- (BOOL) cameraAvailable { return [UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]; } |
检测是否存在前置摄像头
1 2 3 4 5 6 7 8 9 |
- (BOOL) frontCameraAvailable { #ifdef __IPHONE_4_0 return [UIImagePickerController isCameraDeviceAvailable: UIImagePickerControllerCameraDeviceFront]; #else return NO; #endif } |
检测前置摄像头,需要运行在iOS 4或更高版本中。枚举类型UIImagePickerControllerCameraDeviceFront
只在iOS 4及更高版本中可用,因为所有带有前置摄像头的设备(iPhone 4和iPad 2)使用的都是iOS 4及更高版本。所以你用到了宏,如果设备使用的是iOS 3或更低版本就返回NO
。
类似地,可以检查设备上的摄像头是否具备视频录制功能。iPhone 3GS及更新设备的摄像头支持录制视频。你可以使用以下代码来检查。
检测摄像头是否支持视频录制
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (BOOL) videoCameraAvailable { UIImagePickerController *picker = [[UIImagePickerController alloc] init]; // 首次调用前面的方法,检查是否存在摄像头 if(![self cameraAvailable]) return NO; NSArray *sourceTypes = [UIImagePickerController availableMediaTypesForSourceType: UIImagePickerControllerSourceTypeCamera]; if (![sourceTypes containsObject:(NSString *)kUTTypeMovie]){ return NO; } return YES; } |
这段代码会枚举给定摄像头的可用媒体类型,然后判断它是否包含kUTTypeMovie
。
2. 检测照片库是否为空
如果你在使用摄像头,几乎总会用到照片库。在调用UIImagePicker
显示用户相册前,需要确保它里面有照片。可以用检查摄像头是否存在的方法来查看相册是否为空,只要将UIImagePickerControllerSourceTypePhotoLibrary
或UIImagePickerControllerSourceTypeSavedPhotosAlbum
作为源类型传过去就行了。
3. 检测摄像头闪光灯是否存在
到目前为止,带有摄像头闪光灯的唯一设备是iPhone 4^〔1〕^。在未来几年里,将会有越来越多的设备带有摄像头闪光灯。使用UIImagePickerController
的类方法来检查摄像头闪光灯是否存在很容易。
^〔1〕^ 翻译本书时,iPhone 4S和iPhone 5均已发布,它们也带有摄像头闪光灯。——译者注
检测摄像头闪光灯是否存在
1 2 3 4 5 6 7 8 |
- (BOOL) cameraFlashAvailable { #ifdef __IPHONE_4_0 return [UIImagePickerController isFlashAvailableForCameraDevice: UIImagePickerControllerCameraDeviceRear]; #else return NO; #endif } |
4. 检测陀螺仪是否存在
陀螺仪是iPhone 4上新增的一个有意思的传感器。iPhone 4之后发布的设备,包括new iPad和iPhone 5,都有陀螺仪。陀螺仪用于测量设备物理位置的相对变化。相比之下,加速计只能测量力的大小,而不能测量扭动。有了陀螺仪,游戏开发者甚至可能实现六轴控制,类似于索尼PlayStation 3控制器或任天堂的Wii控制器提供的功能。你可以使用CoreMotion.framework
提供的API来检测陀螺仪是否存在。
检测陀螺仪是否存在
1 2 3 4 5 6 7 8 9 |
- (BOOL) gyroscopeAvailable { #ifdef __IPHONE_4_0 CMMotionManager *motionManager = [[CMMotionManager alloc] init]; BOOL gyroAvailable = motionManager.gyroAvailable; return gyroAvailable; #else return NO; #endif } |
如果陀螺仪是你的应用中一个重要功能,而你的目标设备上没有陀螺仪,那么必须用其他输入方法来设计应用。或者也可以在应用的info.plist中的UIRequiredDeviceCapablities
键中指定它们,防止没有陀螺仪的设备安装该应用。本章稍后会进一步介绍这个键。
5. 检测指南针或磁力计
指南针可用性可以使用CoreLocation.framework
中的CLLocationManager
类来检查。调用CLLocationManager
中的headingAvailable
方法,如果返回值为真,你就可以在应用中使用指南针。指南针在与位置有关的应用和用到了增强现实技术的应用中用处比较大。
6. 检测视网膜屏幕
作为iOS开发人员,你已经知道只要为应用中用到的每个资源增加一个@2x的图片文件,就可以满足视网膜屏幕(Retina Display)的需要。但如果要从远程服务器下载图片,采用视网膜屏幕的设备需要下载的图片分辨率为普通屏幕图片分辨率的两倍。
照片浏览器应用就是一个很好的例子,它类似于Flickr查看器或Instagram。当用户在iPhone 4、new iPad或iPhone 5上启动该应用时,下载的图片分辨率应该为非视网膜屏幕设备上图片分辨率的两倍。一些开发者选择忽略它,直接为所有设备下载高分辨率图片,但这有点浪费带宽,甚至通过EDGE下载时可能要慢得多。相反,你应该在判断出设备使用的是视网膜屏幕之后再下载高分辨率图片。这项检查很容易。
检查设备使用的是否是视网膜屏幕
1 2 3 4 5 6 7 8 |
- (BOOL) retinaDisplayCapable { int scale = 1.0; UIScreen *screen = [UIScreen mainScreen]; if([screen respondsToSelector:@selector(scale)]) scale = screen.scale; if(scale == 2.0f) return YES; else return NO; } |
在这段代码中,你会找到设备的mainScreen
,然后检查该设备是否可以显示适用于视网膜屏幕的高分辨率图片。这样,如果苹果推出了外接视网膜屏幕(可能是更新的苹果影院显示器),支持目前这一代iPad直接以视网膜模式输出,那么你的应用无需任何修改依然能工作。
7. 检测振动提醒功能
在写本书时,只有各个版本的iPhone具备振动提醒功能。很遗憾,没有公开的API用于检测设备是否支持振动功能。不过,AudioToolbox.framework
有两个方法用来选择性地振动不同版本的iPhone:
1 2 |
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); |
第一个方法会振动iPhone,而在iPod touch/iPad上则会发出“哔哔”的响声。第二个方法只会振动iPhone。在不支持振动的设备上,它什么都不做。如果你正在开发的一款游戏通过振动设备来提示危险,或是开发一款迷宫游戏,当玩家撞到墙时发出振动,那么应该用第二个方法。第一个方法用来提醒用户,包括振动和发出哔哔声,而第二个方法只能用来发出振动。
8. 检测远程控制功能
iOS应用可以处理按下外接耳机上的按钮触发的远程控制事件。处理这类事件,使用如下方法接收通知:
1 |
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; |
在firstResponder
中实现如下方法:
1 |
remoteControlReceivedWithEvent: |
调用如下方法,确保在不需要这些事件时关闭该功能:
1 |
[[UIApplication sharedApplication] endReceivingRemoteControlEvents]; |
9. 检测拨打电话功能
可以检查设备是否支持拨打电话,方法是查看它是否能打开tel:
类型的URL。UIApplication
类的canOpenURL:
方法可以很方便地检查设备上是否有能够处理特定类型URL的应用。在iPhone上,tel:
这类URL由iPhone上的电话应用来处理。该方法还可以用来检查能够处理给定URL的具体应用是否已安装到设备上。
检测拨打电话功能
1 2 3 4 |
- (BOOL) canMakePhoneCalls { return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"tel://"]]; } |
可用性小提示 在iPod touch上,开发者应该完全隐藏面向电话的功能。举个例子,如果你开发一个显示因特网上电话号码列表的黄页应用,应该只在能够拨打电话的设备上显示拨打电话的那个按钮。不要只是简单地禁用它(因为用户做什么都无法启用它)或是显示一个错误提醒。有先例说明,在iPod touch上显示一个“Not an iPhone”错误提醒会导致该应用无法通过苹果应用审核部门的审查。