iOS 相册的使用

2023-10-16  本文已影响0人  Minoz_min

1 访问相册功能

1.1 配置相关

1.在Info.plist文件中设置相册权限

<key>NSPhotoLibraryAddUsageDescription</key>
<string>允许此权限才能使用添加照片功能</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>允许此权限才能使用相册功能</string>

2.使用PHPhotoLibrary 的API和UI组件,需要导入以下2个头文件

import Photos
import PhotosUI

1.2 iOS 14以后新新增选择照片...权限

image.png

1.2.1 PHAuthorizationStatus.limited,每次冷启动都会弹窗提示

image.png
<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
<true/>

1.2.2 手动触发选择更多照片弹窗

PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: self)
选择照片....jpg

也可以在设置->照片中编辑所选照片

设置->照片.jpg

1.3 获取相册权限和请求相册权限

  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)

相册访问方式新增PHAccessLevel

PHAuthorizationStatus 权限新增.limited,请求权限在readWrite时生效

     func checkPHAuthorization(_ handler: @escaping ((Bool) -> Void)) {
        // 获取相册权限
        let authStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
        
        switch (authStatus) {
        // 允许访问所有照片
        case .authorized:
            handler(true)
        // 选择照片...
        case .limited:
            handler(true)
        // 首次弹窗权限未授权
        case .notDetermined:
            // 请求相册权限
            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) {
        // 允许访问所有照片,选择照片...
        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)
        }
    }

注意:
未设置PHAccessLevel时,选择照片...的权限为.authorized,设置了readWrite时权限为. limited

1.4 单选,使用系统API:UIImagePickerController

let imagePickerController = UIImagePickerController()
imagePickerController.modalPresentationStyle = .fullScreen
imagePickerController.delegate = self
// 是否允许编辑
imagePickerController.allowsEditing = true

需要遵守UINavigationControllerDelegateUIImagePickerControllerDelegate协议

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需要自己实现,相关API

1.5.1 获取缩略图

    /// 获取缩略图
    /// - Parameters:
    ///   - asset: PHAsset
    ///   - imageSize: 图片大小
    ///   - completion: 获取系统图片
    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: 开启同步执行,默认是flase(异步执行)
    ///   - progressHandler: 加载图片进度回调
    ///   - completion: 获取图片回调
    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)
            }
        }
        
        // 该方法是异步执行,会回调多次,当isSynchronous设为true时,deliveryMode属性就会被忽略,并被当作.HighQualityFormat来处理。
        PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: requestOptions) { image, info in
            /*
             info?[PHImageResultIsDegradedKey]
             
             PHImageResultIsDegradedKey 1表示低质量图片,0是高质量图片
             
             设置isSynchronous为true时,阻塞线程,直到图片被准备好或发生错误时才会回调,回调一次
             */
            completion(image)
        }
    }

1.5.3 获取相薄

    /// 获取相簿列表
    func getAlbumList() -> [AlbumItem] {
        var items: [AlbumItem] = []
        
        // 获取系统的相机相册
        let smartOptions = PHFetchOptions()
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: smartOptions)
        items.append(contentsOf: convertCollection(collection: smartAlbums))
        
        // 获取用户创建的相册
        let userCustomCollections = PHFetchOptions()
        let userCustomAlbums = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: userCustomCollections)
        items.append(contentsOf: convertCollection(collection: userCustomAlbums))
        
        return items
    }
    
    /// 转化处理获取到的相簿
    private func convertCollection(collection: PHFetchResult<PHAssetCollection>) -> [AlbumItem] {
        var items: [AlbumItem] = []
        
        // 获取转化前相簿内容的图片
        collection.enumerateObjects { object, index, stop in
            let resultsOptions = PHFetchOptions()
            // 按照创建时间,进行倒序
            resultsOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
            resultsOptions.predicate = NSPredicate(format: "mediaType=%d", PHAssetMediaType.image.rawValue)
            
            let assetsFetchResult = PHAsset.fetchAssets(in: object, options: resultsOptions)
            
            // 过滤隐藏相册,最近删除相册(1000000201)
            if object.assetCollectionSubtype == .smartAlbumAllHidden || object.assetCollectionSubtype.rawValue > 1000000000 { return }
            
            if assetsFetchResult.count > 0 {
                let item = AlbumItem(title: object.localizedTitle ?? "Unnamed", fetchResult: assetsFetchResult)
                
                // 最近项目显示在最前面
                if object.assetCollectionSubtype == .smartAlbumUserLibrary {
                    items.insert(item, at: 0)
                } else {
                    items.append(item)
                }
            }
        }
        
        return items
    }

1.5.4 监听相册变化,PHAuthorizationStatus.limited时需要监听

class LITBSelectImagesViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 开始监听
        PHPhotoLibrary.shared().register(self)
    }
    
    deinit {
        // 结束监听
        PHPhotoLibrary.shared().unregisterChangeObserver(self)
    }
}

extension LITBSelectImagesViewController: PHPhotoLibraryChangeObserver {
    func photoLibraryDidChange(_ changeInstance: PHChange) {
        // selectedFetchResult: PHFetchResult<PHAsset> 为当前选中的相薄
        guard let selectedFetchResult = controller.selectedFetchResult else { return }
        
        DispatchQueue.main.async {
            guard let changes = changeInstance.changeDetails(for: selectedFetchResult) else { return }
            // 获取更新的相册,更新UI
            self.controller.getAllAssets(assets: changes.fetchResultAfterChanges) {
                self.collectionView.reloadData()
            }
        }
    }
}

2 使用PHPickerViewController

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()
        // 可选择的资源数量,0表示不设限制,默认为1
        config.selectionLimit = limitCount
        // 可选择的资源类型,只显示图片(注:images包含livePhotos)
        config.filter = .images
        // 显示livePhotos和视频(注:livePhotos不包含images)
//        config.filter = .any(of: [.livePhotos, .videos])
        // 如果要获取视频,最好设置该属性,避免系统对视频进行转码
        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]) {
        // 关闭页面
        dismiss(animated: true)
        
        // 取消选择也会触发代理方法,会返回空的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
                    // 回调结果在异步线程,展示时需要切换到主线程
                    if let image = itemProvider as? UIImage {
                        DispatchQueue.main.async {
                            print("---:", image)
                        }
                    }
                }
            }
        }
    }
}
最多选择9张照片.jpg

更多PHPickerViewController参考资料:https://juejin.cn/post/6881513652176814093

上一篇下一篇

猜你喜欢

热点阅读