首页 > 其他分享 >Photos框架 - 自定义媒体选择器(相册列表)

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

时间:2024-08-02 10:25:30浏览次数:16  
标签:album completion 自定义 相册 self options Photos let 选择器

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

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

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

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

引言

我们已经实现了媒体资源的列表选择以及媒体资源的大图预览功能,但通常一个完整的媒体选择工具还需要附带一个可以选择指定相册资源的功能。毕竟,有时我们会对自己的相册进行分类,以便更方便地管理和查找。在这篇博客中,我们将探讨如何使用Photos框架来自定义相册列表,让用户可以更加灵活地选择和管理他们的媒体资源。

数据处理

那我们首先来处理数据部分,首先我们需要获取相册列表数据来构建一个相册资料的数据模型,里面应该包含封面资源,标题,以及所属相册的资源数量。

数据模型如下:

import UIKit
import Photos

class PHMediaAlbumModel: NSObject {
    
    /// 相册
    var assetCollection:PHAssetCollection?
    /// 标识
    var identifier:String?
    /// 标题
    var title:String? {
        get {
            assetCollection?.localizedTitle
        }
    }
    /// 封面资源
    var asset:PHAsset?
    /// 资源数量
    var count:Int = 0
}

然后为PHMediaManager添加获取相册列表的方法:

    /// 相册列表
    var albums: [PHMediaAlbumModel] = []
    /// 获取相册资源
    /// - Parameters:
    /// - completion: 回调
    func fetchLocalAlbums(completion: @escaping ([PHMediaAlbumModel]) -> Void) {
        self.albums.removeAll()
        let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .any, options: nil)
        smartAlbums.enumerateObjects { (collection, index, stop) in
            let model = PHMediaAlbumModel()
            model.assetCollection = collection
            model.identifier = collection.localIdentifier
            self.albums.append(model)
        }
        
        let fetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
        fetchResult.enumerateObjects { collection, index, stop in
            let model = PHMediaAlbumModel()
            model.assetCollection = collection
            model.identifier = collection.localIdentifier
            self.albums.append(model)
        }
        
        
        
        for album in self.albums {
            self.fetchAlbumCover(album: album.assetCollection!) { asset in
                album.asset = asset
            }
            self.fetchAlbumCount(album: album.assetCollection!) { count in
                album.count = count
            }
        }
        completion(self.albums)
    }

使用PHAssetCollection.fetchAssetCollections(with:subtype:options:)传递第一个参数为.album获取所有相册。subtype 参数指定相册的子类型(例如 .any),options 参数可以用来指定额外的选项(在这个例子中为 nil)。

使用相同的方法传递第一个参数.smartAlbum为获取所有智能相册。

定义两个私有方法用来获取相册的封面媒体资源以及指定相册的资源个数。

    /// 获取指定相册的封面
    /// - Parameters:
    /// - album: 相册
    /// - completion: 回调
    private func fetchAlbumCover(album: PHAssetCollection, completion: @escaping (PHAsset?) -> Void) {
        let options = PHFetchOptions()
        options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        options.fetchLimit = 1
        let fetchResult = PHAsset.fetchAssets(in: album, options: options)
        completion(fetchResult.firstObject)
    }
    /// 获取指定相册的资源个数
    /// - Parameters:
    /// - album: 相册
    /// - completion: 回调
    private func fetchAlbumCount(album: PHAssetCollection, completion: @escaping (Int) -> Void) {
        let options = PHFetchOptions()
        let fetchResult = PHAsset.fetchAssets(in: album, options: options)
        completion(fetchResult.count)
    }

调整fetchLocalMedias方法添加指定相册的参数,修改后代码如下:

    /// 获取相册媒体资源
    /// - Parameters:
    /// - album: 相册
    /// - completion: 回调
    func fetchLocalMedias(album:PHMediaAlbumModel? = nil,completion: @escaping ([PHMediaModel]) -> Void) {
        self.fetchLocalMedias(album:album,type: config.mediaType, completion: completion)
    }
    /// 获取本地所有媒体资源
    /// - Parameters:
    ///  - album: 相册
    ///  - type: 类型
    ///  - completion: 回调
    private func fetchLocalMedias(album:PHMediaAlbumModel? = nil,type: PHMediaType, completion: @escaping ([PHMediaModel]) -> Void) {
        let options = PHFetchOptions()
        .....
        if let album = album {
            fetchResult = PHAsset.fetchAssets(in: album.assetCollection!, options: options)
        } else {
            fetchResult = PHAsset.fetchAssets(with: options)
        }
        var assets = [PHMediaModel]()
        ....
        self.displayMediaModels = assets
        completion(assets)
    }

UI实现

相册列表

首先来创建一个基础的列表UI,代码如下:

class PHMediaAlbumViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
   
    /// 列表
    let tableView = UITableView(frame: .zero, style: .plain)
    /// 媒体资源管理类
    var mediaManager:PHMediaManager!
    
    /// 选择相册的回调
    var selectAlbumBlock:((_ album:PHMediaAlbumModel)->Void)?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
        setLayout()
        requestData()
    }
    
    func setupView() {
        self.view.addSubview(tableView)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.separatorStyle = .none
        tableView.register(PHMediaAlbumCell.self, forCellReuseIdentifier: "PHMediaAlbumCell")
    }
    
    func setLayout() {
        tableView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
    
    func requestData()  {
       
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return mediaManager.albums.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "PHMediaAlbumCell", for: indexPath) as! PHMediaAlbumCell
        cell.selectionStyle = .none
        let album = mediaManager.albums[indexPath.row]
        cell.renderData(albumModel: album, mediaManager: mediaManager)
        return cell
    }
    
}

