当前位置: 首页 > news >正文

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的内容就可以获取到我们选中的媒体资源列表了。

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

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

结语

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

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 您需要了解的有关 5G 的一切。
  • 大数据之Oracle同步Doris数据不一致问题
  • C#用Aspose.Cells导出Excel,.NET导出Excel
  • Dockerfile自定义镜像
  • 搜索引擎项目(四)
  • ubuntu20.04.6 安装Skywalking 10.0.1
  • Kylin系列(一):入门与深入解析(大数据分析)
  • RVC-AI声音克隆-你的声音不再是唯一
  • Wechat Files目录垃圾清理指南
  • Android笔试面试题AI答之线程Handler、Thread(2)
  • ES(Elasticsearch)常用的函数有哪些?
  • 代码审计:Bluecms v1.6
  • 9. kubernetes资源——pv/pvc持久卷
  • 银行贷款信用评分不足?大数据帮你找回失去的“分”
  • 史上最全网络安全面试题+答案
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 2017-08-04 前端日报
  • css属性的继承、初识值、计算值、当前值、应用值
  • emacs初体验
  • express.js的介绍及使用
  • flask接收请求并推入栈
  • laravel 用artisan创建自己的模板
  • log4j2输出到kafka
  • nodejs实现webservice问题总结
  • React Native移动开发实战-3-实现页面间的数据传递
  • React-flux杂记
  • spring boot 整合mybatis 无法输出sql的问题
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vagrant 添加本地 box 安装 laravel homestead
  • 从伪并行的 Python 多线程说起
  • 基于HAProxy的高性能缓存服务器nuster
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 使用parted解决大于2T的磁盘分区
  • 腾讯大梁:DevOps最后一棒,有效构建海量运营的持续反馈能力
  • 详解NodeJs流之一
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • ​第20课 在Android Native开发中加入新的C++类
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (7)STL算法之交换赋值
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (zhuan) 一些RL的文献(及笔记)
  • (不用互三)AI绘画工具应该如何选择
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (四)stm32之通信协议
  • (转)【Hibernate总结系列】使用举例
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • .htaccess配置常用技巧
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .NET 设计一套高性能的弱事件机制