iOS14 的適配,很重要的一環(huán)就集中在和
方面。
在 iOS13 及以前,當(dāng)用戶首次訪問應(yīng)用程序時,會被要求開放大量權(quán)限,比如相冊、定位、聯(lián)系人等,實際上該應(yīng)用可能僅僅需要一個選擇圖片功能,卻被要求開放整個照片庫的權(quán)限,這確實是不合理的.
相冊
WWDC2020--What's new in PhotoKit
授權(quán)變更
iOS14 新增了Limited Photo Library Access模式,在授權(quán)彈窗中增加了 Select Photo 選項。用戶可以在 App 請求調(diào)用相冊時選擇部分照片讓 App 讀取。從 App 的視?來看,你的相冊里就只有這幾張照片,App 無法得知其它照片的存在。

public enum PHAuthorizationStatus : Int {
...
@available(iOS 14, *)
case limited = 4 //用戶已授權(quán)此應(yīng)用程序用于有限的照片庫訪問。 將PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES添加到應(yīng)用程序的Info.plist,以防止自動彈窗更新用戶受限的庫選擇。 使用PhotosUI / PHPhotoLibrary + PhotosUISupport.h中的-[PHPhotoLibrary(PhotosUISupport)presentLimitedLibraryPickerFromViewController:]手動顯示受限的庫選擇器。
}
相冊授權(quán)狀態(tài)新增PHAuthorizationStatus.limited,選擇此授權(quán)后,App有可能會在每次觸發(fā)相冊功能時都進行彈窗詢問用戶是否需要修改照片權(quán)限。
解決方案
在應(yīng)用程序的 info.plist 中添加
PHPhotoLibraryPreventAutomaticLimitedAccessAlert的值為 YES 來阻止該彈窗反復(fù)彈出,并且可通過下面這個 API 來手動顯示受限的庫選擇器進行照片選擇:

if #available(iOS 14, *) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
}
注意:PHAuthorizationStatus.limited僅在新API才有效,新API新增PHAccessLevel入?yún)?/p>
if #available(iOS 14, *) {
PHPhotoLibrary.requestAuthorization(for: PHAccessLevel.readWrite) { authorizationStatus in
switch authorizationStatus {
case .limited: print("同意訪問部分相冊")
case .authorized: print("同意訪問全部相冊")
case .denied: print("拒絕訪問相冊")
default: print("等待用戶確認(rèn)")
}
}
}
注意:
在limit Photo模式下,AssetsLibrary 訪問相冊會失敗;
在 writeOnly 模式下,AssetLibrary 也會有顯示問題。建議還在使用 AssetsLibrary 的同學(xué)盡快遷移到新 API。
相冊庫變更
iOS14 中官方推薦使用 PHPicker 來替代原 API 進行圖片選擇。PHPicker 為獨立進程,會在視圖最頂層進行展示,應(yīng)用內(nèi)無法對其進行截圖也無法直接訪問到其內(nèi)的數(shù)據(jù)。
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 // 默認(rèn)為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,即廣告標(biāo)識符。用來標(biāo)記用戶,廣泛的用于投放廣告、個性化推薦等。
iOS13 及以前,系統(tǒng)會默認(rèn)為用戶開啟允許追蹤設(shè)置,我們可以簡單的通過代碼來獲取到用戶的 IDFA 標(biāo)識符。
if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
let idfaString = ASIdentifierManager.shared().advertisingIdentifier.uuidString
}
iOS14之后,這個判斷用戶是否允許被追蹤的方法已經(jīng)廢棄:
@available(iOS, introduced: 6, deprecated: 14, message: "This has been replaced by functionality in AppTrackingTransparency's ATTrackingManager class.")
open var isAdvertisingTrackingEnabled: Bool { get }
且IDFA默認(rèn)關(guān)閉,需要向用戶申請獲取權(quán)限,需要在info.plist中明示用戶申請權(quán)限:
key : NSUserTrackingUsageDescription
value: “需要獲取您的設(shè)備信息用以推送您喜歡的內(nèi)容”

不添加會產(chǎn)生以下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.
引入系統(tǒng)庫 AppTrackingTransparency、AdSupport
import AppTrackingTransparency
import AdSupport
if #available(iOS 14, *) {
ATTrackingManager.requestTrackingAuthorization { (status) in
switch status {
//已授權(quán)
case .authorized: let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
case .denied: print("用戶拒絕訪問設(shè)備信息")
default: print("等待用戶確認(rèn)")
}
}
}

定位
WWDC2020--What's new in location
iOS14 定位授權(quán)彈窗新增 Precise 開關(guān),默認(rèn)開啟,顯示用戶精確位置。用戶通過這個開關(guān)可以進行更改。
On :地圖上會顯示精確位置;
Off:將顯示用戶的大致位置

一旦用戶關(guān)閉精準(zhǔn)定位,對于對用戶位置敏感度不高的 App 來說,這個似乎無影響,但是對于強依賴精確位置的 App 適配工作就顯得非常重要了。
一種辦法是:可以通過切換用戶到“隱私設(shè)置”中開啟精確定位,但是可能用戶寧可放棄使用這個應(yīng)用也不愿意授權(quán)app過多的訪問權(quán)限。
臨時精準(zhǔn)位置授權(quán)
iOS14 在 CLLocationManager 新增兩個方法可用于向用戶申請臨時開啟一次精確位置權(quán)限。
@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 和 精準(zhǔn)位置用途
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>TacoFeature</key>
<string>your precise location will be used to deliver tacos to you</string>
<key>WantsToNavigate</key>
<string>您的精確位置將用于計算路線,并允許您使用轉(zhuǎn)彎路線</string>
</dict>

