美文网首页基础应用
ios---聊聊权限那些事

ios---聊聊权限那些事

作者: 梦回蓝桥 | 来源:发表于2018-08-20 16:09 被阅读94次

呐呐,谈个女朋友想解锁更多姿势你也知道必须是要有权限访问的,强制执行?大哥,别闹,违法的!!!
今天就来说说iOS中关于权限的那些事!

  • 相机权限: Privacy - Camera Usage Description
  • 相册权限: Privacy - Photo Library Usage Description
  • 定位权限: Privacy - Location Always and When In Use Usage Description
  • 定位权限: Privacy - Location When In Use Usage Description
  • 蓝牙权限: Privacy - Bluetooth Peripheral Usage Description
  • 通讯录权限: Privacy - Contacts Usage Description
  • 麦克风权限:Privacy - Microphone Usage Description

一 : 通讯录权限

iOS 9 以前的通讯录框架

  1. AddressBookUI.framework框架
    1. 提供了联系人列表界面、联系人详情界面、添加联系人界面等
    2. 一般用于选择联系人
  2. AddressBook.framework 框架
    1. 纯 C 语言的 API,仅仅是获得联系人数据
    2. 没有提供 UI 界面展示,需要自己搭建联系人展示界面
    3. 里面的数据类型大部分基于 Core Foundation 框架,使用起来炒鸡复杂

iOS 9 以后最新通讯录框架

  1. ContactsUI.framework 框架。

    拥有 AddressBookUI.framework 框架的所有功能,使用起来更加的面向对象。

  2. Contacts.framework 框架。

    拥有 AddressBook.framework 框架的所有功能,不再是 C 语言的 API,使用起来非常简单。

值得注意的是如果只是单纯的选择某个联系人的信息时使用AddressBookUI.framework或者ContactsUI.framework 框架时是不需要设置权限
只有在获取用户通讯录的时候系统才会弹出权限提示框

WechatIMG1176.png

选择亲近的联系人只是获取单个联系人信息,这里不需要设置权限相关

1. iOS 9 之前使用AddressBookUI.framework框架获取联系人信息

- (void) pushToEarlyIOS9VC
{
    ABPeoplePickerNavigationController *pvc = [[ABPeoplePickerNavigationController alloc] init];
    pvc.peoplePickerDelegate = self;
    [self presentViewController:pvc animated:YES completion:nil];
}


这样就跳转到了联系人界面,但是这里又分为两种情况,
一种是点击联系人后直接回調:

列表一.png

代码如下:

- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person
{
    CFStringRef firstNameRef = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    CFStringRef lastNameRef = ABRecordCopyValue(person, kABPersonLastNameProperty);

    NSString * firstName = CFBridgingRelease(firstNameRef);
    NSString * lastName = CFBridgingRelease(lastNameRef);

    NSString *nameStr;
    if (! firstName && lastName) {
        nameStr = lastName;
    }else if (! lastName && firstName){
        nameStr = firstName;
    }else if (firstName && lastName){
        nameStr = [NSString stringWithFormat:@"%@%@", lastName, firstName];
    }else{
        nameStr = @"";
    }

    ABMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);
    CFIndex count = ABMultiValueGetCount(phoneMulti);

    NSString *phoneNum;
    for (int i = 0; i  < count; i++)
    {
        // label:_$!<Mobile>!$_或者_$!<Home>!$_
        NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(multi, i);
        // _$!<Mobile>!$_或者_$!<Home>!$_对应的手机号码字符串
        NSString *phone =(__bridge_transfer NSString *)  ABMultiValueCopyValueAtIndex(multi, i);//手机号
        NSLog(@"%@---%@", label, phone);
        phoneNum = phone;
    }

    

    // 之后对取到的手机号phoneNum进行字符串剪切、替换、文本赋值操作

}

另一种是点击联系人后跳转到详情页再进行号码选择:

WechatIMG11767的副本.png

代码如下:

- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    SXLog(@"%d---%d",property,identifier);
    // 获取该联系人多重属性--电话号
    ABMutableMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);

    // 获取该联系人的名字,简单属性,只需ABRecordCopyValue取一次值
    ABMutableMultiValueRef firstNameRef = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    NSString *firstName = (__bridge NSString *)(firstNameRef);
    ABMutableMultiValueRef lastNameRef = ABRecordCopyValue(person, kABPersonLastNameProperty);
    NSString *lastName = (__bridge NSString *)(lastNameRef);

    NSString *nameStr;
    if (!firstName && lastName) {
        nameStr = lastName;
    }else if (!lastName && firstName){
        nameStr = firstName;
    }else if (firstName && lastName){
        nameStr = [NSString stringWithFormat:@"%@%@", lastName, firstName];
    }else{
        nameStr = @"";
    }

    // 点击某个联系人电话后dismiss联系人控制器,并回调点击的数据
    [self dismissViewControllerAnimated:YES completion:^{
        // 从联系人详情中取值,参数identifier是取点击的索引标志(住宅号码、手机号码等)
        NSString *phoneNum =  (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phoneMulti, ABMultiValueGetIndexForIdentifier(phoneMulti,identifier)) ;
        // 之后对取到的手机号phoneNum进行字符串剪切、替换、文本赋值操作
    }];
}

2. iOS 9 之后使用ContactsUI.framework 框架获取联系人信息

- (void) pushToAfterIOS9VC
{
    if (@available(iOS 9.0, *)) {
        CNContactPickerViewController *contactPickerViewController = [[CNContactPickerViewController alloc] init];
        contactPickerViewController.delegate = self;
        [self presentViewController:contactPickerViewController animated:YES completion:nil];
    } else {
        // Fallback on earlier versions
    }
}


这里也跟使用AddressBookUI.framework一样,分两种情况,点击联系人后直接回調

- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {

    NSString *givenName = contact.givenName;
    NSString *familyName = contact.familyName;
    NSArray * phoneNumbers = contact.phoneNumbers;
    for (CNLabeledValue<CNPhoneNumber*>*phone in phoneNumbers) {
        //homeOrMobile:遍历个人对应所有手机号得到的值(_$!<Mobile>!$_或者_$!<Home>!$_)
        NSString *homeOrMobile = phone.label;
        CNPhoneNumber *phonNumber = (CNPhoneNumber *)phone.value;
        // 手机号字符串
        NSString *phoneNumberString = phonNumber.stringValue;

        // 之后做字符串的剪切、替换、文本赋值等操作
    }
}


点击联系人后跳转到进入详情页进行选择:

- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {

    NSString *givenName = contactProperty.contact.givenName;
    NSString *familyName = contactProperty.contact.familyName;
    NSArray * phoneNumbers = contactProperty.contact.phoneNumbers;
    for (CNLabeledValue<CNPhoneNumber*>*phone in phoneNumbers) {
        //homeOrMobile:遍历详情属性得到的值(_$!<Mobile>!$_或者_$!<Home>!$_)
        NSString *homeOrMobile = phone.label;
        CNPhoneNumber *phonNumber = (CNPhoneNumber *)phone.value;
        // 手机号字符串
        NSString *phoneNumberString = phonNumber.stringValue;
        // 如果不做此判断取到的手机号都是最后一个(一个人可能对应多个手机号)
        if ([homeOrMobile isEqualToString:contactProperty.label]) {
            // contactProperty.label手动选择的结果(_$!<Mobile>!$_或者_$!<Home>!$_其中之一)
            // 匹配之后做字符串的剪切、替换、文本赋值等操作
        }
    }
}

到这里选择联系人的几种情况就讲完了,值得注意的是单纯的选择某个联系人的信息跟权限是无关的,是无关的,是无关的!!!

只有在你想获取用户通讯录的时候才需设置用户权限

获取用户通讯录

if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0){
    [self requestContactAuthorAfterSystemVersion9];
}else{
    [self requestContactAuthorEarlySystemVersion9];
}

3. iOS9之前使用AddressBook.framework框架获取用户通讯录

- (void) requestContactAuthorEarlySystemVersion9{

    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined){
        ABAddressBookRef bookRef = ABAddressBookCreate();
        ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
            if (granted){
                bookIsOpen = YES;
                [self getADBookEarlyIOS9];
            }else{
                bookIsOpen = NO;
            }
        });
    }else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized){
        bookIsOpen = YES;
        [self getADBookEarlyIOS9];
    }
}


