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

引言

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

预览功能实现

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

预览UI

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

Swift 复制代码
    /// 添加列表
    func addCollectionView()  {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: CS_SCREENWIDTH, height: CS_SCREENHIGHT)
        layout.minimumLineSpacing = 0.0
        layout.minimumInteritemSpacing = 0.0
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.isPagingEnabled = true
        self.view.addSubview(collectionView)
        collectionView.snp.makeConstraints { make in
            make.top.equalToSuperview()
            make.leading.trailing.bottom.equalToSuperview()
        }
        collectionView.register(PHMediaPreViewCell.self, forCellWithReuseIdentifier: NSStringFromClass(PHMediaPreViewCell.self))
    }

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

Swift 复制代码
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)?
    ....
}

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

Swift 复制代码
    /// 渲染数据
    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
        })
    }

预览数据

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

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

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

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

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

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

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

Swift 复制代码
    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! PHMediaPreViewCell
        let mediaModel = mediaManager.displayMediaModels[indexPath.row]
        cell.renderData(mediaModel: mediaModel, mediaManager: mediaManager)
        let index = mediaManager.selectedMediaModels.firstIndex(of: mediaModel) ?? -1
        cell.index = index
        cell.selectTagTouchBlock = { [weak self] mediaModel in
            self?.touchMediaModel(mediaModel: mediaModel)
        }
        return cell
    }

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

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()
        selectMediaBlock?()
    }

使用预览

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

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

结语

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

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

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

相关推荐
九州ip动态8 小时前
模拟器多开限制ip,如何设置单窗口单ip,每个窗口ip不同
tcp/ip·游戏·媒体
大霸王龙2 天前
数字媒体需求复用工具(DMReqTool)
python·django·媒体
不能只会打代码2 天前
支持用户注册和登录、发布动态、点赞、评论、私信等功能的社交媒体平台创建!!!
前端·css·后端·html·json·媒体·社交媒体平台
天马37982 天前
C#获取视频第一帧_腾讯云媒体处理获取视频第一帧
云计算·腾讯云·媒体
小不点区块2 天前
海外媒体软文发稿:打开全球传播的新窗口-大舍传媒
传媒·媒体
大舍传媒3 天前
海外媒体发稿:阿拉伯海湾新闻-外媒宣发的魅力与机遇
大数据·人工智能·科技·搜索引擎·媒体
我叫汪枫4 天前
SSM post接口传递json 报错 HTTP状态 415 - 不支持的媒体类型
http·json·媒体
小不点区块4 天前
海外媒体发稿:中东地区阿拉伯邮报Arab Post新闻媒体宣发
大数据·人工智能·搜索引擎·媒体
51媒体网-媒体邀约4 天前
「51媒体」:企业成长助推器
媒体
EasyGBS4 天前
国标GB28181摄像机接入EasyGBS国标GB28181设备管理软件:GB28181-2022媒体传输协议解析
安全·音视频·媒体·视频监控·gb28181