iOS14适配

iOS14 的适配,很重要的一环就集中在\color{red}{用户隐私}\color{red}{安全}方面。

在 iOS13 及以前,当用户首次访问应用程序时,会被要求开放大量权限,比如相册、定位、联系人等,实际上该应用可能仅仅需要一个选择图片功能,却被要求开放整个照片库的权限,这确实是不合理的.

相册

WWDC2020--What's new in PhotoKit

授权变更

iOS14 新增了Limited Photo Library Access模式,在授权弹窗中增加了 Select Photo 选项。用户可以在 App 请求调用相册时选择部分照片让 App 读取。从 App 的视⻆来看,你的相册里就只有这几张照片,App 无法得知其它照片的存在。

public enum PHAuthorizationStatus : Int {
    ...
    @available(iOS 14, *)
    case limited = 4  //用户已授权此应用程序用于有限的照片库访问。 将PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES添加到应用程序的Info.plist,以防止自动弹窗更新用户受限的库选择。 使用PhotosUI / PHPhotoLibrary + PhotosUISupport.h中的-[PHPhotoLibrary(PhotosUISupport)presentLimitedLibraryPickerFromViewController:]手动显示受限的库选择器。

}

相册授权状态新增PHAuthorizationStatus.limited,选择此授权后,App有可能会在每次触发相册功能时都进行弹窗询问用户是否需要修改照片权限。

解决方案
在应用程序的 info.plist 中添加
PHPhotoLibraryPreventAutomaticLimitedAccessAlert的值为 YES 来阻止该弹窗反复弹出,并且可通过下面这个 API 来手动显示受限的库选择器进行照片选择:

if #available(iOS 14, *) {
    PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
}

注意PHAuthorizationStatus.limited仅在新API才有效,新API新增PHAccessLevel入参

if #available(iOS 14, *) {
    PHPhotoLibrary.requestAuthorization(for: PHAccessLevel.readWrite) { authorizationStatus in
        switch authorizationStatus {
        case .limited: print("同意访问部分相册")
        case .authorized: print("同意访问全部相册")
        case .denied: print("拒绝访问相册")
        default: print("等待用户确认")
        }
    }
}

注意:
limit Photo 模式下,AssetsLibrary 访问相册会失败
在 writeOnly 模式下,AssetLibrary 也会有显示问题。建议还在使用 AssetsLibrary 的同学尽快迁移到新 API。

相册库变更

iOS14 中官方推荐使用 PHPicker 来替代原 API 进行图片选择。PHPicker 为独立进程,会在视图最顶层进行展示,应用内无法对其进行截图也无法直接访问到其内的数据。

UIImagePickerViewController 功能受限,每次只能选择一张图片,将逐渐被废弃。

    public enum SourceType : Int {
        @available(iOS, introduced: 2, deprecated: 100000, message: "Will be removed in a future release, use PHPicker.")
        case photoLibrary = 0

        case camera = 1

        @available(iOS, introduced: 2, deprecated: 100000, message: "Will be removed in a future release, use PHPicker.")
        case savedPhotosAlbum = 2
    }

PHPicker 支持多选,支持搜索,支持按 image,video,livePhotos等进行选择。

import UIKit
import PhotosUI

class ViewController: UITableViewController {

    var imageView = UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()

        if #available(iOS 14, *) {
            var pickerConfig = PHPickerConfiguration()
            pickerConfig.filter = .images
            pickerConfig.selectionLimit = 3 // 默认为1,为0时表示可多选。
            
            let picker = PHPickerViewController(configuration: pickerConfig)
            picker.delegate = self
            present(picker, animated: true) {
                
            }
        }
    }
}

extension ViewController: PHPickerViewControllerDelegate {
    
    @available(iOS 14, *)
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)
        
        if results.isEmpty { return }
        
        if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
            itemProvider.loadObject(ofClass: UIImage.self) { image, error in
                DispatchQueue.main.async {
                    if let image = image as? UIImage {
                        // Do something with image
                        self.imageView.image = image
                    }
                }
            }
        }
    }
}


IDFA隐私加强

IDFA 全称为 Identity for Advertisers,即广告标识符用来标记用户,广泛的用于投放广告、个性化推荐等

iOS13 及以前,系统会默认为用户开启允许追踪设置,我们可以简单的通过代码来获取到用户的 IDFA 标识符。