- (void) getADBookEarlyIOS9{
    // 2. 获取所有联系人
    ABAddressBookRef addressBookRef = ABAddressBookCreate();
    CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(addressBookRef);
    long count = CFArrayGetCount(arrayRef);
    for (int i = 0; i < count; i++) {
        NSMutableDictionary *dic = [NSMutableDictionary new];
        //获取联系人对象的引用
        ABRecordRef people = CFArrayGetValueAtIndex(arrayRef, i);
    
        //获取当前联系人名字
        NSString *firstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty));
        //获取当前联系人姓氏
        NSString *lastName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonLastNameProperty));
  
        //拼接姓名
        NSString *nameStr;
        if (!lastName && !firstName) {
            nameStr = @"";
        }else if (!lastName && firstName){
            nameStr = [NSString stringWithFormat:@"%@",firstName];
        }else if (lastName && !firstName){
            nameStr = [NSString stringWithFormat:@"%@",lastName];
        }else if (lastName && firstName){
            nameStr = [NSString stringWithFormat:@"%@%@",lastName,firstName];
        }
        NSString *phoneStr;
        ABMultiValueRef phones = ABRecordCopyValue(people, kABPersonPhoneProperty);
        for (NSInteger j=0; j<ABMultiValueGetCount(phones); j++) {
            NSString *phone = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, j));
            phoneStr = phone;
        }
    
        if (!phoneStr) {
            phoneStr = @"";
        }
        [dic setObject:phoneStr forKey:@"mobile"];
        [dic setObject:nameStr forKey:@"name"];
        if ([self isBlankString:nameStr] || [self isBlankString:phoneStr]) {
            //名字或者手机号为空视为非法
        }else{
            [self.phoneNumArr addObject: dic];
        }
    }
}

4. iOS9之后使用Contacts.framework 框架获取用户通讯录

- (void)requestContactAuthorAfterSystemVersion9{

    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    if (status == CNAuthorizationStatusNotDetermined) {
        CNContactStore *store = [[CNContactStore alloc] init];
        [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError*  _Nullable error) {
            if (granted) {
                bookIsOpen = YES;
                [self getADBookAfterIOS9];
            }else{
                bookIsOpen = NO;
                [self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
            }
        }];
    }else if(status == CNAuthorizationStatusRestricted){
        bookIsOpen = NO;
        [self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
    }else if (status == CNAuthorizationStatusDenied){
        bookIsOpen = NO;
        [self showAlertVCWithTitle:@"通讯录访问" message:@"去分期需要访问您的通讯录"];
    }else if (status == CNAuthorizationStatusAuthorized){//已经授权
        bookIsOpen = YES;
        //有通讯录权限-- 进行下一步操作
        [self getADBookAfterIOS9];
    }
}


- (void) showAlertVCWithTitle:(NSString *)title message:(NSString *)message{

    UIAlertController *alertC = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *cancleAC = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:nil];
    UIAlertAction *openAC = [UIAlertAction actionWithTitle:@"打开" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
        NSURL *settingUrl = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
        if ([[UIApplication sharedApplication] canOpenURL:settingUrl]) {
            [[UIApplication sharedApplication] openURL:settingUrl];
        }
    }];
    [alertC addAction:cancleAC];
    [alertC addAction:openAC];
    [self presentViewController:alertC animated:YES completion:nil];
}


- (void) getADBookAfterIOS9{
    // 获取指定的字段,并不是要获取所有字段,需要指定具体的字段
    NSArray *keysToFetch = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
    CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
    CNContactStore *contactStore = [[CNContactStore alloc] init];

    [contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
    
        NSMutableDictionary *mDic = [NSMutableDictionary new];
    
        NSString *givenName = contact.givenName;
        NSString *familyName = contact.familyName;//姓
        //拼接姓名
        NSString *nameStr;
        if (!givenName && !familyName) {
            nameStr = @"";
        }else if (!givenName && familyName){
            nameStr = [NSString stringWithFormat:@"%@",familyName];
        }else if (givenName && !familyName){
            nameStr = [NSString stringWithFormat:@"%@",givenName];
        }else if (givenName && familyName){
            nameStr = [NSString stringWithFormat:@"%@%@",familyName,givenName];
        }
    
        NSArray *phoneNumbers = contact.phoneNumbers;
        NSString *phoneStr;
        for (CNLabeledValue *labelValue in phoneNumbers) {
            //遍历一个人名下的多个电话号码
            NSString *label = labelValue.label;
            CNPhoneNumber *phoneNumber = labelValue.value;
            NSString * string = phoneNumber.stringValue ;
        
            //去掉电话中的特殊字符
            //string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""];
            //string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""];
            //string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""];
            //string = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
            //string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
            phoneStr = string;
        }
        if (!phoneStr) {
            phoneStr = @"";
        }
        [mDic setObject:phoneStr forKey:@"mobile"];
        [mDic setObject:nameStr forKey:@"name"];
        if ([self isBlankString:nameStr] || [self isBlankString:phoneStr]) {
            //名字或者手机号为空视为非法
        }else{
            [self.phoneNumArr addObject:mDic];
        }
    }];
}

