iOS14隐私权限适配及其他

Overview

WWDC2020发布会上苹果展示了新的iOS14系统。对于iOS开发工程师来说,适配iOS14其重点在于隐私权限的适配。具体内容可以观看WWDC2020中:建立更好的隐私信任的视频进行了解。

note:截止目前,适配iOS14需要更新mac系统到11 beta 6、xcode需要更新到12 beta 6、手机需要更新到14 beta 8,相关版本可以到苹果官网下载;更新beta版本打包可能会影响正常上架,该问题未进行测试,但需要引起注意。

广告标识符的获取

广告标识符(Identity for Advertisers)简称IDFA,主要用来标记用户。在iOS14系统中,系统会默认关闭广告标跟踪权限。如果在开发中使用,需要向用户请求权限,步骤如下:

1、info.plist配置

选中info.plist右键Open As->Source Code 然后将下方配置和描述信息添加进去。

1
2
<key>NSUserTrackingUsageDescription</key>
<string>“太平通宝”需要您允许访问广告标识符权限,以便于追踪广告和信息的推送</string>

2、头文件引入

为了适配低版本Xcode编译,需要使用预编译命令,所以头文件引入方式如下所示:

1
2
3
#if defined(__IPHONE_14_0)
#import <AppTrackingTransparency/AppTrackingTransparency.h>//适配iOS14
#endif

3、获取IDFA

iOS14及以上系统,需要先请求跟踪权限,用户同意后才能获取广告标识。获取方法标识符的代码iOS14之前一样。示例代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
+ (NSString *)idfaString {
__block NSString *idfa;
#if defined(__IPHONE_14_0)
if (@available(iOS 14, *)) {
// iOS14及以上版本需要先请求权限
ATTrackingManagerAuthorizationStatus status = ATTrackingManager.trackingAuthorizationStatus;
if (status == ATTrackingManagerAuthorizationStatusNotDetermined) { //用户未做选择或未弹窗
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// 获取到权限后,依然使用老方法获取idfa
if (status == ATTrackingManagerAuthorizationStatusAuthorized) { //用户允许
idfa = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
ICLog(@"--iOS14----%@----",idfa);
}
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}else if(status == ATTrackingManagerAuthorizationStatusAuthorized){//用户允许
idfa = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
}else{
ICLog(@"请在设置-隐私-Tracking中允许App请求跟踪");
}
}
#else
// iOS14以下版本依然使用老方法
// 判断在设置-隐私里用户是否打开了广告跟踪
if ([[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]) {
idfa = [[ASIdentifierManager sharedManager].advertisingIdentifier UUIDString];
ICLog(@"---iOS14以下系统----%@-----",idfa);
} else {
ICLog(@"请在设置-隐私-广告中打开广告跟踪功能");
}
#endif
idfa = (idfa ? : @"");
return idfa;
}

相册权限

iOS14中新增了个有限的图片库访问模式,在授权弹窗的时候会新增个Select Photo选项,用户可以选择一部分图片供App读取,而App无法获取相册中的所有图片信息。

如果用户使用了有限图片库访问(Limited Photo Library Access)的模式, 如果App不进行适配,权限提示框会在每次冷启动打开相册的时候重新弹出,我们需要在info.plist中进行配置,关闭弹窗。

1、info.plist配置

选中info.plist右键Open As->Source Code 然后将下方配置和描述信息添加进去。

1
2
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>

当然,我们也可以通过相应API来控制何时弹出图片选择的弹出。API如下所示:

1
[[PHPhotoLibrary sharedPhotoLibrary] presentLimitedLibraryPickerFromViewController:self];

同时,在iOS14中官方推荐使用苹果推荐使用PHPicker来代替原API来选择图片。

关于PHPicker的介绍可以参看WWDC2020:认识新的照片选择器的相关介绍。

位置信息

在iOS14中,苹果新增了模糊定位的概念。原因是苹果认为很多APP并不需要获取用户的精准定位。所以在iOS14授权弹窗的时候新增了Precise的精准开关,默认会选中精准位置,用户可以通过这个开关进行更改。

对于地理位置敏感的APP

1、info.plist配置

不过对于需要精准定位的App需要在info.plist中设置NSLocationTemporaryUsageDescriptionDictionary字典,key为purposeKey,value为对应获取精准定位的原因。

示例如下:

1
2
3
4
5
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>punchTheClock</key>
<string>“太平通宝”需要您允许精准定位,以便于使用打卡功能</string>
</dict>

2、获取单次精准定位

同时需要调用相应API,获取精准定位。代码如下:

1
[CALocationMnanger requestTemporaryFullAccuracyAuthorizationWithPurposeKey:@"punchTheClock"]

对于地理位置不敏感的App

对定位信息不敏感的APP,iOS14中可以直接在info.plist中添加NSLocationDefaultAccuracyReducedtrue,默认请求模糊定位

选中info.plist右键Open As->Source Code 然后将下方配置和描述信息添加进去。

1
2
<key>NSLocationDefaultAccuracyReduced</key>
<true/>

Local Network

iOS14中新增了Local Network权限提示,具体关于Local Network的相关信息可以查看苹果官网的视频或者少数派的关于iOS14新增的本地网络权限,要开给第三方App吗?等相关资料。

剪贴板

在iOS14中,如果APP读取剪切版的内容时,手机会弹出提示,提示哪个APP在获取剪切板内容。

相机和麦克风

在iOS14中APP如果使用相机和麦克风,手机的的上方会有绿色和黄色的提示,同时也可以查看是哪个APP在使用相机和麦克风。

关于该功能开发人员无法控制。

UIDatePicker更新UI样式

在iOS14中,UIDatePicker的样式新增了UIDatePickerStyleInline,而且为默认值。如果项目中使用了UIDatePicker,而且希望使用原来的样式,需要设置其样式。

1
self.pickerView.preferredDatePickerStyle = UIDatePickerStyleWheels;

UITableViewCell适配

UITableViewCell无法点击

在Xcode12、iOS14中,如果在UITableViewCell渲染前没有调用self.contentView,系统会在渲染完UITableViewCell上的控件后在其上方添加contentView,这会使contentView拦截住UITableViewCell控件的响应事件。

其解决方式是将UITableViewCell上的控件添加到self.contentView

1
2
3
4
[self addSubview:view];
改为:
[self.contentView addSubview:view];

hook修复UITableViewCell无法点击问题

虽然上方的方法可以修复UITableViewCell无法点击问题,但对于一个很庞大的项目来说,不确定是否有遗漏,尤其是一些组件。所以在项目中需要用到修改+hook的方式。

方法也很简单,直接hook住UITableViewCell中的addSubview:方法,全局修复下即可,同时后期写代码也要注意代码规范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import "UITableViewCell+TB.h"
#import <objc/runtime.h>

@implementation UITableViewCell (TB)

+ (void)load {
Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"addSubview:"));
Method method2 = class_getInstanceMethod([self class], @selector(tb_addSubview:));
method_exchangeImplementations(method1, method2);
}

