Photos框架 - 自定义媒体资源选择器(数据部分)

news2024/9/28 11:13:22

引言

在iOS开发中,系统已经为我们提供了多种便捷的媒体资源选择方式,如UIImagePickerController和PHPickerViewController。这些方式不仅使用方便、界面友好,而且我们完全不需要担心性能和稳定性问题,因为它们是由系统提供的,经过充分测试和优化。

然而,在实际开发过程中,为了使我们的APP更加独特,界面更加新颖,设计团队往往会提出个性化的媒体资源选择页面的需求。这时候,我们就需要放弃系统提供的方案,转而创建自定义的媒体资源选择器。本文将介绍如何使用Photos框架来自定义媒体资源选择器,以满足特定的设计和功能需求。

AssetsLibrary -> Photos

在iOS 8之前,开发者主要使用AssetsLibrary框架来访问和管理用户的照片和视频。然而,从iOS 8开始,Apple引入了全新的Photos框架,并逐步弃用AssetsLibrary

自iOS 9起,AssetsLibrary被正式标记为弃用,Apple强烈建议开发者迁移到Photos框架。Photos框架不仅提供了更高效的性能和更丰富的功能,还为开发者提供了更强大的工具来管理和操作用户的媒体资源。

通过Photos框架,开发者可以更轻松地获取媒体元数据、编辑照片、创建自定义相册,以及实现更多自定义功能。这一过渡标志着iOS媒体管理能力的重大提升,为开发者提供了更广泛的可能性和更强大的控制力。

下面我们就使用Photos框架,来创建一个初级的媒体资源选择器,之后的博客中,我们再不停的来完善它的功能。

创建媒体选择器

我打算把它分成数据和UI两部分来实现这个媒体选择器,本篇博客我们就先从数据部分说起。

媒体数据读取

1.创建配置信息

在媒体资源读取时有很多数据我们可以进行任意配置,比如读取的媒体类型、读取的视频最大时长、获取缩略图尺寸,图片缓存个数等等,为此我们创建了一个名为PHMediaConfig的类,代码如下:

import UIKit
import Photos

enum PHMediaType {
    /// 图片
    case image
    /// 视频
    case video
    /// 图片和视频
    case all
}

class PHMediaConfig: NSObject {
    /// 获取资源类型(默认视频和图片)
    var mediaType: PHMediaType = .all
    /// 缩略图缓存数量
    var thumbnailCacheCount: Int = 40
    /// 大图缓存数量
    var originalImageCacheCount: Int = 10
    /// 获取视频的时长最大值
    var videoMaxDuration: TimeInterval = 60
    /// 是否直接加载原图
    var isLoadOriginalImage: Bool = false
    /// 可选图片最大数量
    var maxSelectedImageCount: Int = 9
    /// 缩略图尺寸
    var thumbnailSize: CGSize = CGSize(width: 200, height: 200)
    
}

里定义了很多配置信息,并且也都设置了初始值。

2.创建媒体资源管理类

创建一个继承自NSObjct名为PHMediaManager的类,用来读取媒体资源数据,获取缩略,原图等等一切和数据相关的内容,并通过初始化方法传入配置信息,代码如下:

class PHMediaManager: NSObject {
    
    /// 缩略图缓存
    private var thumbnailCache = NSCache<NSString, UIImage>()
    /// 原图缓存
    private var originalImageCache = NSCache<NSString, UIImage>()
    /// 配置
    private var config: PHMediaConfig!
    
    
    init(config: PHMediaConfig = PHMediaConfig()) {
        super.init()
        self.config = config
        thumbnailCache.countLimit = config.thumbnailCacheCount
        originalImageCache.countLimit = config.originalImageCacheCount
    }
    ....
}

除此之外,我们还定义了两个缓存表,稍后的代码中会使用到它们。

3.获取媒体库权限

苹果对隐私权限的申请非常重视,所以在获取媒体资源前一定要检查权限和申请权限,并给用户友好的提示,包括infoplist文件内的文案也需要认真填写,表明申请权限的用途。

检查和申请权限的代码如下:

    /// 查看相册权限
    func checkPhotoLibraryAuthorization() -> Bool {
        let status = PHPhotoLibrary.authorizationStatus()
        if status == .authorized {
            return true
        } else {
            return false
        }
    }
    /// 查看并获取相册权限
    /// - Parameter completion: 回调
    func requestPhotoLibraryAuthorization(completion: @escaping (Bool) -> Void) {
        PHPhotoLibrary.requestAuthorization { status in
            if status == .authorized {
                print("获取相册权限成功")
                completion(true)
            } else {
                print("获取相册权限失败")
                completion(false)
            }
        }
    }
4.获取媒体资源