最終實現(xiàn)的時候,根據(jù)配置的 purposeKey ,授權(quán)框展示不同的內(nèi)容
func userWantsToNavigate() {
//例如,app需要導(dǎo)航,但是用戶關(guān)閉了精準(zhǔn)定位
if #available(iOS 14.0, *) {
if locationManager.accuracyAuthorization == .reducedAccuracy {//定位精度受限,不精準(zhǔn)
locationManager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "WantsToNavigate") {_ in
if self.locationManager.accuracyAuthorization == .fullAccuracy {
self.beginNavigation()
}
}
} else {
self.beginNavigation()
}
} else {
}
}
最終呈現(xiàn)給用戶的就是左圖,右圖為當(dāng)App主動關(guān)閉精確定位權(quán)限申請

主動設(shè)置定位精度
- 可以直接通過API來根據(jù)不同的需求設(shè)置不同的定位精確度。
let locationMng = CLLocationManager()
locationMng.desiredAccuracy = kCLLocationAccuracyReduced
- 對于地理位置不敏感的App 來說,iOS14 也可以通過直接在 info.plist 中添加
NSLocationDefaultAccuracyReduced為 true 默認(rèn)請求大概位置。
但是,這樣設(shè)置之后,即使用戶想要為該 App 開啟精確定位權(quán)限,也無法開啟。
注意,當(dāng) App 在 Background 模式下,如果并未獲得精確位置授權(quán),那么 Beacon 及其他位置敏感功能都將受到限制。
Local Network
iOS14 當(dāng) App 要使用 Bonjour 服務(wù)、訪問本地局域網(wǎng)、使用 mDNS 服務(wù)等,都需要授權(quán),開發(fā)者需要在 Info.plist 中詳細描述使用的為哪種服務(wù)以及用途:


在 設(shè)置-> 隱私 中也可以查看和修改具體有哪些 App 正在使用 LocalNetwork:


Wi-Fi Address
iOS8 - iOS13 ,用戶在不同的網(wǎng)絡(luò)間切換和接入時,mac 地址都不會改變,這也就使得網(wǎng)絡(luò)運營商還是可以通過 mac 地址對用戶進行匹配和用戶信息收集,生成完整的用戶信息。

iOS14 提供 Wifi 加密服務(wù),每次接入不同的 WiFi 使用的 mac 地址都不同。每過 24 小時,mac 地址還會更新一次。需要關(guān)注是否有使用用戶網(wǎng)絡(luò) mac 地址的服務(wù)。

用戶也可以自行選擇是否開啟 private Wi-Fi address:

剪切板
在 iOS14 中,讀取用戶剪切板的數(shù)據(jù)會彈出提示:

彈出提示的原因是使用
UIPasteboard 訪問用戶數(shù)據(jù),訪問以下數(shù)據(jù)都會彈出 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]?
如果應(yīng)用訪問剪切板僅僅用于判斷是否為URL格式,則 iOS14 新增了兩個 API 可以用于規(guī)避該提示,但只能用于判斷剪切板中是否有 URL,無法訪問甚至獲取剪貼板數(shù)據(jù):
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
}
如果應(yīng)用想直接訪問剪切板的數(shù)據(jù),暫時可能無法做到規(guī)避該提示。
相機和麥克風(fēng)
iOS14 中 App 使用相機和麥克風(fēng)時會有圖標(biāo)提示以及綠點和黃點提示,并且會顯示當(dāng)前是哪個 App 在使用此功能。我們無法控制是否顯示該提示。

會觸發(fā)錄音小黃點的代碼示例:
let recorder = try? AVAudioRecorder(url: fileURL, settings: [:])
recorder?.record()
觸發(fā)相機小綠點的代碼示例:
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默認(rèn)為淺灰色背景,直接設(shè)置背景色或者子視圖背景色為UIColor.clear,親測無效

old : ProbabilityCollegeHeaderView -> _UISystemBackgroundView -> _UITableViewHeaderFooterContentView
new : ProbabilityCollegeHeaderView -> _UISystemBackgroundView -> UIView -> _UITableViewHeaderFooterContentView
iOS14增加了backgroundConfiguration 配置方法,設(shè)置此圖層的背景樣式
if #available(iOS 14.0, *) {
self.backgroundConfiguration = UIBackgroundConfiguration.clear()
}
UITableViewCell
在 iOS14 中,UITableViewCell 中如果有直接添加在 cell 上的控件,也就是使用self.addSubview(_)方式添加的控件,會顯示在 contentView 的下層,contentView 會阻擋事件交互,使所有事件都響應(yīng)tableView:didSelectRowAtIndexPath:方法,如果 customView 存在交互事件將無法響應(yīng)。如果 contentView 設(shè)置了背景色,還會影響界面顯示.
UIDatePicker
iOS 14 中,UIDatePicker UI樣式更新了
@available(iOS 13.4, *)
open var preferredDatePickerStyle: UIDatePickerStyle

如果想使用原來的播輪樣式,需要設(shè)置:
datePicker.preferredDatePickerStyle = .wheels
Xcode12
xcode12默認(rèn)不支持模擬器架構(gòu),導(dǎo)致某些項目運行發(fā)生如下錯誤:
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