调用PHMediaManager中我们新定义的fetchLocalAlbums方法来获取相册数据渲染列表:

    func requestData()  {
        mediaManager.fetchLocalAlbums { [weak self] albums in
            self?.tableView.reloadData()
        }
    }

实现点击事件,调用block代码将选择的相册数据回传到媒体资源列表:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let album = mediaManager.albums[indexPath.row]
        if let block = selectAlbumBlock {
            block(album)
        }
    }

显示相册

修改PHMediaPickerViewController的标题为按钮,并添加事件用来显示和隐藏相册列表:

        navigationBar.titleButtonTouchBlock = { [weak self] in
            if self?.mediaAlbumViewController != nil {
                self?.mediaAlbumViewController?.view.removeFromSuperview()
                self?.mediaAlbumViewController = nil
            } else {
                self?.showMediaAlbumViewController()
            }
        }

显示相册代码如下:

    /// 显示相册列表
    func showMediaAlbumViewController() {
        mediaAlbumViewController = PHMediaAlbumViewController()
        mediaAlbumViewController?.mediaManager = mediaManager
        self.view.addSubview(mediaAlbumViewController!.view)
        mediaAlbumViewController?.view.snp.makeConstraints { make in
            make.top.equalTo(cs_navigationBarHeight)
            make.leading.trailing.bottom.equalToSuperview()
        }
        mediaAlbumViewController?.selectAlbumBlock = {[weak self] album in
            guard let self = self else { return }
            self.navigationBar.title = album.title
            self.mediaAlbumViewController?.view.removeFromSuperview()
            self.mediaAlbumViewController = nil
            self.fetchMediaData(album: album)
        }
    }

调用获取指定相册媒体资源的方法刷新列表:

    /// 获取指定相册的媒体资源
    func fetchMediaData(album:PHMediaAlbumModel) {
        mediaManager.fetchLocalMedias(album: album) {[weak self] (mediaModels) in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.collectionView.reloadData()
            }
        }
    }

效果如下:

结语

通过本篇博客,我们已经了解了如何使用 Photos 框架来自定义媒体选择器,并获取相册的标题、封面和资源总数。然而,为了提升用户体验,我们还可以进一步优化显示效果。例如,通过过滤掉没有资源的相册,我们可以避免展示空相册,从而简化用户的选择过程。此外,还可以通过优化相册的显示效果,使用户能够更加直观和便捷地浏览和选择他们的媒体资源。希望这些技巧能够帮助你构建一个更高效、用户友好的媒体选择工具。

标签:album,completion,自定义,相册,self,options,Photos,let,选择器
From: https://blog.csdn.net/weixin_39339407/article/details/140814267

相关文章

  • clion 《cmake自定义静态库后,生成的exe无法运行》
    背景项目生成lib引入,在生成exe过程中无法正常运行处理办法让链接器静态链接GCC和C++标准库set(CMAKE_EXE_LINKER_FLAGS"-static-libgcc-static-libstdc++")主CMakeLists.txtcmake_minimum_required(VERSION3.28)project(speech)#编译版本set(CMAKE_CXX_STANDAR......
  • 【C#】WPF自定义Image实现图像缩放、平移
    1.xaml实现<UserControlx:Class="HalconDemo.ImageUserControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://sche......
  • 自定义Django后台admin
    Django后台自定义一、AdminSite1、AdminSite属性AdminSite属性属性描述site_header管理页面顶部的文字,默认是‘Django管理’site_title<title>末尾放置的文字site_url‘查看网站’链接的urlindex_title管理索引页顶部的文字index_template自定义主要......
  • Flutter 自定义画笔案例
    首先让我们来看下这张图当UI做的设计图中有这么一个元素,我想大多数人第一反应就是叫UI切图,然后直接使用Image加载,我一开始也是这么做的,毕竟省时省力省心。但是由于后面需要针对不同的状态设置不同的颜色,我不想写过多判断语句来切换图标(我目前的做法是实现一个枚举类,然后拓展该......
  • el-progress 自定义线状进度条右边的文字
    需要展示类似下面的效果 搜了很多slot的方式试了都不行,好像是因为我后面的文字太长了导致了换行,加上这边需要加其他的样式,最后干脆将原始的文字变成空的,自己写右边的文字加样式了<divstyle="margin:10px020px0"v-f......
  • 关键错误:“工具”。 CrewAI 在制作自定义工具时出错?
    我开发了一个团队来从不同的URL获取一些信息。到目前为止总共有大约3个URL,所以我创建了5个代理。1是编辑器(经理),1是其他3个带到表中的所有数据的编译器。如果这有帮助的话,这是我的文件夹结构university_scraper/│├──src/│├──__init__.py│......
  • vue3 自定义渲染,渲染函数实现,配置渲染render函数,低代码配置自定义渲染函数核心实现
    代码父组件<template><divclass="component-name"><!--全局自动的icon--><Extend:render="render"/></div></template><scriptsetuplang="ts">import{ref,reac......
  • 自定义线程池实现(一)
    预期目标1.实现一个相对完备的线程池2.自定义拒绝策略(下一节)线程池的基本参数1.核心线程数2.超时时间3.拒绝策略(在下一篇中添加)4.工作队列5.任务队列工作机制当添加一个任务到线程池中时,线程池会判断工作线程数量是否小于核心线程数,若小于创建工作线程,执行任务;反......
  • 懂个锤子Vue 自定义指定、插槽:
    Vue自定义指定、插槽......
  • Quart自定义文件导出名
    直接上代码fromquartimportQuart,send_fileimportioimportxlwtapp=Quart(__name__)@app.route('/download-excel',methods=["POST"])asyncdefdownload_excel():#创建一个简单的Excel文件workbook=xlwt.Workbook()sheet=workb......