iOS 相冊(cè)的使用

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)限

image.png

1.2.1 PHAuthorizationStatus.limited雅潭,每次冷啟動(dòng)都會(huì)彈窗提示

image.png

  • Info.plist文件中設(shè)置PHPhotoLibraryPreventAutomaticLimitedAccessAlertYES可關(guān)閉彈窗
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>

1.2.2 手動(dòng)觸發(fā)選擇更多照片彈窗

PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
選擇照片....jpg

也可以在設(shè)置->照片中編輯所選照片

設(shè)置->照片.jpg

1.3 獲取相冊(cè)權(quán)限和請(qǐng)求相冊(cè)權(quán)限

  1. 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)
        }
    }
  1. 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

需要遵守UINavigationControllerDelegateUIImagePickerControllerDelegate協(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)
                        }
                    }
                }
            }
        }
    }
}
最多選擇9張照片.jpg

更多PHPickerViewController參考資料:https://juejin.cn/post/6881513652176814093

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市凡恍,隨后出現(xiàn)的幾起案子志秃,更是在濱河造成了極大的恐慌,老刑警劉巖嚼酝,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浮还,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡闽巩,警方通過(guò)查閱死者的電腦和手機(jī)钧舌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)涎跨,“玉大人洼冻,你說(shuō)我怎么就攤上這事∮绾埽” “怎么了撞牢?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叔营。 經(jīng)常有香客問(wèn)我普泡,道長(zhǎng),這世上最難降的妖魔是什么审编? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮歧匈,結(jié)果婚禮上垒酬,老公的妹妹穿的比我還像新娘。我一直安慰自己件炉,他們只是感情好勘究,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著斟冕,像睡著了一般口糕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磕蛇,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天景描,我揣著相機(jī)與錄音,去河邊找鬼秀撇。 笑死超棺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呵燕。 我是一名探鬼主播棠绘,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了氧苍?” 一聲冷哼從身側(cè)響起夜矗,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎让虐,沒(méi)想到半個(gè)月后紊撕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澄干,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年逛揩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片麸俘。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辩稽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出从媚,到底是詐尸還是另有隱情逞泄,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布拜效,位于F島的核電站喷众,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏紧憾。R本人自食惡果不足惜到千,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赴穗。 院中可真熱鬧憔四,春花似錦、人聲如沸般眉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)甸赃。三九已至柿汛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間埠对,已是汗流浹背络断。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留项玛,地道東北人妓羊。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像稍计,于是被迫代替她去往敵國(guó)和親躁绸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容