if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
    let idfaString = ASIdentifierManager.shared().advertisingIdentifier.uuidString
}

iOS14之后,这个判断用户是否允许被追踪的方法已经废弃:

@available(iOS, introduced: 6, deprecated: 14, message: "This has been replaced by functionality in AppTrackingTransparency's ATTrackingManager class.")
open var isAdvertisingTrackingEnabled: Bool { get }

IDFA默认关闭,需要向用户申请获取权限,需要在info.plist中明示用户申请权限:
key : NSUserTrackingUsageDescription
value: “需要获取您的设备信息用以推送您喜欢的内容”

不添加会产生以下crash

[access] This app has crashed because it attempted to access privacy-sensitive data without a usage description.  The app's Info.plist must contain an NSUserTrackingUsageDescription key with a string value explaining to the user how the app uses this data.

引入系统库 AppTrackingTransparency、AdSupport

import AppTrackingTransparency
import AdSupport

if #available(iOS 14, *) {
    ATTrackingManager.requestTrackingAuthorization { (status) in
        switch status {
        //已授权
        case .authorized: let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
        case .denied: print("用户拒绝访问设备信息")
        default: print("等待用户确认")
        }
    }
}

定位

WWDC2020--What's new in location
iOS14 定位授权弹窗新增 Precise 开关,默认开启,显示用户精确位置。用户通过这个开关可以进行更改。
On :地图上会显示精确位置
Off:将显示用户的大致位置

一旦用户关闭精准定位,对于对用户位置敏感度不高的 App 来说,这个似乎无影响,但是对于强依赖精确位置的 App 适配工作就显得非常重要了。
一种办法是:可以通过切换用户到“隐私设置”中开启精确定位,但是可能用户宁可放弃使用这个应用也不愿意授权app过多的访问权限。

临时精准位置授权

iOS14 在 CLLocationManager 新增两个方法可用于向用户申请临时开启一次精确位置权限

@available(iOS 14.0, *)
open func requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String, completion: ((Error?) -> Void)? = nil)

@available(iOS 14.0, *)
open func requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String)

在 Info.plist 中配置NSLocationTemporaryUsageDescriptionDictionary字典,字典中可以配置多个 purposeKey精准位置用途

    <key>NSLocationTemporaryUsageDescriptionDictionary</key>
    <dict>
        <key>TacoFeature</key>
        <string>your precise location will be used to deliver tacos to you</string>
        <key>WantsToNavigate</key>
        <string>您的精确位置将用于计算路线,并允许您使用转弯路线</string>
    </dict>

最终实现的时候,根据配置的 purposeKey ,授权框展示不同的内容

func userWantsToNavigate() {
    //例如,app需要导航,但是用户关闭了精准定位
    if #available(iOS 14.0, *) {
        if locationManager.accuracyAuthorization == .reducedAccuracy {//定位精度受限,不精准
            locationManager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "WantsToNavigate") {_ in 
                if self.locationManager.accuracyAuthorization == .fullAccuracy {
                    self.beginNavigation()
                }
            }
        } else {
            self.beginNavigation()
        }
    } else {
        
    }
}

最终呈现给用户的就是左图,右图为当App主动关闭精确定位权限申请

主动设置定位精度

  • 可以直接通过API来根据不同的需求设置不同的定位精确度。
let locationMng = CLLocationManager()
locationMng.desiredAccuracy = kCLLocationAccuracyReduced
  • 对于地理位置不敏感的App 来说,iOS14 也可以通过直接在 info.plist 中添加 NSLocationDefaultAccuracyReduced 为 true 默认请求大概位置。
    但是,这样设置之后,即使用户想要为该 App 开启精确定位权限,也无法开启。

注意,当 App 在 Background 模式下,如果并未获得精确位置授权,那么 Beacon 及其他位置敏感功能都将受到限制。


Local Network

iOS14 当 App 要使用 Bonjour 服务、访问本地局域网、使用 mDNS 服务等,都需要授权,开发者需要在 Info.plist 中详细描述使用的为哪种服务以及用途:

下图为需要无需申请权限与需要授权的服务:

设置-> 隐私 中也可以查看和修改具体有哪些 App 正在使用 LocalNetwork:

对于使用了下列包含 Bonjour 的 framework,都需要更新描述:

