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

引言

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

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

创建媒体选择器

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

选择器UI实现

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

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

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

Swift 复制代码
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.初始化数据

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

Swift 复制代码
    func initData() {
        let config = PHMediaConfig()
        mediaManager = PHMediaManager(config: config)
    }
3.创建列表

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

Swift 复制代码
    /// 添加列表
    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.请求媒体资源数据

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

Swift 复制代码
    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 }
            DispatchQueue.main.async {
                self.collectionView.reloadData()
            }
        }
    }
5.根据数据渲染列表

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

Swift 复制代码
  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! PHMediaPickerOperateCell
        cell.type = action
        return cell
    }
    
    private func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath,mediaModel:PHMediaModel) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(PHMediaPickerCell.self), for: indexPath) as! PHMediaPickerCell
        let index = mediaManager.selectedMediaModels.firstIndex(of: mediaModel) ?? -1
        cell.index = index
        cell.renderData(mediaModel: mediaModel, mediaManager: mediaManager)
        cell.selectTagTouchBlock = { [weak self] mediaModel in
            self?.touchMediaModel(mediaModel: mediaModel)
        }
        return cell
    }

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

Swift 复制代码
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个,那么就需要在选中时进行判断,具体代码如下:

Swift 复制代码
 /// 媒体资源选中和取消的回调
    private func touchMediaModel(mediaModel:PHMediaModel) {
        if mediaModel.isSelected {
            mediaModel.isSelected = false
            if 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 = true
            mediaManager.selectedMediaModels.append(mediaModel)
        }
        collectionView.reloadData()
    }

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

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

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

结语

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

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

相关推荐
程序员Bears2 小时前
深入理解CSS3:Flex/Grid布局、动画与媒体查询实战指南
前端·css3·媒体·visual studio code
云天徽上2 天前
【数据可视化-33】病毒式社交媒体潮流与用户参与度可视化分析
机器学习·信息可视化·数据挖掘·数据分析·媒体
财经汇报6 天前
媒体关注:联易融聚焦AI+业务,重塑供应链金融生态
人工智能·金融·媒体
北京阳光3486 天前
外商在国内宣传 活动|发布会|参展 邀请媒体
媒体
姚家湾6 天前
闲聊人工智能对媒体的影响
人工智能·媒体
mycm03046 天前
媒体发稿软文推广:新闻稿写什么内容
经验分享·媒体
130252015126 天前
品牌如何通过朝日新闻出海日本?——某企业日本媒体发稿实战
媒体·日本媒体·海外媒体发稿·朝日新闻
bst@微胖子8 天前
Flutter之资源和媒体
javascript·flutter·媒体
vx_330762317211 天前
vos3000外呼系统怎么给普通用户开通播放下载录音权限?
运维·服务器·人工智能·ai·媒体
努力毕业的小土博^_^12 天前
【EI/Scopus顶会矩阵】2025年5-6月涵盖统计建模、数智转型、信息工程、数字系统、自动化系统领域,硕博生执笔未来!
人工智能·深度学习·线性代数·计算机视觉·矩阵·自动化·媒体