Photos框架 - 自定义媒体选择器(UI预览)

554b3494cc0f42debc0c893bac502f04.png

引言

在前面的博客中我们已经介绍了使用媒体资源数据的获取,以及自定义的媒体资源选择列表页。在一个功能完整的媒体选择器中,预览自然是必不可少的,本篇博客我们就来实现一个资源的预览功能,并且实现列表和预览的数据联动效果。

预览功能实现

预览功能包括图片预览和视频预览,并且在预览的同时最好的情况就是我们仍然知道当前正在预览的资源是否已经被选中了,或者说在预览的同时我们仍然可以选择和取消选中。这就需要我们在数据上花点心思。

预览UI

预览页面我们需要创建一个新的视图控制器,然后采用一个全屏的UICollectionView来实现,它的每个元素也都是和屏幕相同大小,具体代码如下:

    /// 添加列表func addCollectionView()  {let layout = UICollectionViewFlowLayout()layout.scrollDirection = .horizontallayout.itemSize = CGSize(width: CS_SCREENWIDTH, height: CS_SCREENHIGHT)layout.minimumLineSpacing = 0.0layout.minimumInteritemSpacing = 0.0collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)collectionView.backgroundColor = .whitecollectionView.delegate = selfcollectionView.dataSource = selfcollectionView.isPagingEnabled = trueself.view.addSubview(collectionView)collectionView.snp.makeConstraints { make inmake.top.equalToSuperview()make.leading.trailing.bottom.equalToSuperview()}collectionView.register(PHMediaPreViewCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPreViewCell.self))}

预览cell的元素内容几乎和列表相同,需要有选中标签,也需要有视频时长等等,具体代码如下:

class PHMediaPreViewCell: UICollectionViewCell {/// 图片private let imageView = UIImageView()/// 选中标签private var selectTag = UIButton()/// 播放按钮private var playButton = UIButton()/// 资源模型private var mediaModel: PHMediaModel?/// 资源管管理类var mediaManager:PHMediaManager?/// 选中或取消回调var selectTagTouchBlock: ((PHMediaModel) -> Void)?....
}

包括它的标签选中逻辑,和画面渲染逻辑也都大同小异,只是布局的样式略有不同,另外它需要加载的图片是原图,而不是缩略图,具体的渲染代码如下:

    /// 渲染数据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})}

预览数据

为了让缩略图列表和预览列表的数据可以联动,在两个页面控制器中我们都是用PHMediaManager来管理显示的媒体数据列表和选中的媒体数据列表。

另外需要定义index来指定当我们从列表页面进入预览页面时的资源索引。

还定义了一个选择的回调,用来给列表页面同步UI。

    /// 资源管理类var mediaManager: PHMediaManager!/// 当前indexvar currentIndex: Int = 0/// 选择回调var selectMediaBlock: (() -> Void)?

当进入预览页面,首先同步索引,将预览的图片定位到我们在列表中点击的媒体资源,代码如下:

    override func viewDidAppear(_ animated: Bool) {super.viewDidAppear(animated)collectionView.scrollToItem(at: IndexPath(row: currentIndex, section: 0), at: .centeredHorizontally, animated: false)}

之后通过mediaManager中的displayMediaModels来渲染列表数据:

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return mediaManager.displayMediaModels.count}func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPreViewCell.self), for: indexPath) as! PHMediaPreViewCelllet mediaModel = mediaManager.displayMediaModels[indexPath.row]cell.renderData(mediaModel: mediaModel, mediaManager: mediaManager)let index = mediaManager.selectedMediaModels.firstIndex(of: mediaModel) ?? -1cell.index = indexcell.selectTagTouchBlock = { [weak self] mediaModel inself?.touchMediaModel(mediaModel: mediaModel)}return cell}

关于媒体资源选中和取消选中的回调,和列表中的处理完全一致,只是多了一个block调用通知列表刷新UI:

    /// 媒体资源选中和取消的回调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()selectMediaBlock?()}

