参考:http://www.hangge.com/blog/cache/detail_1512.html
ios14 使用PHPicker获取相册(需要 import PhotosUI)
- 支持多选
- 支持搜索
- 独立的进程
- 内置隐私
- 不需要直接访问用户相册
- 不会弹出访问相册提示
- 仅提供用户选择的照片和视频(App 无法获取其他照片)
PHPicker 是由系统提供的选择器,可以访问用户相册里的照片和视频。它内置了搜索功能,支持网格中的流体缩放,以及经常会被用到的照片多选功能。
PHPicker 运行在应用程序的进程之外,尽管它看起来像是在应用程序内部运行。它实际上是在一个独立的进程中运行,而不是在它上面呈现的宿主应用。但是该应用不能直接访问选择器,甚至不能截屏选择器内容。只有用户实际选择的内容才会传递回主机应用程序。
PHPickerViewController 依然是通过 delegate 属性来向宿主 App 返回用户选择的结果。
PHPickerConfiguration 。 PHPickerConfiguration 有两个可选属性 selectionLimit 和 filter 。 selectionLimit 默认值为 1,表示单选。若要多选,则设置为 0; filter 指定相册显示类型,可单独设置,也可用数组形式显示多种类型。
代理方法中用户选择的结果会以 PHPickerResult 数组的形式传入,每一个 PHPickerResult 都对应一个照片、视频或者 LivePhoto 的数据。PHPickerResult 只有 itemProvider 和 assetIdentifier 两个属性,剩下的是继承的 Equatable 和 Hashable 协议的实现。
assetIdentifier这是选中对象对应的 PHAsset 的 id,可以用这个 id 通过 PHAset 的相关 api 来进行其他操作。不过苹果在 WWDC 的介绍视频中明确强调了如果要访问 PHAsset 的额外信息,依然需要获取到相册权限后才可以执行。
通过 itemProvider 的 api,我们可以获取到最终的结果,不过这里有点小麻烦:针对照片、视频和 LivePhoto 三种媒体类型,分别要使用不同的 api 来获取;
1.获取照片,通过 NSItemProvider 的 loadObject 方法,并且指定 Class 类型为 UIImage,就可以在回调中得到 UIImage 类型的照片了。
//获取图片
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
//下面只是获取了一张图片
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
// 通过 NSItemProvider 的 loadObject 方法,并且指定 Class 类型为 UIImage,就可以在回调中得到 UIImage 类型的照片了; [weak self]
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
print("一张图片\(String(describing: image))")
DispatchQueue.main.async {
//在主线更新UI
}
}
}
}
2.获取 LivePhoto 与获取照片类似,只是需要将 UIImage 替换为 PHLivePhoto。之后你可以通过 PHLivePhotoView 来显示。或者通过 PHAssetResourceManager 获取 LivePhoto 的原始数据。
//获取LivePhoto
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
//下面只是获取了一张图片
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: PHLivePhoto.self) {
// 通过 NSItemProvider 的 loadObject 方法,并且指定 Class 类型为 UIImage,就可以在回调中得到 UIImage 类型的照片了; [weak self]
itemProvider.loadObject(ofClass: PHLivePhoto.self) { [weak self] phlivephoto, error in
print("phlivephoto\(String(describing: phlivephoto))")
let livephoto = PHLivePhotoView.init(frame: CGRect.init(x: 0, y: 0, width: 300, height: 300))
livephoto.livePhoto=(phlivephoto as! PHLivePhoto)
self?.view.addSubview(livephoto)
}
}
}
3.获取视频,使用 loadFileRepresentation 方法来加载大文件(包括视频)。loadFileRepresentation 的使用方式与 UIImage 类似,但需要额外传入一个参数 forTypeIdentifier 来指定文件类型,指定为 public.movie 可以覆盖相册中的 .mov 和 .mp4 类型。与照片不同的是,这个 api 返回的是一个 URL 类型的临时文件路径,苹果在这个 API 的说明中指出:系统会把请求的文件数据复制到这个路径对应的地址,并且在回调执行完毕后删除临时文件。
//获取视频
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
//下面只是获取了一张图片
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
//这个 api 返回的是一个 URL 类型的临时文件路径,苹果在这个 API 的说明中指出:系统会把请求的文件数据复制到这个路径对应的地址,并且在回调执行完毕后删除临时文件。
itemProvider.loadFileRepresentation(forTypeIdentifier: "public.movie") { url, error in
}
}
}
配置PHPicker:
@available(iOS 14, *)
func setPHPicekr(){
var config = PHPickerConfiguration()
// 可选择的资源数量,0表示不设限制,默认为1
config.selectionLimit = 0
// 可选择的资源类型
// 只显示图片(注:images 包含 livePhotos)
config.filter = .images
// 显示 Live Photos 和视频(注:livePhotos 不包含 images)
// config.filter = .any(of: [.livePhotos, .videos])
// 如果要获取视频,最好设置该属性,避免系统对视频进行转码,默认是automatic
config.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true, completion: nil)
}
综合:
var selectType = "image" //"video" "livephoto"
@available(iOS 14, *)
func setPHPicekr(){
var config = PHPickerConfiguration()
// 可选择的资源数量,0表示不设限制,默认为1
config.selectionLimit = 0
// 可选择的资源类型
// 只显示图片(注:images 包含 livePhotos)
config.filter = .images
// 显示 Live Photos 和视频(注:livePhotos 不包含 images)
// config.filter = .any(of: [.livePhotos, .videos])
// 如果要获取视频,最好设置该属性,避免系统对视频进行转码,默认是automatic,会自动转码
config.preferredAssetRepresentationMode = .current
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true, completion: nil)
}
//获取图片
@available(iOS 14, *)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
if selectType=="image"{
//下面只是获取了一张图片
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
// 通过 NSItemProvider 的 loadObject 方法,并且指定 Class 类型为 UIImage,就可以在回调中得到 UIImage 类型的照片了; [weak self]
itemProvider.loadObject(ofClass: UIImage.self) { image, error in
print("一张图片\(String(describing: image))")
DispatchQueue.main.async {
//在主线更新UI
}
}
}
}else if selectType=="live"{//视频
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
//这个 api 返回的是一个 URL 类型的临时文件路径,苹果在这个 API 的说明中指出:系统会把请求的文件数据复制到这个路径对应的地址,并且在回调执行完毕后删除临时文件。
itemProvider.loadFileRepresentation(forTypeIdentifier: "public.movie") { url, error in
}
}
}else if selectType=="livephoto"{//livephoto
//获取PHPickerResult的itemProvider
if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: PHLivePhoto.self) {
// 通过 NSItemProvider 的 loadObject 方法,并且指定 Class 类型为 UIImage,就可以在回调中得到 UIImage 类型的照片了; [weak self]
itemProvider.loadObject(ofClass: PHLivePhoto.self) { [weak self] phlivephoto, error in
print("phlivephoto\(String(describing: phlivephoto))")
let livephoto = PHLivePhotoView.init(frame: CGRect.init(x: 0, y: 0, width: 300, height: 300))
livephoto.livePhoto=(phlivephoto as! PHLivePhoto)
// self?.view.addSubview(livephoto)
}
}
}
}
ios8-13使用Photos获取相册(需要import Photos)
1。***********获取系统相册的照片和录像*********
let assetArr: [PHAsset] = getAllAlbumAndPHAsset()//获取照片资源
//吧强求到图片资源转换成UIIMgae,在回掉方法中获取图片data数据,然后做相应的操作
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
self.getImageFromPHAsset(asset: assetArr[0], size: CGSize.init(width: 100, height: 100))
}
********************
//
//1.获取所有PHAsset资源的集合,包含视频和照片
func getAllPHAssetFromSysytem()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(with: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
assetsFetchResults.enumerateObjects { (asset, index, stop) in
arr.append(asset)
}
print("\(arr.count)")
return arr
}
//2.先获取所有相册,然后从相机胶卷中获取PHAsset集合,(相机胶卷是相册中的一个,包含了所有视频和相册)
func getAllAlbumAndPHAsset()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
// 所有智能相册集合(系统自动创建的相册)
let smartAlbums:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.albumRegular, options: options)
//遍历得到每一个相册
for i in 0..<smartAlbums.count {
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
let collection:PHCollection = smartAlbums[i];//得到一个相册,一个集合就是一个相册
/**
相册标题英文:
Portrait、Long Exposure、Panoramas、Hidden、Recently Deleted、Live Photos、Videos、Animated、Recently Added、Slo-mo、Time-lapse、Bursts、Camera Roll、Screenshots、Favorites、Selfies
*/
print("相册标题---%@",collection.localizedTitle as Any);
//遍历获取相册
if collection is PHAssetCollection {
if collection.localizedTitle == "相机胶卷" || collection.localizedTitle == "Camera Roll"{//相册的名字是相机交卷,这里面包含了所有的资源,包括照片、视频、gif。 注意相册名字中英文
let assetCollection = collection as! PHAssetCollection
//collection中的资源都统一使用PHFetchResult 对象封装起来。
//得到PHFetchResult封装的图片资源集合
let fetchResult:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
var assetArr:[PHAsset] = []
if fetchResult.count>0{
//某个相册里面的所有PHAsset对象(PHAsset对象对应的是图片,需要通过方法请求到图片)
assetArr = getAllPHAssetFromOneAlbum(assetCollection: assetCollection)
arr.append(contentsOf: assetArr)
}
}
}
}
return arr
}
//获取一个相册里的所有图片的PHAsset对象
func getAllPHAssetFromOneAlbum(assetCollection:PHAssetCollection)->([PHAsset]){
// 存放所有图片对象
var arr:[PHAsset] = []
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
// 获取所有图片资源对象
let results:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
results.enumerateObjects { (asset, index, stop) in
print("\(asset)")
arr.append(asset)
}
return arr
}
根据PHAsset获取原图片信息
func getImageFromPHAsset(asset:PHAsset,size:CGSize){
var requestID:PHImageRequestID = -2
let scale:CGFloat = CGFloat(UIScreen.main.scale)
let width:CGFloat = CGFloat(min(WIDTH, 500))
if (requestID >= 1 && (size.width) / width == scale) {
PHCachingImageManager.default().cancelImageRequest(requestID)
}
let option:PHImageRequestOptions=PHImageRequestOptions.init()
option.deliveryMode = PHImageRequestOptionsDeliveryMode.opportunistic
option.resizeMode = PHImageRequestOptionsResizeMode.fast;
requestID = PHCachingImageManager.default().requestImageData(for: asset, options: option, resultHandler: { (Dat, str, orientation, [AnyHashable : Any]?) in
//Dat是图片数据
self.image = UIImage.init(data: Dat!)
self.collec?.reloadData()
})
//请求视频的
// requestID = PHCachingImageManager.default().requestAVAsset(forVideo: PHAsset, options: PHVideoRequestOptions?, resultHandler: { (<#AVAsset?#>, AVAudioMix?, [AnyHashable : Any]?) in
//
// })
}
2.*************获取所有相册显示在collectionview,分页加载***************
/**
用来显示所有的相册照片
使用: let looAlbum = LYBMutipleSelectAlbumView.init(frame: CGRect.init(x: 0, y: 0, width: Int(WIDTH), height: Int(HEIGHT)-Int(bottomSafeHeight)))
view.addSubview(looAlbum)
}
*/
import UIKit
import Photos
class LYBMutipleSelectAlbumView: UIView,UICollectionViewDelegate,UICollectionViewDataSource {
var collec:UICollectionView?
var image:UIImage!=UIImage.init(named: "appstart")
var imageArr:[UIImage]=[]//获取到的图片数组
var assetArr: [PHAsset]=[]//获取所有照片资源
var index:Int = 0//记录显示图片的最大索引
let step:Int = 12//每次加载多少张图片
override init(frame: CGRect) {
super.init(frame: frame)
assetArr = getAllAlbumAndPHAsset()//获取照片资源
createCollectionView()
//吧强求到图片资源转换成UIIMgae,在回掉方法中获取图片data数据,然后做相应的操作
pullUprefresh()//先刷新一下
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createCollectionView(){
let flowLayout = UICollectionViewFlowLayout.init()
flowLayout.itemSize=CGSize.init(width: WIDTH/4, height: WIDTH/4)
flowLayout.minimumLineSpacing=0
flowLayout.minimumInteritemSpacing=0
collec = UICollectionView.init(frame: CGRect.init(x: 0, y: 0, width: Int(WIDTH), height: Int(HEIGHT)-Int(bottomSafeHeight)-Int(TopSpaceHigh)), collectionViewLayout: flowLayout)
collec?.backgroundColor=UIColor.white
collec?.delegate=self
collec?.dataSource=self
collec?.register(LYBMutipleSelectAlbumCollectionviewcell.classForCoder(), forCellWithReuseIdentifier: "LYBMutipleSelectAlbumCollectionviewcell")
addSubview(collec!)
initNormalAutoFooterRefresh()//上拉加载
}
//上拉加载
func initNormalAutoFooterRefresh(){
let footer = MJRefreshAutoNormalFooter()
//上刷新相关设置
footer.setRefreshingTarget(self, refreshingAction: #selector(pullUprefresh))
//self.bottom_footer.stateLabel.isHidden = true // 隐藏文字
//是否自动加载(默认为true,即表格滑到底部就自动加载,这个我建议关掉,要不然效果会不好)
footer.isAutomaticallyRefresh = false
footer.isAutomaticallyChangeAlpha = true //自动更改透明度
//修改文字
footer.setTitle("上拉加载", for: .idle)//普通闲置的状态
footer.setTitle("正在加载中", for: .refreshing)//正在刷新的状态
footer.setTitle("加载完成", for: .noMoreData)//数据加载完毕的状态
collec?.mj_footer=footer
}
//上拉刷新
@objc func pullUprefresh(){
if(assetArr.count<=step){//所有资源小于10个,加载实际的个数
for i in 0..<assetArr.count{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
collec?.mj_footer.endRefreshingWithNoMoreData()//没有更多数据
}else {
index+=step
if assetArr.count>index{
for i in index-step..<index{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
}else{//最后几个元素
if assetArr.count>index-step && assetArr.count<index{
for i in index..<assetArr.count{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
}
}
}
print("上拉刷新")
//因为获取图片是异步加载的,所以延迟刷线
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
self.collec?.mj_footer.endRefreshing()
self.collec?.reloadData()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell:LYBMutipleSelectAlbumCollectionviewcell = collectionView.dequeueReusableCell(withReuseIdentifier: "LYBMutipleSelectAlbumCollectionviewcell", for: indexPath) as! LYBMutipleSelectAlbumCollectionviewcell
cell.image = imageArr[indexPath.item]
cell.backgroundColor=UIColor.red
return cell
}
//1.获取所有PHAsset资源的集合,包含视频和照片
func getAllPHAssetFromSysytem()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(with: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
assetsFetchResults.enumerateObjects { (asset, index, stop) in
arr.append(asset)
}
print("\(arr.count)")
return arr
}
//2.先获取所有相册,然后从相机胶卷中获取PHAsset集合,(相机胶卷是相册中的一个,包含了所有视频和相册)
func getAllAlbumAndPHAsset()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
// 所有智能相册集合(系统自动创建的相册)
let smartAlbums:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.albumRegular, options: options)
//遍历得到每一个相册
for i in 0..<smartAlbums.count {
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
let collection:PHCollection = smartAlbums[i];//得到一个相册,一个集合就是一个相册
/**
相册标题英文:
Portrait、Long Exposure、Panoramas、Hidden、Recently Deleted、Live Photos、Videos、Animated、Recently Added、Slo-mo、Time-lapse、Bursts、Camera Roll、Screenshots、Favorites、Selfies
*/
print("相册标题---%@",collection.localizedTitle as Any);
//遍历获取相册
if collection is PHAssetCollection {
if collection.localizedTitle == "相机胶卷" || collection.localizedTitle == "Camera Roll"{//相册的名字是相机交卷,这里面包含了所有的资源,包括照片、视频、gif。 注意相册名字中英文
let assetCollection = collection as! PHAssetCollection
//collection中的资源都统一使用PHFetchResult 对象封装起来。
//得到PHFetchResult封装的图片资源集合
let fetchResult:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
var assetArr:[PHAsset] = []
if fetchResult.count>0{
//某个相册里面的所有PHAsset对象(PHAsset对象对应的是图片,需要通过方法请求到图片)
assetArr = getAllPHAssetFromOneAlbum(assetCollection: assetCollection)
arr.append(contentsOf: assetArr)
}
}
}
}
return arr
}
//获取一个相册里的所有图片的PHAsset对象
func getAllPHAssetFromOneAlbum(assetCollection:PHAssetCollection)->([PHAsset]){
// 存放所有图片对象
var arr:[PHAsset] = []
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
// 获取所有图片资源对象
let results:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
results.enumerateObjects { (asset, index, stop) in
print("\(asset)")
arr.append(asset)
}
return arr
}
根据PHAsset获取原图片信息
func getImageFromPHAsset(asset:PHAsset,size:CGSize){
var requestID:PHImageRequestID = -2
let scale:CGFloat = CGFloat(UIScreen.main.scale)
let width:CGFloat = CGFloat(min(WIDTH, 500))
if (requestID >= 1 && (size.width) / width == scale) {
PHCachingImageManager.default().cancelImageRequest(requestID)
}
let option:PHImageRequestOptions=PHImageRequestOptions.init()
option.deliveryMode = PHImageRequestOptionsDeliveryMode.opportunistic
option.resizeMode = PHImageRequestOptionsResizeMode.fast;
requestID = PHCachingImageManager.default().requestImageData(for: asset, options: option, resultHandler: { (Dat, str, orientation, [AnyHashable : Any]?) in
//Dat是图片数据,吧UIIMage加入到数组中
let image:UIImage=UIImage.init(data: Dat!)!
self.imageArr.append(image)
})
//请求视频的
// requestID = PHCachingImageManager.default().requestAVAsset(forVideo: PHAsset, options: PHVideoRequestOptions?, resultHandler: { (<#AVAsset?#>, AVAudioMix?, [AnyHashable : Any]?) in
//
// })
}
}
*************
/**
相册多选的cell
*/
import UIKit
class LYBMutipleSelectAlbumCollectionviewcell: UICollectionViewCell {
var imageV:UIImageView!
var image:UIImage=UIImage.init(named: "appstart")!{
willSet(image) {
}
didSet {
imageV.image=image
}
}
override init(frame: CGRect) {
super.init(frame: frame)
createCell()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createCell(){
imageV = UIImageView.init(frame:CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
imageV.image=UIImage.init(named: "appstart")
addSubview(imageV)
}
}
3.**************相册多选封装***************
/**
//设置为不选中状态
collectionView.deselectItem(at: indexPath, animated: false)
//这个是用来获取所有的indexpath
collectionView.indexPathsForSelectedItems
用来显示所有的相册照片
使用: let looAlbum = LYBMutipleSelectAlbumView.init(frame: CGRect.init(x: 0, y: 0, width: Int(WIDTH), height: Int(HEIGHT)-Int(bottomSafeHeight)))
looAlbum.selectBlock={
(selectimageArr)in
print("\(selectimageArr)")
}
view.addSubview(looAlbum)
*/
import UIKit
import Photos
class LYBMutipleSelectAlbumView: UIView,UICollectionViewDelegate,UICollectionViewDataSource {
var collec:UICollectionView?
var image:UIImage!=UIImage.init(named: "appstart")
var imageArr:[UIImage]=[]//从系统相册获取到的图片数组
var assetArr: [PHAsset]=[]//从系统相册获取所有照片资源
var index:Int = 0//记录显示图片的最大索引
let step:Int = 12//每次加载多少张图片
var isSelectCell:Bool=false//是否选中cell,默认不选中,不显示对号
var indexArr:[Int]=[]//存放选中cell序号的数组
var selectImageArr:[UIImage]=[]//选中的cell对应的图片
//确认按钮,选中照片
var selectBlock:([UIImage])->()={
([UIImage])->()in
}
override init(frame: CGRect) {
super.init(frame: frame)
assetArr = getAllAlbumAndPHAsset()//获取照片资源
createCollectionView()
//吧强求到图片资源转换成UIIMgae,在回掉方法中获取图片data数据,然后做相应的操作
pullUprefresh()//先刷新一下
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var sureAndCancelView:UIView={
let v:UIView=UIView.init(frame: CGRect.init(x: 0, y: 0, width: WIDTH, height: 50))
let cancelBtn:UIButton=UIButton.init(frame: CGRect.init(x: 20, y: 0, width: 100, height: 50))
cancelBtn.setTitle("取消", for: UIControl.State.normal)
cancelBtn.addTarget(self, action: #selector(cancelClick), for: UIControl.Event.touchUpInside)
cancelBtn.setTitleColor(UIColor.black, for: UIControl.State.normal)
v.addSubview(cancelBtn)
let sureBtn:UIButton=UIButton.init(frame: CGRect.init(x:WIDTH-120, y: 0, width: 100, height: 50))
sureBtn.setTitle("确定", for: UIControl.State.normal)
sureBtn.addTarget(self, action: #selector(sureClick), for: UIControl.Event.touchUpInside)
sureBtn.setTitleColor(UIColor.black, for: UIControl.State.normal)
v.addSubview(sureBtn)
return v
}()
//确定按钮
@objc func sureClick(sender:UIButton){
print("确定")
//吧选中的图片放到一个集合给到外面
for index in indexArr{
selectImageArr.append(imageArr[index])
}
selectBlock(selectImageArr)
self.removeFromSuperview()
}
//取消按钮
@objc func cancelClick(sender:UIButton){
print("取消")
self.removeFromSuperview()
}
func createCollectionView(){
addSubview(sureAndCancelView)
let flowLayout = UICollectionViewFlowLayout.init()
flowLayout.itemSize=CGSize.init(width: WIDTH/4, height: WIDTH/4)
flowLayout.minimumLineSpacing=0
flowLayout.minimumInteritemSpacing=0
collec = UICollectionView.init(frame: CGRect.init(x: 0, y: 50, width: Int(WIDTH), height: Int(HEIGHT)-Int(bottomSafeHeight)-Int(TopSpaceHigh)), collectionViewLayout: flowLayout)
collec?.backgroundColor=UIColor.white
collec?.delegate=self
collec?.dataSource=self
// collec?.register(LYBMutipleSelectAlbumCollectionviewcell.classForCoder(), forCellWithReuseIdentifier: "LYBMutipleSelectAlbumCollectionviewcell")
addSubview(collec!)
collec?.allowsMultipleSelection=true//允许多选
initNormalAutoFooterRefresh()//上拉加载
}
//上拉加载
func initNormalAutoFooterRefresh(){
let footer = MJRefreshAutoNormalFooter()
//上刷新相关设置
footer.setRefreshingTarget(self, refreshingAction: #selector(pullUprefresh))
//self.bottom_footer.stateLabel.isHidden = true // 隐藏文字
//是否自动加载(默认为true,即表格滑到底部就自动加载,这个我建议关掉,要不然效果会不好)
footer.isAutomaticallyRefresh = false
footer.isAutomaticallyChangeAlpha = true //自动更改透明度
//修改文字
footer.setTitle("上拉加载", for: .idle)//普通闲置的状态
footer.setTitle("正在加载中", for: .refreshing)//正在刷新的状态
footer.setTitle("加载完成", for: .noMoreData)//数据加载完毕的状态
collec?.mj_footer=footer
}
//上拉刷新
@objc func pullUprefresh(){
if(assetArr.count<=step){//所有资源小于10个,加载实际的个数
for i in 0..<assetArr.count{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
collec?.mj_footer.endRefreshingWithNoMoreData()//没有更多数据
}else {
index+=step
if assetArr.count>index{
for i in index-step..<index{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
}else{//最后几个元素
if assetArr.count>index-step && assetArr.count<index{
for i in index..<assetArr.count{
self.getImageFromPHAsset(asset:assetArr[i], size: CGSize.init(width: 100, height: 100))
}
}
}
}
print("上拉刷新")
//因为获取图片是异步加载的,所以延迟刷线
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
self.collec?.mj_footer.endRefreshing()
self.collec?.reloadData()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArr.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let CellIdentifier:String = String.init(format: "cell%d%d", indexPath.section,indexPath.item)//以indexPath来唯一确定cell
collec?.register(LYBMutipleSelectAlbumCollectionviewcell.classForCoder(), forCellWithReuseIdentifier: CellIdentifier)
let cell:LYBMutipleSelectAlbumCollectionviewcell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! LYBMutipleSelectAlbumCollectionviewcell
cell.image = imageArr[indexPath.item]
cell.backgroundColor=UIColor.red
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)//取消选中,和显示按钮两回事
let cell:LYBMutipleSelectAlbumCollectionviewcell=collectionView.cellForItem(at: indexPath) as! LYBMutipleSelectAlbumCollectionviewcell
if indexArr.contains(indexPath.item){
//过滤掉已经选中的cell,这里通过filter过滤掉的到一个新的数组,再把新的数组赋值给原来的数组,swift中没有现成的删除指定元素的方法
let filterArr = indexArr.filter {
$0 != indexPath.item//数组中留下的元素都是不等于indexPath.item
}
indexArr=filterArr
cell.isSelect=false
}else {
indexArr.append(indexPath.item)//吧cell的画序号加入到数组
cell.isSelect=true
}
//数组中的序号做一个数组排序,升序
indexArr.sort {
$0<$1
}
print("\(indexPath.item)-----\(indexArr)-----")
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
print("取消选择")
}
//1.获取所有PHAsset资源的集合,包含视频和照片
func getAllPHAssetFromSysytem()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
let assetsFetchResults:PHFetchResult = PHAsset.fetchAssets(with: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
assetsFetchResults.enumerateObjects { (asset, index, stop) in
arr.append(asset)
}
print("\(arr.count)")
return arr
}
//2.先获取所有相册,然后从相机胶卷中获取PHAsset集合,(相机胶卷是相册中的一个,包含了所有视频和相册)
func getAllAlbumAndPHAsset()->([PHAsset]){
var arr:[PHAsset] = []
let options = PHFetchOptions.init()
// 所有智能相册集合(系统自动创建的相册)
let smartAlbums:PHFetchResult = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.albumRegular, options: options)
//遍历得到每一个相册
for i in 0..<smartAlbums.count {
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
let collection:PHCollection = smartAlbums[i];//得到一个相册,一个集合就是一个相册
/**
相册标题英文:
Portrait、Long Exposure、Panoramas、Hidden、Recently Deleted、Live Photos、Videos、Animated、Recently Added、Slo-mo、Time-lapse、Bursts、Camera Roll、Screenshots、Favorites、Selfies
*/
print("相册标题---%@",collection.localizedTitle as Any);
//遍历获取相册
if collection is PHAssetCollection {
if collection.localizedTitle == "相机胶卷" || collection.localizedTitle == "Camera Roll"{//相册的名字是相机交卷,这里面包含了所有的资源,包括照片、视频、gif。 注意相册名字中英文
let assetCollection = collection as! PHAssetCollection
//collection中的资源都统一使用PHFetchResult 对象封装起来。
//得到PHFetchResult封装的图片资源集合
let fetchResult:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
var assetArr:[PHAsset] = []
if fetchResult.count>0{
//某个相册里面的所有PHAsset对象(PHAsset对象对应的是图片,需要通过方法请求到图片)
assetArr = getAllPHAssetFromOneAlbum(assetCollection: assetCollection)
arr.append(contentsOf: assetArr)
}
}
}
}
return arr
}
//获取一个相册里的所有图片的PHAsset对象
func getAllPHAssetFromOneAlbum(assetCollection:PHAssetCollection)->([PHAsset]){
// 存放所有图片对象
var arr:[PHAsset] = []
// 是否按创建时间排序
let options = PHFetchOptions.init()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate",
ascending: false)]//时间排序
options.predicate = NSPredicate.init(format: "mediaType == %ld", PHAssetMediaType.image.rawValue)//˙只选照片
// 获取所有图片资源对象
let results:PHFetchResult = PHAsset.fetchAssets(in: assetCollection, options: options)
// 遍历,得到每一个图片资源asset,然后放到集合中
results.enumerateObjects { (asset, index, stop) in
print("\(asset)")
arr.append(asset)
}
return arr
}
根据PHAsset获取原图片信息
func getImageFromPHAsset(asset:PHAsset,size:CGSize){
var requestID:PHImageRequestID = -2
let scale:CGFloat = CGFloat(UIScreen.main.scale)
let width:CGFloat = CGFloat(min(WIDTH, 500))
if (requestID >= 1 && (size.width) / width == scale) {
PHCachingImageManager.default().cancelImageRequest(requestID)
}
let option:PHImageRequestOptions=PHImageRequestOptions.init()
option.deliveryMode = PHImageRequestOptionsDeliveryMode.opportunistic
option.resizeMode = PHImageRequestOptionsResizeMode.fast;
requestID = PHCachingImageManager.default().requestImageData(for: asset, options: option, resultHandler: { (Dat, str, orientation, [AnyHashable : Any]?) in
//Dat是图片数据,吧UIIMage加入到数组中
let image:UIImage=UIImage.init(data: Dat!)!
print("\(image.size)---压缩前")
//压缩图片
let newSize:CGSize = CGSize.init(width: 100, height: 100)
UIGraphicsBeginImageContext(newSize)
image.draw(in: CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
print("\(String(describing: newImage))---压缩后")
self.imageArr.append(newImage!)
})
//请求视频的
// requestID = PHCachingImageManager.default().requestAVAsset(forVideo: PHAsset, options: PHVideoRequestOptions?, resultHandler: { (<#AVAsset?#>, AVAudioMix?, [AnyHashable : Any]?) in
//
// })
}
}
**********
/**
相册多选的cell
*/
import UIKit
class LYBMutipleSelectAlbumCollectionviewcell: UICollectionViewCell {
var imageV:UIImageView!
var image:UIImage=UIImage.init(named: "appstart")!{
willSet(image) {
}
didSet {
imageV.image=image
}
}
//是否选中
var isSelect:Bool=false{
didSet{
print("\(isSelect)")
if isSelect{
selectbtn.setImage(UIImage.init(named: "comm_btn_checkmark"), for: UIControl.State.normal)
}else {
selectbtn.setImage(UIImage.init(), for: UIControl.State.normal)
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
createCell()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createCell(){
imageV = UIImageView.init(frame:CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
imageV.image=UIImage.init(named: "appstart")
addSubview(imageV)
addSubview(selectbtn)
}
lazy var selectbtn:UIButton={
let btn:UIButton=UIButton.init(frame: CGRect.init(x: 0, y: self.frame.size.width-50, width: 30, height: 30))
return btn
}()
}