权限申请通过后,就可以开始获取媒体资源了,这时候有两个方案可以供我们选择:

方案一:在读取资源时,通过PHAsset直接读取缩略图构建模型数组。

如果采用方案1的话,在渲染列表时,我们就可以直接使用UIImage进行渲染,页面反应很快,用户体验会很好。

但是呢预先加载所有的缩略图这样会占用很大的内存,尤其是相册资源比较多的情况甚至可能会导致崩溃,这样的话我们就需要手动控制一次加载资源的数量。

方案二:在读取资源时,只保存PHAsset。

这个方案呢,我们到不需要考虑内存的问题,因为只有在现实的时候才会加载缩略图,显示完成之后就会自动被释放,但是这就会有新的问题,每次图片都是重新加载,可能会使得页面不流畅,影响用户体验。这样的话我们就需要自己来创建和管理缓存来提升用户体验。

我们来采取方案二 + 自定义缓存的方式来读取媒体资源,这样的话我们的数据模型只需要保存PHAsset就可以了,我们先来看一下自定义数据模型的代码:

import UIKit
import Photos

class PHMediaModel: NSObject {
    
    /// 资源
    var asset: PHAsset?
    /// 是否选中
    var isSelected: Bool = false
    /// 资源标识
    var identifier: String?
    /// 类型
    var mediaType: PHAssetMediaType {
        get {
            return asset?.mediaType ?? .unknown
        }
    }
    /// 视频时长
    var videoDuration: TimeInterval {
        get {
            return asset?.duration ?? 0
        }
    }
}

除了PHAsset以外,还定义了一些选中状态已经视频时长等数据,稍后我们会使用到它们。

下面就开始读取媒体资源数据,构建自定义数据模型:

    /// 获取相册资源
    /// - Parameters:
    func fetchLocalAlbums(completion: @escaping ([PHMediaModel]) -> Void) {
        self.fetchLocalAlbums(type: config.mediaType, completion: completion)
    }
    
    
    /// 获取本地相册资源
    /// - Parameters:
    ///  - type: 类型
    ///  - completion: 回调
    private func fetchLocalAlbums(type: PHMediaType, completion: @escaping ([PHMediaModel]) -> Void) {
        let options = PHFetchOptions()
        options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        if type == .image {
            options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)
        } else if type == .video {
            options.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue)
        } else {
            options.predicate = NSPredicate(format: "mediaType = %d || mediaType = %d", PHAssetMediaType.image.rawValue, PHAssetMediaType.video.rawValue)
        }
        let fetchResult = PHAsset.fetchAssets(with: options)
        var assets = [PHMediaModel]()
        fetchResult.enumerateObjects { asset, index, stop in
            if asset.mediaType == .image {
                let model = PHMediaModel()
                model.asset = asset
                model.identifier = asset.localIdentifier
                assets.append(model)
            } else if asset.mediaType == .video {
                if asset.duration <= self.config.videoMaxDuration {
                    let model = PHMediaModel()
                    model.asset = asset
                    model.identifier = asset.localIdentifier
                    assets.append(model)
                }
            }
           
        }
        completion(assets)
    }

通常情况下,我们只关心图片类型和视频类型的数据,并且根据视频的时长还进行了进一步的过滤。

5.获取资源缩略图

另外我们还单独定义了一个读取资源缩略图的方法,并且在这个方法里面使用了缩略图缓存,代码如下:

    /// 获取缩略图
    /// - Parameters:
    /// - asset: 资源
    /// - size: 尺寸
    /// - completion: 回调
    func fetchThumbnail(asset: PHAsset, size: CGSize? = nil, completion: @escaping (UIImage?) -> Void) {
        if let size = size {
            config.thumbnailSize = size
        }
        let key = asset.localIdentifier as NSString
        if let image = thumbnailCache.object(forKey: key) {
            completion(image)
        } else {
            let options = PHImageRequestOptions()
            options.isSynchronous = false
            options.resizeMode = .fast
            options.deliveryMode = .opportunistic
            options.isNetworkAccessAllowed = true
            PHImageManager.default().requestImage(for: asset, targetSize: config.thumbnailSize, contentMode: .aspectFill, options: options) { image, info in
                if let image = image {
                    self.thumbnailCache.setObject(image, forKey: key)
                    completion(image)
                } else {
                    completion(nil)
                }
            }
        }
    }
6.获取图片资源原图