使用预览

这样我们就需要把原来的列表点击事件做一下调整,点击后让它显示预览画面,代码如下:

    /// 媒体点击private func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath,mediaModel:PHMediaModel) {self.showPreView(index: indexPath.row - actionArray.count)}
    // 显示预览private func showPreView(index:Int) {let preVC = PHMediaPreViewController()preVC.mediaManager = mediaManagerpreVC.currentIndex = indexpreVC.modalPresentationStyle = .fullScreenself.present(preVC, animated: false, completion: nil)preVC.selectMediaBlock = { [weak self]  inself?.collectionView.reloadData()}}

结语

有了前面媒体列表功能做基础,预览的功能实现起来显得轻松了许多,只不过是将加载缩略图替换为了加载原图。

需要注意的一点,列表和预览必须公用通一个mediaManager这样才能保证数据的一致。

另外本篇博客我们没有涉及到视频资源的预览,因为它将会设计AVKit或者AV Foundation框架中的内容,在我的其它博客专栏中曾经有过介绍,有兴趣的可以查看一下。

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

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

相关文章

GLSL教程 第9章:计算着色器

目录 9.1 计算着色器的基本概念 计算着色器的主要特点: 9.2 计算着色器的基础知识 1. 创建计算着色器 计算着色器代码: 2. 编译和链接计算着色器 示例代码: 3. 执行计算着色器 示例代码: 9.3 实现并行计算和数据并行处理…

SD-WAN 的真相以及它如何支持企业数字化转型

企业需要灵活、安全的网络解决方案,以支持随时随地工作模式和多云策略,他们正在转向软件定义广域网 (SD-WAN) 技术来实现这一目标。 其操作简单、独立于运营商的 WAN 连接和改进的安全功能可提供直接云访问,并为安全访问服务边缘 (SASE) 策略…

字典树、并查集适用于算法竞赛

字典树 题目:835. Trie字符串统计 - AcWing题库 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以…

C++初学者指南-6.函数对象--函数对象

