APP开发避免不开系统权限的问题,今天做定位时需要在不允许定位的时候做一些操作,所以,今天就大概的了解了一些。

权限分类

升到iOS10之后,需要设置权限的有:
麦克风权限:Privacy - Microphone Usage Description 是否允许此App使用你的麦克风?
相机权限: Privacy - Camera Usage Description 是否允许此App使用你的相机?
相册权限: Privacy - Photo Library Usage Description 是否允许此App访问你的媒体资料库?
通讯录权限: Privacy - Contacts Usage Description 是否允许此App访问你的通讯录?
蓝牙权限:Privacy - Bluetooth Peripheral Usage Description 是否许允此App使用蓝牙?
语音转文字权限:Privacy - Speech Recognition Usage Description 是否允许此App使用语音识别?
日历权限:Privacy - Calendars Usage Description
定位权限:Privacy - Location When In Use Usage Description
定位权限: Privacy - Location Always Usage Description
位置权限:Privacy - Location Usage Description
媒体库权限:Privacy - Media Library Usage Description
健康分享权限:Privacy - Health Share Usage Description
健康更新权限:Privacy - Health Update Usage Description
运动使用权限:Privacy - Motion Usage Description
音乐权限:Privacy - Music Usage Description
提醒使用权限:Privacy - Reminders Usage Description
Siri使用权限:Privacy - Siri Usage Description
电视供应商使用权限:Privacy - TV Provider Usage Description
视频用户账号使用权限:Privacy - Video Subscriber Account Usage Description

【联网权限】

在iOS 10下 ,首次进入应用时,会有询问是否允许网络连接权限的的弹窗,为更好进行用户交互,需要在打开应用时获取应用禁用网络权限状态(状态分为:未知、限制网络、未限制网络),客户端根据不同的权限状态定制相应的人机交互。

针对请求应用网络权限可能存在的几种情形:

关闭 Restricted
无线局域网 NotRestricted
无线局域网&蜂窝 NotRestricted
不进行操作 Unknown

使用 CoreTelephony.framework 框架下的 CTCellularData 类中的方法和属性进行解决
需要先导入#import <CoreTelephony/CTCellularData.h>

应用启动后,检测应用中是否有联网权限
CTCellularData *cellularData = [[CTCellularData alloc]init];
cellularData.cellularDataRestrictionDidUpdateNotifier =  ^(CTCellularDataRestrictedState state){
  //获取联网状态
  switch (state) {
        case kCTCellularDataRestricted:
                  NSLog(@"Restricrted");          
                  break;
        case kCTCellularDataNotRestricted:
                  NSLog(@"Not Restricted");          
                  break;
        case kCTCellularDataRestrictedStateUnknown:
                  NSLog(@"Unknown");          
                  break;
        default:
            break;
  };
};
查询应用是否有联网功能
CTCellularData *cellularData = [[CTCellularData alloc]init];
CTCellularDataRestrictedState state = cellularData.restrictedState;
 switch (state) {
   case kCTCellularDataRestricted:
         NSLog(@"Restricrted");      
         break;
   case kCTCellularDataNotRestricted:
         NSLog(@"Not Restricted");      
         break;  
   case kCTCellularDataRestrictedStateUnknown:
         NSLog(@"Unknown");      
         break;
   default:
         break;
}

不幸的是,苹果这个功能可能出得太仓促,并没有给开发者提供相应的 API。所以,我们没办法检测到用户点击“允许”或“不允许”网络请求的回调,也没法检测到当前用户是否授权的状态。

【相册权限】

iOS8以后,第一次访问系统设备的一些属性时,比如相册,照相机,定位等,都会接收系统的权限访问,这个访问并不能使用代码跳过,而在系统的settings里面得隐私设置,可以设置某个程序对系统设备属性的访问权限,而我们能做的就是通过AVCaptureDevice来获取AVAuthorizationStatus属性,判断一下当前的权限,防止在不允许的情况下有Bug!

需要导入#import <Photos/Photos.h>这个头文件
这个方法判断是否允许使用相册的回调
 [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
  if (status == PHAuthorizationStatusAuthorized) {
      //允许使用相册
  }else{
  //不允许使用相册
 }
  }];