\color{red}{-----------------------------}

二:位置权限

导入类库#import <CoreLocation/CLLocationManager.h>并遵守CLLocationManagerDelegate协议

#pragma mark - 启动定位权限
-(void)getLocation
{
    //判断定位功能是否打开
    if ([CLLocationManager locationServicesEnabled]) {
        locationmanager = [[CLLocationManager alloc]init];
        locationmanager.delegate = self;
        currentCity = [NSString new];

        if (@available(iOS 9.0, *)) {
            locationmanager.allowsBackgroundLocationUpdates = YES;
        } else {
            // Fallback on earlier versions
        }
        [locationmanager requestAlwaysAuthorization];//后台始终定位
        [locationmanager requestWhenInUseAuthorization];//使用期间定位
    
        //设置寻址精度
        locationmanager.desiredAccuracy = kCLLocationAccuracyBest;
        locationmanager.distanceFilter = 5.0;
        [locationmanager startUpdatingLocation];
    }
}

#pragma mark CoreLocation delegate (定位失败)
//定位失败后调用此代理方法
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    //失败后执行你需要的相应操作
    strlatitude = @"";
    strlongitude = @"";
}

#pragma mark 定位成功后则执行此代理方法
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
    [locationmanager stopUpdatingHeading];
    //旧址
    CLLocation *currentLocation = [locations lastObject];
    CLGeocoder *geoCoder = [[CLGeocoder alloc]init];

    strlatitude = [NSString stringWithFormat:@"%.5f",currentLocation.coordinate.latitude];
    strlongitude = [NSString stringWithFormat:@"%.5f",currentLocation.coordinate.longitude];

    SXLog(@"%@---%@",strlatitude,strlongitude);

    //反地理编码
    [geoCoder reverseGeocodeLocation:currentLocation completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
        if (placemarks.count > 0) {
            CLPlacemark *placeMark = placemarks[0];
            currentCity = placeMark.locality;
            if (!currentCity) {
                currentCity = @"无法定位当前城市";
            }
            //placeMark里面包含了你需要的信息,做相应处理就好了
        }
    }];
}


如果有更高需求,可点进协议查看更多方法
至于高德、百度地图,之前也都用过,官方文档说的也比较清晰,这里就不过多解读了

这里还需要注意的是如果同时设置了requestWhenInUseAuthorizationrequestAlwaysAuthorizationrequestAlwaysAuthorization的权限是大于(包括)requestWhenInUseAuthorization

如果需要后台持续定位,首先要将下图的Location updates打钩

屏幕快照 2018-08.png

//执行如下方法
[self.locationManager requestAlwaysAuthorization];


这样就可以实现后台定位,但是该方法只能实现后台定位20-30分钟的时间;
如果想达到后台永久持续定位的效果,在定位的时候需要添加如下代码:

self.locationManager.pausesLocationUpdatesAutomatically = NO;   //系统是否可以自行中断程序的定位功能


该方法让系统不能够自行关闭程序的定位功能,保证程序一直处于后台定位中

三:相册权限

相机、相册、麦克风等权限状态都对应下列这种

AuthorizationStatusNotDetermined      // 用户从未进行过授权等处理,首次访问相应内容会提示用户进行授权
AuthorizationStatusAuthorized = 0,    // 用户已授权,允许访问
AuthorizationStatusDenied,            // 用户拒绝访问
AuthorizationStatusRestricted,        // 应用没有相关权限,且当前用户无法改变这个权限,比如:家长控制

是否支持

[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]


获取权限状态
ios8之前

ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];


iOS8之后

#import <Photos/PHPhotoLibrary.h>
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];