C初学者指南-6.函数对象–函数对象 文章目录 C初学者指南-6.函数对象--函数对象函数对象示例:区间查询区间内的查找区间划分(分组) 指南标准库函数对象比较算术运算 函数对象 提供至少一个成员函数重载 operator() 的对象 class Multiplier {int m_; public:// cons…

还在用if校验参数?SpringBoot使用validation优雅实现参数校验

👩🏽‍💻个人主页:阿木木AEcru (更多精彩内容可进入主页观看) 🔥 系列专栏:《Docker容器化部署系列》 《Java每日面筋》 💹每一次技术突破,都是对自我能力的挑战和超越。 目录 一、前…

鸿蒙APP架构及开发入门

1.鸿蒙系统 1.1 什么是鸿蒙 鸿蒙是一款面向万物互联时代的、全新的分布式操作系统。 在传统的单设备系统能力基础上,鸿蒙提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机、PC、智能音箱、耳机、…

深入解析食堂采购系统源码:打造高效食材供应链APP的核心

本篇文章,笔者将从系统架构、关键模块、技术选型和优化策略等方面,深入解析食堂采购系统的源码,为您揭示打造高效食材供应链APP的核心要点。 一、系统架构 食堂采购系统通常采用分层架构,以保证系统的可维护性和扩展性。主要包括…

Android 列表或网格形式展示大量数据:RecyclerView(二):缓存复用

一、缓存复用 为什么要了解这个呢?当我们rv出现卡顿,出现闪烁的时候,你应该如何优化呢? 为什么有时候onCreateViewHolder会被调用?onBindVilewHolder会被调用呢? visiable的使用,会导致重新绘制…

Linux---git工具

目录 初步了解 基本原理 基本用法 安装git 拉取远端仓库 提交三板斧 1、添加到缓存区 2、提交到本地仓库 3、提交到远端 其他指令补充 多人协作管理 windows用户提交文件 Linux用户提交文件 初步了解 在Linux中,git是一个指令,可以帮助我们做…

jionlp根据词典进行行政区划补全

背景 需要对地址数据进行行政区划补全的,可以用下面的方法,当然是有条件限制的,只限于提供本省的词典和补全本身的地址数据,否则容易错乱 效果测试 lp = LocationParser() loc = 侨英街道乐海南里170号 res = lp(loc) print(res)1、安装或者更新 python安装 pip insta…

Python爬虫技术 第18节 数据存储

Python 爬虫技术常用于从网页上抓取数据,并将这些数据存储起来以供进一步分析或使用。数据的存储方式多种多样,常见的包括文件存储和数据库存储。下面我将通过一个简单的示例来介绍如何使用 Python 爬取数据,并将其存储为 CSV 和 JSON 文件格…

LangChain4j-RAG高级-核心概念

RetrievalAugmentor整体概念 简单总结一下 LangChain4j中对于RetrievalAugmentor这里官方描述的比较模糊, 只在 DefaultRetrievalAugmentor章节给出来了一个灵感来源的文章(LangChain框架中的设计思路)和一个研究报告, 有兴趣可以看一下: Deconstructing RAGhttps://arxiv.o…

FRP配置内网穿透52版本以上适用

简述 适用frp配置内网穿透来说我们需要进行简单的区分,具有公网IP的服务器我们简称为服务端,内网的服务器我们可以简称为客户端,frp需要针对不同的服务器配置不同的文件 下载安装包 Linux下载地址 https://github.com/fatedier/frp/relea…

Flink SQL 的工作机制

前言 Flink SQL 引擎的工作流总结如图所示。 从图中可以看出,一段查询 SQL / 使用TableAPI 编写的程序(以下简称 TableAPI 代码)从输入到编译为可执行的 JobGraph 主要经历如下几个阶段: 将 SQL文本 / TableAPI 代码转化为逻辑执…

svelte - 5. 动画

动画函数 函数作用使用场景示例引入的模块使用示例tweened运动动画,做到渐变的效果控制进度条速度svelte/motion函数:tweened(0, { duration: 400 })spring运动动画,用于频繁变化的值控制鼠标红点顺滑度svelte/motion函数:spring({ x: 50, y: 50 }, { stiffness: 0.1, damp…

华为ensp中ISIS原理与配置(超详细)

isis原理与配置 8-20字节; 地址组成:area id,system id,set三部分组成; system id占6个字节;sel占一个,剩下的为area id区域号; system id 唯一, 一般将router id 配…

深入学习H264和H265

目录 前言 一 什么是H264/H265? H.264 (MPEG-4 AVC) H.265 (HEVC) 二 为什么要学习H264和H265? 1. 深入理解视频压缩原理 2. 硬件优化与集成 3. 调试与故障排除 4. 持续的技术更新 三 NAL(Network Abstraction Layer)详解…

【前端 11】初探DOM

JavaScript 对象 - DOM 初探 在Web开发中,DOM(Document Object Model,文档对象模型)是一个至关重要的概念。它不仅仅是一个API,更是Web页面与JavaScript代码之间的桥梁,允许开发者通过编程的方式动态地访问…

Redis:快速键值存储的入门指南

一、什么是Redis? Redis,全称为Remote Dictionary Server,是一种开源的、高性能的键值(Key-Value)存储系统。与传统的关系型数据库不同,Redis将数据主要存储在内存中,因此能够提供极低延迟的数…

【Unity2D 2022:UI】TextMeshPro组件无法显示中文

在Unity中创建了一个预制体Card,上面挂载了一些Text Mesh Pro组件用来显示卡牌信息。但是在输入文字后,发现无法显示中文: 解决方法如下: 一、导入字体文件(ttf格式)和常用字字集(txt格式&…