相册权限
PHAuthorizationStatus photoAuthorStatus = [PHPhotoLibrary authorizationStatus];
switch (photoAuthorStatus) {
  case PHAuthorizationStatusAuthorized:
      NSLog(@"允许授权");
      break;
  case PHAuthorizationStatusDenied:
      NSLog(@"不允许授权");
      break;
  case PHAuthorizationStatusNotDetermined:
      NSLog(@"不确定");
      break;
  case PHAuthorizationStatusRestricted:
      NSLog(@"限制");
      break;
  default:
      break;
}

在使用相册的时候,需要在plist文件中做一些设置


 

屏幕快照 2016-12-23 下午6.56.48.png

【相机和麦克风权限】

需要导入#import <AVFoundation/AVFoundation.h>
相机对应AVMediaTypeVideo
麦克风对应AVMediaTypeAudio

权限类型
typedef NS_ENUM(NSInteger, AVAuthorizationStatus) {
  AVAuthorizationStatusNotDetermined = 0,// 未询问用户是否授权
  AVAuthorizationStatusRestricted,// 未授权,例如家长控制
  AVAuthorizationStatusDenied,// 未授权,用户拒绝造成的
  AVAuthorizationStatusAuthorized// 已授权}
权限检测
AVAuthorizationStatus videoAuthStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  if (videoAuthStatus == AVAuthorizationStatusNotDetermined) {// 未询问用户是否授权
  }else if(videoAuthStatus == AVAuthorizationStatusRestricted || videoAuthStatus == AVAuthorizationStatusDenied) {// 未授权
  }else{// 已授权
  }
请求授权
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {

      if (granted){// 用户同意授权

      }else {// 用户拒绝授权

      }

}];

【定位权限】

导入类库

#import <CoreLocation/CLLocationManager.h>

一些枚举值

这里就要查看CLLocationManager的授权状态,此方法会返回当前授权状态:
[CLLocationManager authorizationStatus]

授权状态为枚举值:
kCLAuthorizationStatusNotDetermined                  //用户尚未对该应用程序作出选择
kCLAuthorizationStatusRestricted                     //应用程序的定位权限被限制 
kCLAuthorizationStatusAuthorizedAlways               //一直允许获取定位
kCLAuthorizationStatusAuthorizedWhenInUse            //在使用时允许获取定位
kCLAuthorizationStatusAuthorized                     //已废弃,相当于一直允许获取定位
kCLAuthorizationStatusDenied                         //拒绝获取定位

判断用户是否授权应用获取定位权限的完整代码:

if ([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorized)) {        

//定位功能可用

}else if ([CLLocationManager authorizationStatus] ==kCLAuthorizationStatusDenied) {

//定位不能用

}

跳转到设置->允许定位

NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
  if ([[UIApplication sharedApplication] canOpenURL:url]) {
                [[UIApplication sharedApplication] openURL:url]      
      }

【推送权限】

检查是否有通讯权限
UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
switch (settings.types) {
  case UIUserNotificationTypeNone:
      
      break;
  case UIUserNotificationTypeAlert:
    
      break;
  case UIUserNotificationTypeBadge:
   
      break;
  case UIUserNotificationTypeSound:
 
      break;

  default:
      break;
}
获取推送权限
UIUserNotificationSettings *setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];

【通信录权限】

在iOS8中我们访问通讯录用的还是ABAddressBookRef,现在苹果又更新了,推出了ContactsUI,据说比以前更好用,下面简单介绍一下用法

程序启动需要先请求授权
//判断是否已经授权
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if ( status == CNAuthorizationStatusAuthorized) {
//如果已经授权,直接返回
return;
}else{
//iOS8授权方式 
//创建通讯录
// ABAddressBookRef book = ABAddressBookCreateWithOptions(NULL, NULL);
//请求授权
// ABAddressBookRequestAccessWithCompletion(book, ^(bool granted, CFErrorRef error) {
// if (granted) {
// NSLog(@"授权成功");
// }else{
// NSLog(@"授权失败");
// }
// });
// [CNContactStore requestAccessForEntityType:completionHandler:]
//iOS9授权
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error) {
NSLog(@"error=%@",error);
}
if (granted) {
NSLog(@"授权成功");
}else{
NSLog(@"授权失败");
}

}]; 
}

获取联系人界面