除了缩略图之外,还需要读取图片原图用来图片单张预览,代码如下:

    /// 获取原图
    /// - Parameters:
    /// - asset: 资源
    /// - completion: 回调
    func fetchOriginalImage(asset: PHAsset, completion: @escaping (UIImage?) -> Void) {
        let key = asset.localIdentifier as NSString
        if let image = originalImageCache.object(forKey: key) {
            completion(image)
        } else {
            let options = PHImageRequestOptions()
            options.isSynchronous = false
            options.resizeMode = .fast
            options.deliveryMode = .opportunistic
            options.isNetworkAccessAllowed = true
            PHImageManager.default().requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFill, options: options) { image, info in
                if let image = image {
                    self.originalImageCache.setObject(image, forKey: key)
                    completion(image)
                } else {
                    completion(nil)
                }
            }
        }
    }
7.获取视频原数据

获取完图片数据,视频也需要原始的预览数据,毕竟我们上传时不能只上传一个缩略图,代码如下:

    /// 获取视频原数据
    /// - Parameters:
    /// - asset: 资源
    /// - completion: 回调
    func fetchVideoData(asset: PHAsset, completion: @escaping (Data?) -> Void) {
        let options = PHVideoRequestOptions()
        options.isNetworkAccessAllowed = true
        PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { avAsset, audioMix, info in
            if let urlAsset = avAsset as? AVURLAsset {
                do {
                    let data = try Data(contentsOf: urlAsset.url)
                    completion(data)
                } catch {
                    completion(nil)
                }
            } else {
                completion(nil)
            }
        }
    }

结语

我们从自定义个媒体选择器入手,来探讨一些Photos框架的用法,本篇博客我们主要介绍了使用Photos获取相册权限,读取媒体数据,以及如何配置读取的数据参数。

下一篇博客我们将开始使用这些数据来构建一个媒体资源选择器的UI页面。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1949057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于扩散的生成模型的语音增强和去噪

第二章 目标说话人提取之《Speech Enhancement and Dereverberation with Diffusion-based Generative Models》 文章目录 前言一、任务二、动机三、挑战四、方法1.方法:基于分数的语音增强生成模型(sgmse)2.网络结构 五、实验评价1.数据集2.采样器设置和评价指标3.基线模型4.评…

godot新建项目及设置外部编辑器为vscode

一、新建项目 初次打开界面如下所示&#xff0c;点击取消按钮先关闭掉默认弹出的框 点击①新建弹出中间的弹窗②中填入项目的名称 ③中设置项目的存储路径&#xff0c;点击箭头所指浏览按钮&#xff0c;会弹出如下所示窗口 根据图中所示可以选择或新建自己的游戏存储路径&…

音视频开发之旅(85)- 图像分类-VGG模型解析

目录 1. VGG解决的问题 2. 网络结构和参数 3. pytorch搭建vgg 4.flower_photos分类任务实践 5.资料 一、VGG解决的问题 论文链接&#xff1a;https://arxiv.org/pdf/1409.1556 在VGG之前&#xff0c;大多数深度学习模型相对较浅&#xff0c;比如下面的AlexNet(5层卷积和3…

记录阿里云部署gitlab

登录阿里云&#xff1a; 阿里云登录 - 欢迎登录阿里云&#xff0c;安全稳定的云计算服务平台 选择自己的ECS实例。我的实例是 使用VNC登录&#xff1a;输入用户名和密码 安装所需的依赖包&#xff1a; sudo yum install -y yum-utils device-mapper-persistent-data lvm2 添…

Git(分布式版本控制系统)(fourteen day)

一、分布式版本控制系统 1、Git概述 Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变更&#xff0c;它由Linux、torvalds创建的&#xff0c;最初被设计用于Linux内核的开发。Git允许开发人员跟踪和管理代码的版本&#xff0c;并且可以在不同的开发人员之间进行…

货架管理a

路由->vue的el标签->Api->call方法里calljs的api接口->数据声明const xxxData-> 编辑按钮:点击跳出页面并把这一行的数据给到表单formDataba2 保存按钮:formDataba2改过的数据->xxApi发送->查询Api 跳转仓库:把tableData.value数据清空->callXxxAp…

华为云依赖引入错误

问题&#xff1a;记录一次项目加在华为云依赖错误&#xff0c;如下&#xff1a; 错误信息&#xff1a;Could not find artifact com.huawei.storage:esdk-obs-java:pom:3.1.2.1 in bintray-qcloud-maven-repo (https://dl.bintray.com/qcloud/maven-repo/) 找到本地仓库&#…

mac环境Qt Creator报错:Warning: You are changing a read-only file.

mac环境Qt Creator报错&#xff1a; Warning: You are changing a read-only file. 权限许可 文件权限问题 修改文件夹权限的基本语法&#xff1a; 打开终端&#xff1a;打开 macOS 中的终端应用程序。 sudo chmod -R permissions folder_pathchmod 是改变文件或文件夹权限…

虚拟机之ip配置,ssh连接到虚拟机

