用 Swift 5.0 编写
TLPhotoPicker 允许应用程序从多个智能专辑中选取图片和视频,类似于当前的脸书应用。
🙉
示例Facebook Picker | TLPhotoPicker |
---|---|
![]() |
![]() |
特性
- 支持智能专辑集合。
- 相册、自拍、全景、收藏、视频、自定义用户专辑
- 选择的顺序索引。
- 播放视频和实况照片。
- 只播放一个。在可见单元格范围内播放第一个视频或实况照片。
- 显示视频时长。
- 异步请求数据并显示单元格。
- 滚动性能优于 Facebook 显示视频资产集合。
- 自定义单元格
- 自定义显示和选择规则
- 重载照片库中发生的变化。
- 支持 iCloud 照片图库
- 为图片添加长按预览功能。(致 @smeshko) 预览
智能专辑集合 | LivePhotoCell | VideoPhotoCell | PhotoCell | CustomCell(instagram) |
---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
Custom Camera Cell
Live CameraCell |
---|
![]() |
安装
要求
- Swift 5.0(Swift 4.2 -> 使用 '版本 1.8.3')
- iOS 9.1(用于使用实时照片)
Cocoapods
TLPhotoPicker 可通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 中
platform :ios, '9.1'
pod "TLPhotoPicker"
Carthage
Carthage 是 Cocoa 简单、去中心化的依赖管理器。
在您的项目的 Cartfile 中指定 TLPhotoPicker。
github "tilltue/TLPhotoPicker"
Swift Package Manager
Swift Package Manager 是一个用于自动化 Swift 代码分发的工具,并且集成到 Swift 编译器中。它处于早期开发阶段,但 TLPhotoPicker 支持在支持的平台上使用它。
设置好 Swift package 后,将 Alamofire 作为依赖项添加到您的 Package.swift 的 dependencies 值中即可。
dependencies: [
.package(url: "https://github.com/tilltue/TLPhotoPicker.git", .upToNextMajor(from: "2.1.0"))
]
别忘了在
info.plist
中填写隐私描述。
iOS 14 您可以通过在应用的 info plist 中将此键设置为 yes 来抑制系统的自动提示。PHPhotoLibraryPreventAutomaticLimitedAccessAlert = YES https://developer.apple.com/videos/play/wwdc2020/10641/
用法
使用代理
您可以选择代理方法或闭包来处理选择器事件。
class ViewController: UIViewController,TLPhotosPickerViewControllerDelegate {
var selectedAssets = [TLPHAsset]()
@IBAction func pickerButtonTap() {
let viewController = TLPhotosPickerViewController()
viewController.delegate = self
var configure = TLPhotosPickerConfigure()
//configure.nibSet = (nibName: "CustomCell_Instagram", bundle: Bundle.main) // If you want use your custom cell..
self.present(viewController, animated: true, completion: nil)
}
//TLPhotosPickerViewControllerDelegate
func shouldDismissPhotoPicker(withTLPHAssets: [TLPHAsset]) -> Bool {
// use selected order, fullresolution image
self.selectedAssets = withTLPHAssets
return true
}
func dismissPhotoPicker(withPHAssets: [PHAsset]) {
// if you want to used phasset.
}
func photoPickerDidCancel() {
// cancel
}
func dismissComplete() {
// picker viewcontroller dismiss completion
}
func canSelectAsset(phAsset: PHAsset) -> Bool {
//Custom Rules & Display
//You can decide in which case the selection of the cell could be forbidden.
}
func didExceedMaximumNumberOfSelection(picker: TLPhotosPickerViewController) {
// exceed max selection
}
func handleNoAlbumPermissions(picker: TLPhotosPickerViewController) {
// handle denied albums permissions case
}
func handleNoCameraPermissions(picker: TLPhotosPickerViewController) {
// handle denied camera permissions case
}
}
使用闭包
init(withPHAssets: (([PHAsset]) -> Void)? = nil, didCancel: ((Void) -> Void)? = nil)
init(withTLPHAssets: (([TLPHAsset]) -> Void)? = nil, didCancel: ((Void) -> Void)? = nil)
var canSelectAsset: ((PHAsset) -> Bool)? = nil
var didExceedMaximumNumberOfSelection: ((TLPhotosPickerViewController) -> Void)? = nil
var handleNoAlbumPermissions: ((TLPhotosPickerViewController) -> Void)? = nil
var handleNoCameraPermissions: ((TLPhotosPickerViewController) -> Void)? = nil
var dismissCompletion: (() -> Void)? = nil
class ViewController: UIViewController,TLPhotosPickerViewControllerDelegate {
var selectedAssets = [TLPHAsset]()
@IBAction func pickerButtonTap() {
let viewController = TLPhotosPickerViewController(withTLPHAssets: { [weak self] (assets) in // TLAssets
self?.selectedAssets = assets
}, didCancel: nil)
viewController.didExceedMaximumNumberOfSelection = { [weak self] (picker) in
//exceed max selection
}
viewController.handleNoAlbumPermissions = { [weak self] (picker) in
// handle denied albums permissions case
}
viewController.handleNoCameraPermissions = { [weak self] (picker) in
// handle denied camera permissions case
}
viewController.selectedAssets = self.selectedAssets
self.present(viewController, animated: true, completion: nil)
}
}
自定义单元格 自定义单元格必须继承自 TLPhotoCollectionViewCell
class CustomCell_Instagram: TLPhotoCollectionViewCell {
}
//If you want custom camera cell?
//only used camera cell
[Sample](https://github.com/tilltue/TLPhotoPicker/blob/master/Example/TLPhotoPicker/CustomCameraCell.swift)
//Adding the possibility to handle cell display according to a specific conditions
func update(with phAsset: PHAsset)
func selectedCell()
func willDisplayCell()
func endDisplayingCell()
自定义规则和显示
您可以实现自己的规则来处理单元格的显示。您可以决定在哪种情况下单元格的选择可能被禁止。
例如,如果您想将其宽度小于300的单元格的选择禁用,您可以按照以下步骤进行
- 重写您的自定义单元格的update方法并添加自己的显示规则
override func update(with phAsset: PHAsset) {
super.update(with: phAsset)
self.sizeRequiredOverlayView?.isHidden = !(phAsset.pixelHeight <= 300 && phAsset.pixelWidth <= 300)
}
在此代码中,我们当所需的高度和宽度值满足条件时显示覆盖层。
- 当你实例化一个
TLPhotosPickerViewController
的子类时,你可以传递一个名为canSelectAsset
的闭包,根据某些规则来处理选择。(或代理)
//use delegate
public protocol TLPhotosPickerViewControllerDelegate: class {
...
func canSelectAsset(phAsset: PHAsset) -> Bool
...
}
extension UserViewController: TLPhotosPickerViewControllerDelegate {
func canSelectAsset(phAsset: PHAsset) -> Bool {
if asset.pixelHeight < 100 || asset.pixelWidth < 100 {
self?.showUnsatisifiedSizeAlert(vc: viewController)
return false
}
return true
}
}
//or use closure
viewController.canSelectAsset = { [weak self] asset -> Bool in
if asset.pixelHeight < 100 || asset.pixelWidth < 100 {
self?.showUnsatisifiedSizeAlert(vc: viewController)
return false
}
return true
}
在此代码中,我们显示当闭包中的条件不满足时将显示一个警报。
TLPHAsset
public struct TLPHAsset {
public enum AssetType {
case photo,video,livePhoto
}
// phasset
public var phAsset: PHAsset? = nil
// selected order index
public var selectedOrder: Int = 0
// asset type
public var type: AssetType
// get full resolution image
public var fullResolutionImage: UIImage?
// get photo file size (async)
public func photoSize(options: PHImageRequestOptions? = nil ,completion: @escaping ((Int)->Void), livePhotoVideoSize: Bool = false)
// get video file size (async)
public func videoSize(options: PHVideoRequestOptions? = nil, completion: @escaping ((Int)->Void))
// get async icloud image (download)
@discardableResult
public func cloudImageDownload(progressBlock: @escaping (Double) -> Void, completionBlock:@escaping (UIImage?)-> Void ) -> PHImageRequestID?
// get original media file async copy temporary media file ( photo(png,gif...etc.) and video ) -> Don't forget, You should delete temporary file.
// parmeter : convertLivePhotosToJPG
// false : If you want mov file at live photos
// true : If you want png file at live photos ( HEIC )
public func tempCopyMediaFile(videoRequestOptions: PHVideoRequestOptions? = nil,
imageRequestOptions: PHImageRequestOptions? = nil,
livePhotoRequestOptions: PHLivePhotoRequestOptions? = nil,
exportPreset: String = AVAssetExportPresetHighestQuality,
convertLivePhotosToJPG: Bool = false,
progressBlock:((Double) -> Void)? = nil,
completionBlock:@escaping ((URL,String) -> Void)) -> PHImageRequestID?
//Apparently, This is not the only way to export video.
//There is many way that export a video.
//This method was one of them.
public func exportVideoFile(options: PHVideoRequestOptions? = nil,
outputURL: URL? = nil,
outputFileType: AVFileType = .mov,
progressBlock:((Double) -> Void)? = nil,
completionBlock:@escaping ((URL,String) -> Void))
// get original asset file name
public var originalFileName: String?
}
注意:方便导出方法fullResolutionImage、cloudImageDownload、tempCopyMediaFile、exportVideoFile,如果您想使用更复杂的导出资产选项(进度、导出类型等),这还远远不够。
自定义
let viewController = TLPhotosPickerViewController()
var configure = TLPhotosPickerConfigure()
viewController.configure = configure
public struct TLPhotosPickerConfigure {
public var customLocalizedTitle: [String: String] = ["Camera Roll": "Camera Roll"] // Set [:] if you want use default localized title of album
public var tapHereToChange = "Tap here to change"
public var cancelTitle = "Cancel"
public var doneTitle = "Done"
public var emptyMessage = "No albums"
public var emptyImage: UIImage? = nil
public var usedCameraButton = true
public var usedPrefetch = false
public var previewAtForceTouch = false
public var allowedLivePhotos = true
public var allowedVideo = true
public var allowedAlbumCloudShared = false
public var allowedPhotograph = true // for camera : allow this option when you want to take a photos
public var allowedVideoRecording = true //for camera : allow this option when you want to recording video.
public var recordingVideoQuality: UIImagePickerControllerQualityType = .typeMedium //for camera : recording video quality
public var maxVideoDuration:TimeInterval? = nil //for camera : max video recording duration
public var autoPlay = true
public var muteAudio = true
public var preventAutomaticLimitedAccessAlert = true // newest iOS 14
public var mediaType: PHAssetMediaType? = nil
public var numberOfColumn = 3
public var minimumLineSpacing: CGFloat = 5
public var minimumInteritemSpacing: CGFloat = 5
public var singleSelectedMode = false
public var maxSelectedAssets: Int? = nil //default: inf
public var fetchOption: PHFetchOptions? = nil //default: creationDate
public var fetchCollectionOption: [FetchCollectionType: PHFetchOptions] = [:]
public var singleSelectedMode = false
public var selectedColor = UIColor(red: 88/255, green: 144/255, blue: 255/255, alpha: 1.0)
public var cameraBgColor = UIColor(red: 221/255, green: 223/255, blue: 226/255, alpha: 1)
public var cameraIcon = TLBundle.podBundleImage(named: "camera")
public var videoIcon = TLBundle.podBundleImage(named: "video")
public var placeholderIcon = TLBundle.podBundleImage(named: "insertPhotoMaterial")
public var nibSet: (nibName: String, bundle:Bundle)? = nil // custom cell
public var cameraCellNibSet: (nibName: String, bundle:Bundle)? = nil // custom camera cell
public var fetchCollectionTypes: [(PHAssetCollectionType,PHAssetCollectionSubtype)]? = nil
public var groupByFetch: PHFetchedResultGroupedBy? = nil // cannot be used prefetch options
public var supportedInterfaceOrientations: UIInterfaceOrientationMask = .portrait
public var popup: [PopupConfigure] = []
public init() {
}
}
//Related issue: https://github.com/tilltue/TLPhotoPicker/issues/201
//e.g.
//let option = PHFetchOptions()
//configure.fetchCollectionOption[.assetCollections(.smartAlbum)] = option
//configure.fetchCollectionOption[.assetCollections(.album)] = option
//configure.fetchCollectionOption[.topLevelUserCollections] = option
public enum FetchCollectionType {
case assetCollections(PHAssetCollectionType)
case topLevelUserCollections
}
public enum PopupConfigure {
//Popup album view animation duration
case animation(TimeInterval)
}
// PHFetchedResultGroupedBy
//
// CGrouped by date, cannot be used prefetch options
// take about few seconds ( 5000 image iPhoneX: 1 ~ 1.5 sec )
public enum PHFetchedResultGroupedBy {
case year
case month
case week
case day
case hour
case custom(dateFormat: String)
}
//customizable photos picker viewcontroller
class CustomPhotoPickerViewController: TLPhotosPickerViewController {
override func makeUI() {
super.makeUI()
self.customNavItem.leftBarButtonItem = UIBarButtonItem.init(barButtonSystemItem: .stop, target: nil, action: #selector(customAction))
}
func customAction() {
self.dismiss(animated: true, completion: nil)
}
}
//for log
public protocol TLPhotosPickerLogDelegate: class {
func selectedCameraCell(picker: TLPhotosPickerViewController)
func deselectedPhoto(picker: TLPhotosPickerViewController, at: Int)
func selectedPhoto(picker: TLPhotosPickerViewController, at: Int)
func selectedAlbum(picker: TLPhotosPickerViewController, title: String, at: Int)
}
//for collection supplement view
let viewController = TLPhotosPickerViewController()
viewController.customDataSouces = CustomDataSources() // inherit TLPhotopickerDataSourcesProtocol
public protocol TLPhotopickerDataSourcesProtocol {
func headerReferenceSize() -> CGSize
func footerReferenceSize() -> CGSize
func registerSupplementView(collectionView: UICollectionView)
func supplementIdentifier(kind: String) -> String
func configure(supplement view: UICollectionReusableView, section: (title: String, assets: [TLPHAsset]))
}
作者
您所在的组织或项目使用 TLPhotoPicker 了吗?请通过电子邮件告诉我。
wade.hawk, [email protected]
许可证
TLPhotoPicker 遵循 MIT 许可证。更多信息请参阅 LICENSE 文件。