1 訪問(wèn)相冊(cè)功能
1.1 配置相關(guān)
1.在Info.plist
文件中設(shè)置相冊(cè)權(quán)限
<key>NSPhotoLibraryAddUsageDescription</key>
<string>允許此權(quán)限才能使用添加照片功能</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>允許此權(quán)限才能使用相冊(cè)功能</string>
2.使用PHPhotoLibrary
的API和UI組件您朽,需要導(dǎo)入以下2個(gè)頭文件
import Photos
import PhotosUI
1.2 iOS 14
以后新新增選擇照片...
權(quán)限
1.2.1 PHAuthorizationStatus
為.limited
雅潭,每次冷啟動(dòng)都會(huì)彈窗提示
- 在
Info.plist
文件中設(shè)置PHPhotoLibraryPreventAutomaticLimitedAccessAlert
為YES
可關(guān)閉彈窗
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>
1.2.2 手動(dòng)觸發(fā)選擇更多照片彈窗
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
也可以在設(shè)置->照片
中編輯所選照片
1.3 獲取相冊(cè)權(quán)限和請(qǐng)求相冊(cè)權(quán)限
- iOS 14.0包括以上使用
@available(iOS 14, *)
open class func authorizationStatus(for accessLevel: PHAccessLevel) -> PHAuthorizationStatus
@available(iOS 14, *)
open class func requestAuthorization(for accessLevel: PHAccessLevel, handler: @escaping (PHAuthorizationStatus) -> Void)
相冊(cè)訪問(wèn)方式新增PHAccessLevel
-
addOnly
只讀 -
readWrite
讀寫(xiě)
PHAuthorizationStatus
權(quán)限新增.limited
骨坑,請(qǐng)求權(quán)限在readWrite
時(shí)生效
func checkPHAuthorization(_ handler: @escaping ((Bool) -> Void)) {
// 獲取相冊(cè)權(quán)限
let authStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
switch (authStatus) {
// 允許訪問(wèn)所有照片
case .authorized:
handler(true)
// 選擇照片...
case .limited:
handler(true)
// 首次彈窗權(quán)限未授權(quán)
case .notDetermined:
// 請(qǐng)求相冊(cè)權(quán)限
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
DispatchQueue.main.async {
switch status {
case .authorized, .limited:
handler(true)
default:
handler(false)
}
}
}
// 拒絕等
default:
handler(false)
}
}
- iOS 14.0以下使用
@available(iOS, introduced: 8, deprecated: 100000)
open class func authorizationStatus() -> PHAuthorizationStatus
@available(iOS, introduced: 8, deprecated: 100000)
open class func requestAuthorization(_ handler: @escaping (PHAuthorizationStatus) -> Void)
func checkPHAuthorization(_ handler: @escaping ((Bool) -> Void)) {
let authStatus = PHPhotoLibrary.authorizationStatus()
switch (authStatus) {
// 允許訪問(wèn)所有照片畔塔,選擇照片...
case .authorized:
handler(true)
case .notDetermined:
PHPhotoLibrary.requestAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
handler(true)
default:
handler(false)
}
}
}
default:
handler(false)
}
}
注意:
未設(shè)置PHAccessLevel
時(shí)郎笆,選擇照片...
的權(quán)限為.authorized
昌犹,設(shè)置了readWrite
時(shí)權(quán)限為. limited
1.4 單選糟需,使用系統(tǒng)API:UIImagePickerController
let imagePickerController = UIImagePickerController()
imagePickerController.modalPresentationStyle = .fullScreen
imagePickerController.delegate = self
// 是否允許編輯
imagePickerController.allowsEditing = true
需要遵守UINavigationControllerDelegate
和UIImagePickerControllerDelegate
協(xié)議
extension LITBImagePickerController: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
// 取消
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
// 選擇照片
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var fileImage: UIImage?
if picker.allowsEditing {
if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
fileImage = image
}
} else {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
fileImage = image
}
}
if let img = fileImage {
systemHandler?(img)
}
picker.dismiss(animated: true, completion: nil)
}
}
1.5 多選屉佳,多選UI需要自己實(shí)現(xiàn),相關(guān)API
1.5.1 獲取縮略圖
/// 獲取縮略圖
/// - Parameters:
/// - asset: PHAsset
/// - imageSize: 圖片大小
/// - completion: 獲取系統(tǒng)圖片
func getThumbnailWithAsset(asset: PHAsset, imageSize: CGSize, completion: @escaping ((UIImage?) -> Void)) {
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .fast
let scale = UIScreen.main.scale
let size = CGSize(width: imageSize.width * scale, height: imageSize.height * scale)
PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: requestOptions) { image, info in
completion(image)
}
}
1.5.2 獲取原圖
/// 獲取原圖
/// - Parameters:
/// - asset: PHAsset
/// - isSync: 開(kāi)啟同步執(zhí)行洲押,默認(rèn)是flase(異步執(zhí)行)
/// - progressHandler: 加載圖片進(jìn)度回調(diào)
/// - completion: 獲取圖片回調(diào)
func getOriginalPhotoWithAsset(asset: PHAsset, isSync: Bool = false, progressHandler: ((Double?, Error?) -> Void)?, completion: @escaping ((UIImage?) -> Void)) {
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .fast
requestOptions.isSynchronous = isSync
requestOptions.progressHandler = { (progress, error, stop, info) in
DispatchQueue.main.async {
progressHandler?(progress, error)
}
}
// 該方法是異步執(zhí)行武花,會(huì)回調(diào)多次,當(dāng)isSynchronous設(shè)為true時(shí)杈帐,deliveryMode屬性就會(huì)被忽略体箕,并被當(dāng)作.HighQualityFormat來(lái)處理。
PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: requestOptions) { image, info in
/*
info?[PHImageResultIsDegradedKey]
PHImageResultIsDegradedKey 1表示低質(zhì)量圖片挑童,0是高質(zhì)量圖片
設(shè)置isSynchronous為true時(shí)累铅,阻塞線程,直到圖片被準(zhǔn)備好或發(fā)生錯(cuò)誤時(shí)才會(huì)回調(diào)站叼,回調(diào)一次
*/
completion(image)
}
}
1.5.3 獲取相薄
/// 獲取相簿列表
func getAlbumList() -> [AlbumItem] {
var items: [AlbumItem] = []
// 獲取系統(tǒng)的相機(jī)相冊(cè)
let smartOptions = PHFetchOptions()
let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: smartOptions)
items.append(contentsOf: convertCollection(collection: smartAlbums))
// 獲取用戶創(chuàng)建的相冊(cè)
let userCustomCollections = PHFetchOptions()
let userCustomAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: userCustomCollections)
items.append(contentsOf: convertCollection(collection: userCustomAlbums))
return items
}
/// 轉(zhuǎn)化處理獲取到的相簿
private func convertCollection(collection: PHFetchResult<PHAssetCollection>) -> [AlbumItem] {
var items: [AlbumItem] = []
// 獲取轉(zhuǎn)化前相簿內(nèi)容的圖片
collection.enumerateObjects { object, index, stop in
let resultsOptions = PHFetchOptions()
// 按照創(chuàng)建時(shí)間争群,進(jìn)行倒序
resultsOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
resultsOptions.predicate = NSPredicate(format: "mediaType=%d", PHAssetMediaType.image.rawValue)
let assetsFetchResult = PHAsset.fetchAssets(in: object, options: resultsOptions)
// 過(guò)濾隱藏相冊(cè),最近刪除相冊(cè)(1000000201)
if object.assetCollectionSubtype == .smartAlbumAllHidden || object.assetCollectionSubtype.rawValue > 1000000000 { return }
if assetsFetchResult.count > 0 {
let item = AlbumItem(title: object.localizedTitle ?? "Unnamed", fetchResult: assetsFetchResult)
// 最近項(xiàng)目顯示在最前面
if object.assetCollectionSubtype == .smartAlbumUserLibrary {
items.insert(item, at: 0)
} else {
items.append(item)
}
}
}
return items
}
1.5.4 監(jiān)聽(tīng)相冊(cè)變化大年,PHAuthorizationStatus
為.limited
時(shí)需要監(jiān)聽(tīng)
class LITBSelectImagesViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 開(kāi)始監(jiān)聽(tīng)
PHPhotoLibrary.shared().register(self)
}
deinit {
// 結(jié)束監(jiān)聽(tīng)
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
}
extension LITBSelectImagesViewController: PHPhotoLibraryChangeObserver {
func photoLibraryDidChange(_ changeInstance: PHChange) {
// selectedFetchResult: PHFetchResult<PHAsset> 為當(dāng)前選中的相薄
guard let selectedFetchResult = controller.selectedFetchResult else { return }
DispatchQueue.main.async {
guard let changes = changeInstance.changeDetails(for: selectedFetchResult) else { return }
// 獲取更新的相冊(cè)换薄,更新UI
self.controller.getAllAssets(assets: changes.fetchResultAfterChanges) {
self.collectionView.reloadData()
}
}
}
}
2 使用PHPickerViewController
- 不需要設(shè)置相冊(cè)訪問(wèn)權(quán)限
- 不能編輯照片
- 不支持查看大圖
import UIKit
import PhotosUI
import Photos
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
@IBAction func onSingle(_ sender: Any) {
showPikcer(limitCount: 1)
}
@IBAction func onMultiple(_ sender: Any) {
showPikcer(limitCount: 9)
}
func showPikcer(limitCount: Int) {
var config = PHPickerConfiguration()
// 可選擇的資源數(shù)量,0表示不設(shè)限制翔试,默認(rèn)為1
config.selectionLimit = limitCount
// 可選擇的資源類型轻要,只顯示圖片(注:images包含livePhotos)
config.filter = .images
// 顯示livePhotos和視頻(注:livePhotos不包含images)
// config.filter = .any(of: [.livePhotos, .videos])
// 如果要獲取視頻,最好設(shè)置該屬性垦缅,避免系統(tǒng)對(duì)視頻進(jìn)行轉(zhuǎn)碼
config.preferredAssetRepresentationMode = .current
let pickerVC = PHPickerViewController(configuration: config)
pickerVC.delegate = self
present(pickerVC, animated: true)
}
}
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
// 關(guān)閉頁(yè)面
dismiss(animated: true)
// 取消選擇也會(huì)觸發(fā)代理方法冲泥,會(huì)返回空的results
guard !results.isEmpty else { return }
results.forEach { result in
// 判斷類型是否為UIImage
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
// 獲取圖片
result.itemProvider.loadObject(ofClass: UIImage.self) { itemProvider, error in
// 回調(diào)結(jié)果在異步線程,展示時(shí)需要切換到主線程
if let image = itemProvider as? UIImage {
DispatchQueue.main.async {
print("---:", image)
}
}
}
}
}
}
}
更多PHPickerViewController
參考資料:https://juejin.cn/post/6881513652176814093