TLPhotoPicker 2.1.10

TLPhotoPicker 2.1.10

测试已测试
Lang语言 SwiftSwift
许可证 MIT
Released最后发布Jul 2024
SPM支持 SPM

Wade Hawk 维护。



  • 作者:
  • wade.hawk

Version License Platform Swift

用 Swift 5.0 编写

TLPhotoPicker 允许应用程序从多个智能专辑中选取图片和视频,类似于当前的脸书应用。

示例🙉

Facebook Picker TLPhotoPicker
Facebook Picker TLPhotoPicker

特性

  • 支持智能专辑集合。
    • 相册、自拍、全景、收藏、视频、自定义用户专辑
  • 选择的顺序索引。
  • 播放视频和实况照片。
    • 只播放一个。在可见单元格范围内播放第一个视频或实况照片。
  • 显示视频时长。
  • 异步请求数据并显示单元格。
    • 滚动性能优于 Facebook 显示视频资产集合。
  • 自定义单元格
  • 自定义显示和选择规则
  • 重载照片库中发生的变化。
  • 支持 iCloud 照片图库
  • 为图片添加长按预览功能。(致 @smeshko) 预览
智能专辑集合 LivePhotoCell VideoPhotoCell PhotoCell CustomCell(instagram)
Facebook Picker LivePhotoCell VideoPhotoCell PhotoCell PhotoCell

Custom Camera Cell

Live CameraCell
Like Line

安装

要求

  • 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 文件。