Wi-Fi Address

iOS8 - iOS13 ,用户在不同的网络间切换和接入时mac 地址都不会改变,这也就使得网络运营商还是可以通过 mac 地址对用户进行匹配和用户信息收集,生成完整的用户信息。

iOS14 提供 Wifi 加密服务,每次接入不同的 WiFi 使用的 mac 地址都不同。每过 24 小时,mac 地址还会更新一次。需要关注是否有使用用户网络 mac 地址的服务。

用户也可以自行选择是否开启 private Wi-Fi address:


剪切板

在 iOS14 中,读取用户剪切板的数据会弹出提示:


弹出提示的原因是使用 UIPasteboard 访问用户数据,访问以下数据都会弹出 toast 提示:

    open var string: String?
    open var strings: [String]?

    open var url: URL?
    open var urls: [URL]?

    @NSCopying open var image: UIImage?
    open var images: [UIImage]?

    @NSCopying open var color: UIColor?
    open var colors: [UIColor]?

如果应用访问剪切板仅仅用于判断是否为URL格式,则 iOS14 新增了两个 API 可以用于规避该提示,但只能用于判断剪切板中是否有 URL,无法访问甚至获取剪贴板数据:

extension UIPasteboard.DetectionPattern {

    /// NSString value, suitable for implementing "Paste and Go"
    @available(iOS 14.0, *)
    public static let probableWebURL: UIPasteboard.DetectionPattern

    /// NSString value, suitable for implementing "Paste and Search"
    @available(iOS 14.0, *)
    public static let probableWebSearch: UIPasteboard.DetectionPattern
}

如果应用想直接访问剪切板的数据,暂时可能无法做到规避该提示


相机和麦克风

iOS14 中 App 使用相机麦克风时会有图标提示以及绿点和黄点提示,并且会显示当前是哪个 App 在使用此功能。我们无法控制是否显示该提示。


会触发录音小黄点的代码示例:

let recorder = try? AVAudioRecorder(url: fileURL, settings: [:])
recorder?.record()

触发相机小绿点的代码示例:

guard let device = AVCaptureDevice.default(for: AVMediaType.video) else {
    return
}
let input = try? AVCaptureDeviceInput(device: device)
let session = AVCaptureSession()
guard let input2 = input, session.canAddInput(input2) else { return }
session.canAddInput(input2)
session.startRunning()

UITableView

UITableViewHeaderFooterView

tableView在plain模式下,UITableViewHeaderFooterView默认为浅灰色背景,直接设置背景色或者子视图背景色为UIColor.clear,亲测无效

使用debug view hierarchy 看出来HeaderView 层级有所变化
old : ProbabilityCollegeHeaderView -> _UISystemBackgroundView -> _UITableViewHeaderFooterContentView
new : ProbabilityCollegeHeaderView -> _UISystemBackgroundView -> UIView -> _UITableViewHeaderFooterContentView

iOS14增加了backgroundConfiguration 配置方法,设置此图层的背景样式

if #available(iOS 14.0, *) {
    self.backgroundConfiguration = UIBackgroundConfiguration.clear()
}

UITableViewCell

在 iOS14 中,UITableViewCell 中如果有直接添加在 cell 上的控件,也就是使用self.addSubview(_)方式添加的控件,会显示在 contentView 的下层contentView 会阻挡事件交互,使所有事件都响应tableView:didSelectRowAtIndexPath:方法如果 customView 存在交互事件将无法响应。如果 contentView 设置了背景色,还会影响界面显示.


UIDatePicker

iOS 14 中,UIDatePicker UI样式更新了

@available(iOS 13.4, *)
open var preferredDatePickerStyle: UIDatePickerStyle

如果想使用原来的播轮样式,需要设置:

datePicker.preferredDatePickerStyle = .wheels

Xcode12

xcode12默认不支持模拟器架构,导致某些项目运行发生如下错误:

No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID...

或者:
ld: in /Users/vicentwyh/xxx, building for iOS Simulator, but linking in object file built for iOS, file '/Users/vicentwyhxxx for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

解决方法:(以下任选一种一种)

  • 在Build Setting中,删除VALID_ARCHS这一栏
  • 或者在VALID_ARCHS添加x86_64

参考:
iOS14 隐私适配及部分解决方案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容