请求权限

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { 
if (status == PHAuthorizationStatusAuthorized){ 
    //已授权
  }else{ 
    //未授权
    }
 }];


这里值得注意的一点是

1、iOS11之前访问相册和存储照片到相册(读写权限),需要用户授权,需要添加NSPhotoLibraryUsageDescription
2、iOS11之后:默认开启访问相册权限(读权限),无需用户授权,无需添加NSPhotoLibraryUsageDescription,添加图片到相册(写权限),才需要用户授权,需要添加NSPhotoLibraryAddUsageDescription

PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied){
    // 无权限
    UIAlertView * alart = [[UIAlertView alloc]initWithTitle:@"温馨提示" message:@"请您设置允许该应用访问您的相机\n设置>隐私>相机" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
    [alart show];
    return;
}

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
    if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied) {
        SXLog(@"没权限");
    }else if (status == PHAuthorizationStatusNotDetermined){
        SXLog(@"暂未确定");
    }else if (status == PHAuthorizationStatusAuthorized){
        SXLog(@"已授权");
    }
}];

四:相机、麦克风权限

是否支持

// 是否有摄像头
[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]
// 前置摄像头是否可用
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront];
// 后置摄像头是否可用
[UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear];


获取权限状态

//获取相机权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
//获取麦克风权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];


获取相机、麦克风权限

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
    if (granted) {
        //相机授权成功
    }else{
        //相机授权失败
    }
}];

[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
    if (granted) {
        //麦克风授权成功
    }else{
        //麦克风授权失败
    }
}];

#import <Foundation/Foundation.h>
//获取相机或麦克风权限状态
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

if (status == AVAuthorizationStatusNotDetermined) {
    //暂未授权的处理, AVMediaTypeVideo改为AVMediaTypeAudio就是麦克风
    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
        if (granted) {
            SXLog(@"相机或麦克风授权成功");
        }else{
            SXLog(@"相机或麦克风授权失败");
        }
    }];
}else if (status == AVAuthorizationStatusRestricted || status == AVAuthorizationStatusDenied){
    SXLog(@"无权限");
}else if (status == AVAuthorizationStatusAuthorized){
    SXLog(@"有权限");
}

五:推送权限

这个最近几个项目用的都是极光或者友盟,就不多说了,按文档无脑集成就好!

相关文章

  • ios---聊聊权限那些事

    呐呐,谈个女朋友想解锁更多姿势你也知道必须是要有权限访问的,强制执行?大哥,别闹,违法的!!!今天就来说说iOS中...

  • 聊聊那些事

    从昨天反思后很久没有入睡,不知道从什么时候开始我对一切事物没有了耐心,看剧喜欢快进,因此更喜欢刷电影。在很多...

  • iOS---常用权限判断

    权限判断引入头文件

  • 聊聊高考那些事

    说起高考,其实离我已经有些遥远了,虽然我怎么着也是个90后,但是人家00后参加高考好像跟我也没什么关系。 但是突然...

  • 聊聊“标签”那些事

    图片发自简书App 文|冬少爷 2018年4月5日 星期四 天气:大雨 01 人往高处走,水往低处流,这是千古...

  • 聊聊板书那些事

    在信息技术发展的今天,教师已经掌握了多种多样的多媒体技术,课堂也逐渐缤纷多彩,但是我们在追求多元化的同时,时常会忽...

  • 聊聊“随便”那些事

    “今天晚上吃什么?” “随便。” “那我们吃川菜吧?” “不行啊,川菜太辣,吃了会上火。” “那我们去兰州拉面馆吃...

  • 聊聊监护那些事

    在生与死之间,往往要经历一个“失智”的阶段,虽然人活着,但其实你已不是你,无法做出分析、判断与选择。这个时候...

  • 聊聊前端那些事

    三个月前,我还是一个在工地搬砖的土木男。虽然在部门顶着个技术员的称号,实际上干的跟技术毫无关系的事,我曾经以为的技...

  • 【聊聊】电梯那些事

    记得在前几个月, 总有业主反应, 怎么电梯又坏了, 是不是质量问题, 杂牌子吧。 堂堂日本TOSHIBA(东芝) ...

网友评论

    本文标题:ios---聊聊权限那些事

    本文链接:https://www.haomeiwen.com/subject/bqazbftx.html