右边是我的虚拟机&#xff0c;左边是我使用vscode来连接&#xff08;终端也可以。然后注意vscode配置后点一下刷新&#xff0c;不会自动刷新的QA&#xff09;&#xff08;吐槽一下&#xff0c;虚拟机都不能复制内容呢&#xff0c;确实仿真&#xff0c;centos仿真就是因为没有图…

基于深度学习网络的USB摄像头实时视频采集与水果识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 将usb摄像头对准一个播放不同水果图片的显示器&#xff0c;然后进行识别&#xff0c;识别结果如下&#xff1a; 本课题中…

爬取贴吧的标题和链接

免责声明 感谢您学习本爬虫学习Demo。在使用本Demo之前&#xff0c;请仔细阅读以下免责声明&#xff1a; 学习和研究目的&#xff1a;本爬虫Demo仅供学习和研究使用。用户不得将其用于任何商业用途或其他未经授权的行为。合法性&#xff1a;用户在使用本Demo时&#xff0c;应确…

微信小程序-自定义tabBar

通过官网给出的示例自己实现了自定义的tabBar&#xff0c;但结果发现 无法监听页面生命周期函数 结语&#xff1a;原想的是实现不一样的效果&#xff08;如下&#xff09; 故尝试了自定义tabBar&#xff0c;虽然做出来了&#xff0c;但也发现这个做法存在不足&#xff1a; 在…

算法竞赛数据生成及使用Sublime对拍

写在前面&#xff1a;最近几天看蒋老师直接使用了Sublime中的FastOlympicCode插件进行了对拍&#xff0c;出于兴趣来学习一下&#xff0c;关于插件的配置已经有很多大佬讲过啦&#xff0c;这里不再赘述。数据生成的代码我会放到最后&#xff0c;包括生成数组、区间、树、图。 …

2024安全大模型技术与市场研究报告

大模型驱动的AIGC引发技术革命&#xff0c;国资委强调国企需加大AI投入。大模型解决网络安全行业攻防不对等问题&#xff0c;国内外企业纷纷推出基于大模型的网络安全产品&#xff0c;AI将改变网络安全产品格局。 自 2022 年底开始&#xff0c;以 LLM(大语言模型&#xff0c;简…

CSS(九)——CSS 轮廓(outline)

CSS 轮廓&#xff08;outline&#xff09; 轮廓&#xff08;outline&#xff09;是绘制于元素周围的一条线&#xff0c;位于边框边缘的外围&#xff0c;可起到突出元素的作用。 轮廓&#xff08;outline&#xff09;属性指定元素轮廓的样式、颜色和宽度。 让我们用一个图来看…

使用Claude 3.5 Sonnet和Stable Diffusion XL:如何通过Amazon Bedrock不断优化图像生成直到满足需求

在Amazon Bedrock的AI模型中&#xff0c;Anthropic Claude 3系列现在新增了图像识别功能。特别是最新的Anthropic Claude 3.5 Sonnet&#xff0c;图像识别能力得到了显著提升。我进行了一些简单的试验和比较&#xff0c;深入探索了这些Claude模型在OCR&#xff08;光学字符识别…

边界网关IPSEC VPN实验

拓扑&#xff1a; 实验要求&#xff1a;通过IPSEC VPN能够使PC2通过网络访问PC3 将整个路线分为三段 IPSEC配置在FW1和FW2上&#xff0c;在FW1与FW2之间建立隧道&#xff0c;能够传递IKE&#xff08;UDP500&#xff09;和ESP数据包&#xff0c;然后在FW1与PC2之间能够流通数据…

C# Nmodbus,EasyModbusTCP读写操作

Nmodbus读写 两个Button控件分别为 读取和写入 分别使用控件的点击方法 ①引用第三方《NModbus4》2.1.0版本 全局 public SerialPort port new SerialPort("COM2", 9600, Parity.None, 8, (StopBits)1); ModbusSerialMaster master; public Form1() port.Open();…

河道高效治理新策略:视频AI智能监控如何助力河污防治

一、背景与现状 随着城市化进程的加快&#xff0c;河道污染问题日益严重&#xff0c;对生态环境和居民生活造成了严重影响。为了有效治理河道污染&#xff0c;提高河道管理的智能化水平&#xff0c;TSINGSEE青犀提出了一套河污治理视频智能分析及管理方案。方案依托先进的视频…

LeetCode三道模式匹配算法题(同构字符串 一串三~)

模式匹配 好像回到小学语文课堂~ &#x1f923; 老师&#xff1a;ABB式 大家能想出哪些呀~&#x1f60a; 我&#xff1a;绿油油~&#x1f601; 金灿灿~&#x1f601; 哈哈哈哈哈 好啦&#xff0c;来看算法题~ LeetCode 205 同构字符串 哈哈哈哈 怎么判断 绿油油 和 金灿灿 是…