- (void)addSubview:(UIView *)view{
[super addSubview:view];
}

- (void)tb_addSubview:(UIView *)view {
if ([view isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) {
[self tb_addSubview:view];
} else {
[self.contentView addSubview:view];
}
}

@end

UITableViewCell 背景色变成灰色

灰色为self.contentView的背景色,在init方法中共添加即可。

YYAnimatedImageView 无法加载图片

因为项目中用到YYImage第三方库。

解决方式:
修复:YYAnimatedImageView.m 529行的函数修改如下:

1
2
3
4
5
6
7
8
9
- (void)displayLayer:(CALayer *)layer {
if (_curFrame) {
layer.contents = (__bridge id)_curFrame.CGImage;
}else {
if (@available(iOS 14.0, *)) {
[super displayLayer:layer];
}
}
}

但在项目中,并不会这些修复,毕竟是第三方库,我们可以直接替换这个类的该方法。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import "YYAnimatedImageView+TB.h"
#import <objc/runtime.h>

@implementation YYAnimatedImageView (TB)

+ (void)load {
Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"displayLayer:"));
Method method2 = class_getInstanceMethod([self class], @selector(tb_displayLayer:));
method_exchangeImplementations(method1, method2);
}

- (void)tb_displayLayer:(CALayer *)layer{
Ivar ivar = class_getInstanceVariable(self.class, "_curFrame");
UIImage *_curFrame = object_getIvar(self, ivar);
if (_curFrame) {
layer.contents = (__bridge id)_curFrame.CGImage;
}else{
if (@available(iOS 14.0, *)) {
[super displayLayer:layer];
}
}
}

@end

写在最后

关于真机调试

最新Xcode 12 beta版本31G,而且还不稳定,如果需要使用低版本Xcode运行到iOS14的真机上可以下载iOS14的DeviceSupport,存放的路径为:

1
2
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

关于更新iOS14系统

关于更新iOS14 beta版的系统,我在百度经验上水了篇文章,可以点击百度经验查看,当然经验水的有些粗糙,还有错别字。

也可能是好久没写了,写完后,竟然还给了0.3元的红包……

参考资料