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

news2024/11/15 1:48:45

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

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

创建媒体选择器

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

选择器UI实现

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

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

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

class PHMediaPickerViewController: UIViewController {
    
    /// 列表
    private var collectionView: UICollectionView!
    /// 媒体资源管理类
    private var mediaManager:PHMediaManager!
    
    /// 数据源
    private var dataSource: [Any] = []
    /// 选中的资源
    private var selectedMediaModels: [PHMediaModel] = []
    /// 最大选中数量
    private var maxSelectedCount: Int = 9

    override func viewDidLoad() {
        super.viewDidLoad()
        initData()
        addCollectionView()
        requestPhotoLibraryAuthorization()

    }
    ....
}
2.初始化数据

根据默认的配置信息来初始化媒体资源管理器,以及数据源列表,并为列表添加两个字符串类型的元素,当做是调取摄像头拍摄图片和视频的item。

    func initData() {
        let config = PHMediaConfig()
        mediaManager = PHMediaManager(config: config)
        let actionArray = ["photo","video"]
        dataSource.append(contentsOf: actionArray)
    }
3.创建列表

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

    /// 添加列表
    func addCollectionView()  {
        let layout = UICollectionViewFlowLayout()
        let itemWidth = (CS_SCREENWIDTH - (2.0 * 3)) / 4.0
        layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
        layout.minimumLineSpacing = 2.0
        layout.minimumInteritemSpacing = 0.0
        collectionView = 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 = self
        collectionView.dataSource = self
        self.view.addSubview(collectionView)
        // 注册 媒体资源类型cell
        collectionView.register(PHMediaPickerCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPickerCell.self))
        // 注册 操作cell
        collectionView.register(PHMediaPickerOperateCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPickerOperateCell.self))
    }
4.请求媒体资源数据

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

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

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

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return dataSource.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let item = dataSource[indexPath.row]
        if let mediaModel = item as? PHMediaModel {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPickerCell.self), for: indexPath) as! PHMediaPickerCell
            let index = selectedMediaModels.firstIndex(of: mediaModel) ?? -1
            if index == -1 {
                cell.setNormal()
            } else {
                cell.setSelected(index: index)
            }
            cell.renderData(mediaModel: mediaModel, mediaManager: mediaManager)
            return cell
        } else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPickerOperateCell.self), for: indexPath) as! PHMediaPickerOperateCell
            
            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 = mediaModel
        self.mediaManager = mediaManager
        playButton.isHidden = mediaModel.mediaType != .video
        let duration = Int(mediaModel.videoDuration)
        let title = secondsToHourMinuteSecond(seconds: duration)
        playButton.setTitle(title, for: .normal)
        self.mediaManager?.fetchThumbnail(asset: mediaModel.asset!, completion: {[weak self] (image) in
            guard let self = self else { return }
            self.imageView.image = image
        })
    }


}

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

6.资源的选中和取消

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

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let item = dataSource[indexPath.row]
        if let mediaModel = item as? PHMediaModel {
            // 点击媒体资源
            self.collectionView(collectionView, didSelectItemAt: indexPath, mediaModel: mediaModel)
        } else {
            // 点击操作
            self.collectionView(collectionView, didSelectItemAt: indexPath, action: item as! String)
        }
        
    }
    
    /// 媒体点击
    private func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath,mediaModel:PHMediaModel) {
        if mediaModel.isSelected {
            mediaModel.isSelected = false
            if let index = selectedMediaModels.firstIndex(of: mediaModel) {
                selectedMediaModels.remove(at: index)
            }
        } else {
            if selectedMediaModels.count >= maxSelectedCount {
                print("最多选中\(maxSelectedCount)个")
                return
            }
            mediaModel.isSelected = true
            selectedMediaModels.append(mediaModel)
        }
        collectionView.reloadData()
    }

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

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

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

结语

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

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

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

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

相关文章

学习React(描述 UI)

React 是一个用于构建用户界面(UI)的 JavaScript 库,用户界面由按钮、文本和图像等小单元内容构建而成。React 帮助你把它们组合成可重用、可嵌套的 组件。从 web 端网站到移动端应用,屏幕上的所有内容都可以被分解成组件。在本章…

08 字符串和字节串

使用单引号、双引号、三单引号、三双引号作为定界符(delimiter)来表示字符串,并且不同的定界符之间可以相互嵌套。 很多内置函数和标准库对象也都支持对字符串的操作。 x hello world y Python is a great language z Tom said, "Le…

数据结构----算法复杂度

1.数据结构前言 数据是杂乱无章的,我们要借助结构将数据管理起来 1.1 数据结构 数据结构(Data Structure)是计算机存储、组织数据的⽅式,指相互之间存在⼀种或多种特定关系的数 据元素的集合。没有⼀种单⼀的数据结构对所有⽤途都有⽤,所…

【Python机器学习】k-近邻算法简单实践——电影分类

k-近邻算法(KNN)的工作原理是:存在一个样本数据集合,也被称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系,输入没有标签的数据后,将…

内存管理概念 (二)

目录 一 . 基本分页存储管理分页存储的几个基本概念页面 与 页面大小地址结构页表 基本地址变换机构具有快表的地址变换机构两级页表 二. 基本分段存储管理分段段表地址变换机构页表和分段的对比段的共享与保护 三. 段页式存储管理分页,分段管理优缺点分页分段段页式…

MATLAB中reset用法

