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框架中的内容,在我的其它博客专栏中曾经有过介绍,有兴趣的可以查看一下。

相关推荐
红米饭配南瓜汤18 小时前
WebRTC 码率预估(1) - 接收端 TransportFeedback 生成和发送流程指南
网络·音视频·webrtc·媒体
趣浪吧2 天前
AI在手机上真没用吗?
人工智能·智能手机·aigc·音视频·媒体
科技互联.3 天前
JHMS媒体传讯服务:自媒体时代品宣的官方背书
媒体
卜锦元8 天前
音视频媒体服务领域中三种架构方式的定义与区别(Mesh、MCU、SFU)
架构·音视频·媒体
蚁巡信息巡查系统9 天前
自媒体内容安全审核指引怎么写,有哪些内容?
安全·信息可视化·媒体·内容运营
卜锦元10 天前
Mediasoup的SFU媒体服务转发中心详解(与传统SFU的区别)
音视频·webrtc·媒体
flex888813 天前
一款专为媒体爱好者设计的跨平台客户端软件,整合 Jellyfin、Emby、CMS 、webdav和IPTV媒体服务
媒体
Swift社区14 天前
iOS 基于 Foundation Model 构建媒体流
ios·iphone·swift·媒体
我希望的一路生花14 天前
告别文件混乱!Adobe Bridge 2026 全媒体可视化管理,让设计流程更顺畅
adobe·媒体
点金石游戏出海16 天前
每周资讯 | 印度数字媒体与娱乐市场在2025财年达93亿美;《崩坏:星穹铁道》新版本登顶iOS畅销榜首
游戏·娱乐·媒体·游戏资讯·崩坏星穹铁道