简介:本文深入探讨Swift照片/视频选择器的核心实现方法,涵盖权限管理、UI构建及性能优化策略,为开发者提供系统化的解决方案。
在iOS应用开发中,媒体内容选择功能是社交类、电商类应用的标配需求。Swift语言凭借其类型安全、性能高效等特性,成为构建照片/视频选择器的理想选择。本文将从权限管理、UI设计、性能优化三个维度,系统阐述如何开发一个高效稳定的媒体选择器。
iOS系统要求应用必须明确请求相册访问权限。在Swift中,可通过PHPhotoLibrary.requestAuthorization()方法实现动态权限请求:
import Photosfunc checkPhotoLibraryPermission() {let status = PHPhotoLibrary.authorizationStatus()switch status {case .notDetermined:PHPhotoLibrary.requestAuthorization { status inDispatchQueue.main.async {self.handleAuthorization(status: status)}}case .restricted, .denied:showPermissionDeniedAlert()case .authorized, .limited:presentMediaPicker()@unknown default:break}}
此实现包含三个关键点:状态预检查、异步回调处理、UI线程更新。对于.limited状态(iOS 14+新增的精选相册权限),需要特别处理用户部分授权的情况。
建议将权限状态存储在UserDefaults中,避免重复请求:
let hasRequestedPhotoPermission = UserDefaults.standard.bool(forKey: "hasRequestedPhotoPermission")if !hasRequestedPhotoPermission {checkPhotoLibraryPermission()UserDefaults.standard.set(true, forKey: "hasRequestedPhotoPermission")}
使用Photos框架的PHAsset和PHFetchResult构建数据模型:
func fetchAllPhotos() -> PHFetchResult<PHAsset> {let options = PHFetchOptions()options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]options.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)return PHAsset.fetchAssets(with: options)}
对于视频支持,只需修改mediaType为.video。实际开发中建议使用PHAssetCollection进行相册分组,提升用户体验。
推荐采用UICollectionView实现网格布局,关键实现点包括:
UICollectionViewLayout实现动态间距PHImageManager的requestImage方法进行异步加载
class MediaPickerCell: UICollectionViewCell {var asset: PHAsset? {didSet {guard let asset = asset else { return }let options = PHImageRequestOptions()options.isSynchronous = falseoptions.deliveryMode = .highQualityFormatPHImageManager.default().requestImage(for: asset,targetSize: CGSize(width: 200, height: 200),contentMode: .aspectFill,options: options) { image, _ inDispatchQueue.main.async {self.imageView.image = image}}}}}
通过维护选中状态数组实现多选:
class MediaPickerViewController: UIViewController {var selectedAssets = Set<PHAsset>()func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {guard let asset = fetchResult?.object(at: indexPath.item) else { return }if selectedAssets.contains(asset) {selectedAssets.remove(asset)} else {if selectedAssets.count < maxSelectionCount {selectedAssets.insert(asset)} else {showMaxSelectionAlert()}}collectionView.reloadItems(at: [indexPath])}}
PHCachingImageManager替代默认管理器targetSize避免内存浪费
let imageManager = PHCachingImageManager()var previousPreheatRect = CGRect.zerofunc updateCache() {// 计算可见区域let visibleRect = CGRect(origin: collectionView.contentOffset,size: collectionView.bounds.size)// 更新预加载区域// ...实现细节imageManager.stopCachingImagesForAllAssets()imageManager.startCachingImages(for: assetsToCache,targetSize: PHImageManagerMaximumSize,contentMode: .aspectFill,options: nil)}
对于视频选择场景,建议使用PHAssetResource获取视频URL:
func getVideoURL(for asset: PHAsset, completion: @escaping (URL?) -> Void) {let options = PHVideoRequestOptions()options.version = .originaloptions.isNetworkAccessAllowed = truePHImageManager.default().requestAVAsset(forVideo: asset, options: options) { asset, _, _ inif let avAsset = asset as? AVURLAsset {DispatchQueue.main.async {completion(avAsset.url)}} else {completion(nil)}}}
通过UIImagePickerController或AVFoundation实现拍照/录像功能:
func presentCustomCamera() {let picker = UIImagePickerController()picker.sourceType = .camerapicker.mediaTypes = ["public.image", "public.movie"]picker.delegate = selfpresent(picker, animated: true)}
对于需要上传到云端的场景,建议:
PHAssetResource获取原始数据
func uploadAsset(_ asset: PHAsset, toCloud storage: CloudStorage) {let options = PHAssetResourceRequestOptions()options.isNetworkAccessAllowed = truePHAssetResourceManager.default().requestData(for: asset.resources!.first!, options: options) { data instorage.uploadData(data, progress: { progress in// 更新UI进度})}}
Instruments的Allocations工具检测内存泄漏通过系统化的架构设计和性能优化,开发者可以构建出既满足功能需求又具备良好用户体验的媒体选择器。实际开发中建议采用模块化设计,将权限管理、数据获取、UI展示等模块解耦,便于后期维护和功能扩展。