//获取联系人列表
- (IBAction)btn2Click:(id)sender {
CNContactPickerViewController *contactPickerVC = [[CNContactPickerViewController alloc] init];
contactPickerVC.delegate = self;//控制器实现CNContactPickerDelegate
[self presentViewController:contactPickerVC animated:YES completion:nil];

}
//CNContactPickerDelegate
//取消
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker{
[picker dismissViewControllerAnimated:YES completion:nil];
}
//不实现此方法,默认进入详细列表界面
/**点击联系人**/
//- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
// NSLog(@"%@",contact.phoneNumbers[0]);
// 
//}
/**点击联系人某个属性**/
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty{

if ([contactProperty.value isKindOfClass:[CNPhoneNumber class]]) {
CNPhoneNumber *phoneNum = contactProperty.value;
NSLog(@"%@",phoneNum.stringValue);
}else{
NSLog(@"选择的不是手机号");
}

}

跳转到系统的设置界面

在项目中,我们经常会碰到使用位置的需求。当用户设置app不允许使用位置的时候,最好的用户体验就是直接调转到系统的位置设置界面,进行设置。
电池电量 Prefs:root=BATTERY_USAGE
通用设置 Prefs:root=General
存储空间 Prefs:root=General&path=STORAGE_ICLOUD_USAGE/DEVICE_STORAGE
蜂窝数据 Prefs:root=MOBILE_DATA_SETTINGS_ID
Wi-Fi 设置 Prefs:root=WIFI
蓝牙设置 Prefs:root=Bluetooth
定位设置 Prefs:root=Privacy&path=LOCATION
辅助功能 Prefs:root=General&path=ACCESSIBILITY
关于手机 Prefs:root=General&path=About
键盘设置 Prefs:root=General&path=Keyboard
显示设置 Prefs:root=DISPLAY
声音设置 Prefs:root=Sounds
App Store 设置 Prefs:root=STORE
墙纸设置 Prefs:root=Wallpaper
打开电话 Mobilephone://
世界时钟 Clock-worldclock://
闹钟 Clock-alarm://
秒表 Clock-stopwatch://
倒计时 Clock-timer://
打开相册 Photos://

NSURL*url=[NSURL URLWithString:@"Prefs:root=Privacy&path=LOCATION"];
Class LSApplicationWorkspace = NSClassFromString(@"LSApplicationWorkspace");
[[LSApplicationWorkspace performSelector:@selector(defaultWorkspace)] performSelector:@selector(openSensitiveURL:withOptions:) withObject:url withObject:nil];

这个需要添加URL Scheme,方法:Target -> Info -> URL Types,点击“+”,将URL Schemes设置为Prefs即可。

 

20160105155212746.jpeg

上面那一个方法里面有一个performSelector的方法,因为我也不太明白,所以
在此我对performSelector系列方法进行了总结

- (id)performSelector:(SEL)aSelector;

- (id)performSelector:(SEL)aSelector withObject:(id)object;

- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

这三个方法都是同步执行,与线程无关,在需要动态的去调用方法的时候去使用。

例如:

[self performSelector:@selector(configUI)]; 与[self configUI]; 效果完全相同。

withObject:(id)object 这是要传递的参数

2、

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes;

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;

  这两个方法为异步执行,只能在主线程中执行。可用于点击UI中一个按钮会触发一个消耗性能的事件,在事件执行期间按钮会一直处于高亮状态,此时可以调用该方法去异步的处理该事件,避免上述问题。

在方法未到执行时间之前,取消方法为:

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;

调用该方法之前或在该方法所在的viewController生命周期结束的时候去调用取消函数,以确保不会引起内存泄露。

3、

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

这两个方法,在主线程和子线程中均可执行,均会调用主线程的aSelector方法

如果设置wait为YES:等待当前线程执行完以后,主线程才会执行aSelector方法;

设置为NO:不等待当前线程执行完,就在主线程上执行aSelector方法。

如果,当前线程就是主线程,那么aSelector方法会马上执行。

注意:apple不允许程序员在主线程以外的线程中对ui进行操作,此时我们必须调用performSelectorOnMainThread函数在主线程中完成UI的更新

4、 

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array NS_AVAILABLE(10_5, 2_0);

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

在我们指定的线程中调用方法。

5、

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg NS_AVAILABLE(10_5, 2_0);

后台执行

今天晚上平安夜,而且还是周六,我在这里找了一天,才整理出来这么一点。书写不易,点赞吧