Photos框架 - 自定义媒体选择器(UI列表)

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

关于自定义媒体选择器上一篇博客我们已经介绍了使用Photos获取媒体资源数据和处理媒体资源数据,有了数据,UI的实现就比较灵活了,我就以上面的设计样式为例,然后把重点放到底部图片和视频的选择区域上。

创建媒体选择器

本篇博客我们就来讨论一下媒体资源选择器UI部分的实现,先来实现一下简单的选中和取消选中以及获取选中结果的功能。

选择器UI实现

由于UI的功能都很基础我们就将它分成列表和预览两大部分来处理。

列表部分
1.创建视图控制器

首先继承自UIViewController创建了一个名为PHMediaPickerViewController的类当做列表的视图控制器,为视图控制器定义列表,媒体资源管理类,已经选中的资源列表等信息,代码如下:

class PHMediaPickerViewController: UIViewController {/// 列表private var collectionView: UICollectionView!/// 媒体资源管理类private var mediaManager:PHMediaManager!/// 媒体资源管理类private var mediaManager:PHMediaManager!/// 操作数据源private let actionArray = ["photo","video"]/// 完成点击回调var doneBlock: (([PHMediaModel]) -> Void)?override func viewDidLoad() {super.viewDidLoad()initData()addCollectionView()requestPhotoLibraryAuthorization()}....
}
2.初始化数据

根据默认的配置信息来初始化媒体资源管理器。

    func initData() {let config = PHMediaConfig()mediaManager = PHMediaManager(config: config)}
3.创建列表

初始化列表,我们设置为4列的列表,列间间距和行间距都为2.0。并注册展示媒体资源类型的cell和展示操作相机类型的cell,代码如下:

    /// 添加列表func addCollectionView()  {let layout = UICollectionViewFlowLayout()let itemWidth = (CS_SCREENWIDTH - (2.0 * 3)) / 4.0layout.itemSize = CGSize(width: itemWidth, height: itemWidth)layout.minimumLineSpacing = 2.0layout.minimumInteritemSpacing = 0.0collectionView = UICollectionView(frame: CGRect(x: 0, y: cs_navigationBarHeight, width: CS_SCREENWIDTH, height: CS_SCREENHIGHT - cs_navigationBarHeight), collectionViewLayout: layout)collectionView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: cs_bottomInset, right: 0.0)collectionView.delegate = selfcollectionView.dataSource = selfself.view.addSubview(collectionView)// 注册 媒体资源类型cellcollectionView.register(PHMediaPickerCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPickerCell.self))// 注册 操作cellcollectionView.register(PHMediaPickerOperateCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPickerOperateCell.self))}
4.请求媒体资源数据

在请求媒体资源前记得先检查权限,获取到权限之后开始请求媒体数据并添加到列表中回到主线程刷线,代码如下:

    func requestPhotoLibraryAuthorization() {mediaManager.requestPhotoLibraryAuthorization {[weak self] (isAuthorized) inguard let self = self else { return }if isAuthorized {self.requstMediaData()} else {print("没有权限")}}}func requstMediaData() {mediaManager.fetchLocalAlbums {[weak self] (mediaModels) inguard let self = self else { return }DispatchQueue.main.async {self.collectionView.reloadData()}}}
5.根据数据渲染列表

实现代理方法,根据数据类型来渲染列表的数据,代码如下:

  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return actionArray.count + mediaManager.displayMediaModels.count}func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {if indexPath.row < actionArray.count {return self.collectionView(collectionView, cellForItemAt: indexPath, action: actionArray[indexPath.row])} else {return self.collectionView(collectionView, cellForItemAt: indexPath, mediaModel: mediaManager.displayMediaModels[indexPath.row - actionArray.count])}}private func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath,action:String) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPickerOperateCell.self), for: indexPath) as! PHMediaPickerOperateCellcell.type = actionreturn cell}private func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath,mediaModel:PHMediaModel) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPickerCell.self), for: indexPath) as! PHMediaPickerCelllet index = mediaManager.selectedMediaModels.firstIndex(of: mediaModel) ?? -1cell.index = indexcell.renderData(mediaModel: mediaModel, mediaManager: mediaManager)cell.selectTagTouchBlock = { [weak self] mediaModel inself?.touchMediaModel(mediaModel: mediaModel)}return cell}

PHMediaPickerOperateCell类型的Item就只使用制定图片渲染即可,而PHMediaPickerCell的item相对而言元素需要多一些,选中状态以及视频的时长等等,代码如下:

class PHMediaPickerCell: UICollectionViewCell {/// 图片private var imageView = UIImageView()/// 选中标签private var selectTag = UILabel()/// 播放按钮private var playButton = UIButton()/// 资源模型private var mediaModel: PHMediaModel?/// 资源管管理类private var mediaManager: PHMediaManager?........../// 渲染数据func renderData(mediaModel: PHMediaModel, mediaManager: PHMediaManager) {self.mediaModel = mediaModelself.mediaManager = mediaManagerplayButton.isHidden = mediaModel.mediaType != .videolet duration = Int(mediaModel.videoDuration)let title = secondsToHourMinuteSecond(seconds: duration)playButton.setTitle(title, for: .normal)self.mediaManager?.fetchThumbnail(asset: mediaModel.asset!, completion: {[weak self] (image) inguard let self = self else { return }self.imageView.image = image})}}

我们省略了一些关于创建UI和布局代码,把重点放到数据的渲染上,这里直接使用mediaManager读取缩略图数据,而mediaManager是直接从视图控制器传递过来的,所以每个item都将共享缩略图的缓存。

6.资源的选中和取消

下面就是资源的选择和取消选择功能了,我们设置了资源最大选中数量为9个,那么就需要在选中时进行判断,具体代码如下:

 /// 媒体资源选中和取消的回调private func touchMediaModel(mediaModel:PHMediaModel) {if mediaModel.isSelected {mediaModel.isSelected = falseif let index = mediaManager.selectedMediaModels.firstIndex(of: mediaModel) {mediaManager.selectedMediaModels.remove(at: index)}} else {if mediaManager.selectedMediaModels.count >= mediaManager.maxSelectedCount {print("最多选中\(mediaManager.maxSelectedCount)个")return}mediaModel.isSelected = truemediaManager.selectedMediaModels.append(mediaModel)}collectionView.reloadData()}

接下来我们只需要读取selectedMediaModels的内容就可以获取到我们选中的媒体资源列表了。

但是会有另外一个问题,就是目前通过数组里面的元素我们只能获取到缩略图,还没有获取到原图和原视频资源。

这时候仍然有两个方案,我们可以在点击的时候就开始加载媒体的原始资源,或者我们可以在需要的时候才开始加载原始资源,两个方案都是可以的,我们在下一篇博客再来讨论它们。

结语

本篇博客我们使用已经获取到的媒体数据创建了一个基础的媒体资源选择列表页面。并且使用实现了列表资源的选择和取消选择功能。

至此整个资源选择列表的功能算是完成了,但是常见的资源列表往往还会有一个资源大图的预览功能,下一篇博客我们就来讨论一下媒体原始资源的加载时机,并实现预览功能。

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

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

相关文章

前端养成记-实现一个低配版简单版本的vue3表单自定义设计组件

简介&#xff1a; 通过使用了最新的vue3,vite2,TypeScript等主流技术开发&#xff0c;并配合使用vuedraggable 插件以及antd design vue 组件库实现低配版本的自定义表单设计组件&#xff1b; 项目地址&#xff1a;https://gitee.com/hejunqing/vue3-antdv-generator

直指谷歌?OpenAI将推出在线搜索工具“SearchGPT”

KlipC报道&#xff1a;OpenAI 也在经过一段时间的沉寂后&#xff0c;重新开始了密集的新品发布。当地时间7月25日&#xff0c;OpenAI进军搜索市场&#xff0c;宣布正在测试新的AI搜索工具SearchGPT。目前只向一小部分用户开放&#xff0c;从中获取反馈意见。 华尔街分析师表示…

Cuppa CMS v1.0 任意文件读取漏洞(CVE-2022-25578)

前言 春秋云镜靶场是一个专注于网络安全培训和实战演练的平台&#xff0c;旨在通过模拟真实的网络环境和攻击场景&#xff0c;提升用户的网络安全防护能力和实战技能。这个平台主要提供以下功能和特点&#xff1a; 实战演练&#xff1a; 提供各种网络安全攻防演练场景&#…

深层互联重磅发布,潮流开放式一对多无线导游讲解器

佩戴讲解器走进博物馆&#xff0c;已经是很多人的假期日常&#xff0c;但让我们越来越习以为常的无线讲解器&#xff0c;也许没那么简单。日前&#xff0c;深层互联全新推出IndoorLink潮流开放式导游讲解器&#xff0c;可开展主副双讲及同声传译&#xff0c;将不简单推向了一个…

【vue3|第18期】Vue-Router路由的三种传参方式

日期:2024年7月17日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.408…

上传文件传参 pc端vue的formData

formData let formData new FormData(); formData.append("file", blob, ref ".png"); //添加参数并且重新命名文件名称 if(ref.toString().indexOf(qrcode) > 0) formData.append(noStbg, true)//添加参数 uploadType(formData, sour…

保障企业数据主权:安全可控的爬虫工具与管理平台

摘要 在数据驱动的时代&#xff0c;企业对数据的需求日益增长&#xff0c;但如何在保障数据主权的前提下高效采集数据&#xff1f;本文深入探讨了选择安全可控爬虫工具与管理平台的重要性&#xff0c;分析了关键特性&#xff0c;并提出实用建议&#xff0c;助力企业维护数据安…

2024新版 黑马程序员《C++零基础入门》笔记——第一章17 数据类型-布尔型

1.数据类型-布尔型 2.代码实践 #include "iostream" using namespace std;int main() {// 布尔&#xff1a;bool 字面量仅仅有2个&#xff1a;true或falsebool flag true; // true 表示是真&#xff0c;本质上是数字1bool flag2 false; // false 表示是…

NVIDIA Drivers、CUDA、Pytorch安装

NVIDIA Drivers、CUDA、Pytorch 这三者的版本有着十分紧密的关联&#xff0c;很容易因为版本不对而重复卸载、重装。 这里写个记录&#xff0c;方便查阅。 一、NVIDIA Drivers、CUDA NVIDIA Drivers、CUDA 的关系可以在这里看到&#xff1a; CUDA Compatibilityhttps://docs.n…

7.25 阿里云OSS上传 + 后台返回token + 导出excel

1.阿里云Oss上传 只需要一点就是上传到云端后&#xff0c;前端调用上传文件接口&#xff0c;返回一个资源路径。 接着在提交表单时&#xff0c;前端把这个路径设置为img的参数即可。 1.1上传限制 只上传图片 Api("阿里云文件管理") CrossOrigin //跨域 RestContr…

C语言进阶——一文带你深入了解“C语言关键字”(中篇)

本篇文章属于C语言进阶篇的“C语言关键字”&#xff0c;旨在分享我对C语言关键字的深度学习和了解。同时带领大家深入浅出的走进C语言进阶知识——关键字篇&#xff01; 目录 一、变量的命名规则 二、标识符的命名规则 一、变量的命名规则 1、命名应当直观且可以拼读&#x…

【基础算法总结】优先级队列

优先级队列 1.最后一块石头的重量2.数据流中的第 K 大元素4.前K个高频单词4.数据流的中位数 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1…

汉兴能源研发费用率下降,“不差钱”募集资金近九成补流?

《港湾商业观察》施子夫 王璐 日前&#xff0c;冲刺创业板的上海汉兴能源科技股份有限公司&#xff08;以下简称&#xff0c;汉兴能源&#xff09;更新了招股书。 2023年6月末&#xff0c;汉兴能源正式递表创业板&#xff0c;保荐机构为长江证券。 从业务属性上来看&#x…

React间的组件通信

一、父传子&#xff08;props&#xff09; 步骤 父组件传递数据&#xff0c;子组件标签身上绑定属性子组件接收数据&#xff0c;props的参数 // 子组件 function Son(props) {return (<div>this is Son, {props.name}</div>) }// 父组件 function App() {const n…

数字看板:跨行业需求下的创新与升级

在当今这个数据驱动的时代&#xff0c;数字看板作为信息展示与决策支持的重要工具&#xff0c;正逐步渗透到各行各业之中。从智慧城市到智能制造&#xff0c;从金融分析到医疗健康&#xff0c;数字看板以其直观、动态、高效的特点&#xff0c;成为了连接数据与决策者的桥梁。本…

keil调试SH79F7416

仿真器JET51A, 调试设置 选择器件 再次点击调试就一切正常啦

在同一台linux服务器上安装2+个mysql服务

1. 制作第二个mysql配置文件my.13306.cnf 如下面的配置。请注意&#xff1a;下面的端口&#xff0c;和路径相关的参数&#xff0c;需要和第一个mysql的配置重合&#xff0c;除了basedir参数&#xff0c;该参数是mysql安装的根路径。 [mysqld] group_concat_max_len 102400 u…

2024年Python3.12.0安装+激活+配置教程,保姆级教学,学好Python的第一步!

目录 Python下载 一.安装步骤 二.软件测试 三.环境配置 附赠《2024年最新Python免费电子书&#xff0c;知识点源码资料》→戳这里 Python下载 Python安装包&Pycharm安装包&#xff0c;永久激活码以打包好&#xff0c;需要的朋友可以直接扫下方CSDN官方认证的安全二维码…

前端学习7——自学习梳理

​​​​​​jQuery 教程 | 菜鸟教程jQuery 教程 jQuery 是一个 JavaScript 库。 jQuery 极大地简化了 JavaScript 编程。 jQuery 很容易学习。 本章节的每一篇都包含了在线实例 通过本站的在线编辑器&#xff0c;你可以在线运行修改后的代码&#xff0c;并查看运行结果。 实例…

3.1、数据结构-线性表

数据结构 数据结构线性结构线性表顺序存储和链式存储区别单链表的插入和删除练习题 栈和队列练习题 串&#xff08;了解&#xff09; 数据结构 数据结构该章节非常重要&#xff0c;上午每年都会考10-12分选择题下午一个大题 什么叫数据结构&#xff1f;我们首先来理解一下什…