目录 语法 说明 示例 重置坐标区和图窗属性 参数说明 局限性 reset函数的功能是重置图形对象属性。 语法 reset(h) 说明 reset(h) 将指定图形对象的所有属性重置为其默认值。没有默认值的属性不重置。 MATLAB 不会重置任何图形对象的 Position 或 Units 属性。此外&am…

Java-根据前缀-日期-数字-生成流水号(不重复)

🎈边走、边悟🎈迟早会好 小伙伴们在日常开发时可能会遇到的业务-生成流水号,在企业中可以说是比较常见的需求, 可以采用"前缀日期数字"的方式(ps:此方式是需要用到缓存的)前缀:为了…

SpringBoot集成Kaptcha验证码

Hi 👋, Im shy 有人见尘埃,有人见星辰 1. 什么是Kaptcha验证码? Kaptcha是一个强大的开源Java验证码生成库,由Google开发。它能够生成高度可配置的图片验证码,主要用于防止自动化程序滥用web应用,提高应用的安全性。 2. Kaptcha的主要特性 Kaptch…

Kafka快速入门+SpringBoot简单的秒杀案例

1. 主题相关 1.1 创建主题 kafka-topics.sh --create --bootstrap-server [服务器地址] --replication-factor [副本数] --partitions [分区数] --topic [主题名]liberliber-VMware-Virtual-Platform:/home/zookeeper$ docker-compose exec kafka /bin/bash #进入kafka容器 b…

还手动抄字幕?学会这3个视频转文字方法,轻松提取视频中的字幕!

大家有尝试过考试前极限抱佛脚吗? 在下不才,曾经试过一次,轻松在及格线低空飘过【大家不要学不要学不要学,重要的事情说三遍!!!】 至于我当时究竟是怎么做到的呢?其实这里面有点小…

走进数组的奇妙之旅(1)-学习笔记

引言: 在前几篇文章中,我们深入探讨了函数的奥秘。在讲述函数知识的过程中,我们邂逅了一个新的概念,你或许还记得在演示 strcpy函数时,出现的这行代码:char1[20]{0};。当时,你是否感到好奇&…

国产光电耦合器2024年的机遇与挑战

随着科技的飞速发展,2024年对于国产光电耦合器行业来说,无疑是充满机遇与挑战的一年。本文将深入探讨该行业在技术创新、市场竞争、5G时代、新兴应用领域和国际市场拓展方面的现状及未来前景。 技术创新的黄金期 物联网和人工智能技术的迅猛发展&#x…

【实在RPA案例集】实在智能助力中国烟草11省40余家多场景自动化!

近年来,为深入贯彻行业数字化转型战略部署和发展新质生产力体制机制,诸多省市烟草公司及中烟公司大力推进烟草行业数字化转型,然而烟草行业在数字化转型过程中始终存在一个核心痛点,即数据整合的复杂性、系统间的兼容性问题&#…

众人吹捧的Exo并不是真正的分布式推理,而无人问津的Cake或许才是

之前就看到不少抖音AI区的博主吹火爆外网的Exo项目支持多台苹果机运行一个 Llama 70B。我就觉得这个事情或许有些不对劲,而随着最近 Meta 出了 405B,又有朋友向我推荐Exo,我不禁想知道一个 Bonjour 的 Zeroconf 怎么就让 MLX 支持 P2P 的分布…

IP 泄露: 原因与避免方法

始终关注您的IP信息! 您的IP地址不仅显示您的位置,它包含几乎所有的互联网活动信息! 如果出现IP泄漏,几乎所有的信息都会被捕获甚至非法利用! 那么,网站究竟如何追踪您的IP地址?您又如何有效…

【前端学习笔记】CSS基础一

一、什么是CSS 1.CSS 介绍 CSS(Cascading Style Sheets,层叠样式表)是一种用来控制网页布局和设计外观的样式语言。它使得开发者可以分离网页的内容(HTML)和表现形式(样式),提高了…

C语言玩一下标准输出——颜色、闪烁、加粗、下划线属性

文章目录 C语言玩一下标准输出——颜色、闪烁、加粗、下划线属性转换Tip切换内容介绍显示方式字体色背景色 常用光标控制附示例和运行结果 C语言玩一下标准输出——颜色、闪烁、加粗、下划线属性 标准输出格式其属性可控制,控制由一系列的控制码指定。标准输出函数可…

【OAuth2系列】集成微信小程序登录到 Spring Security OAuth 2.0

作者:后端小肥肠 创作不易,未经允许严禁转载。 姊妹篇: 【Spring Security系列】权限之旅:SpringSecurity小程序登录深度探索_spring security 微信小程序登录-CSDN博客 目录 1. 前言 2. 总体登录流程 3. 数据表设计 3.1. sys…

2025第25届北京环卫展|市政设施展|清洗设备展览会

2025第25届北京国际环卫与市政设施及清洗设备展览会 时间:2025年 4月10-12日 地点:全国农业展览馆(朝阳区北三环东路16号) 邀 请 函 指导支持:中国城市环境卫生协会 北京市城市管理委员会 主办单位:北京…

Coggle数据科学 | Kaggle 知识点:时序模型 Prophet

本文来源公众号“Coggle数据科学”,仅用于学术分享,侵权删,干货满满。 原文链接:Kaggle 知识点:时序模型 Prophet Prophet 算法 在时间序列中Prophet是Facebook开源的时间序列预测算法,可以有效处理节假…