TLPhotoPicker
June 3, 2026 ยท View on GitHub
TLPhotoPicker
A modern, flexible photo and video picker for iOS applications. TLPhotoPicker enables selecting media from multiple smart albums with an interface similar to Facebook's photo picker.
Demo ๐
| Facebook Picker | TLPhotoPicker |
|---|---|
![]() | ![]() |
Features
- โ Smart Album Support - Camera roll, selfies, panoramas, favorites, videos, and custom albums
- ๐ฑ Selection Order - Visual order indicators for selected media
- โถ๏ธ Media Playback - Preview videos and Live Photos directly in the picker
- โฑ๏ธ Video Duration - Display video length on thumbnails
- โก High Performance - Async asset loading with excellent scrolling performance
- ๐จ Customizable - Custom cells, selection rules, and UI elements
- ๐ Live Updates - Automatic reload when Photos library changes
- โ๏ธ iCloud Support - Seamless iCloud Photo Library integration
| Smart Albums | Live Photo | Video | Photo | Custom Cell |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
Custom Camera Cell
| Live Camera Cell |
|---|
![]() |
Requirements
- iOS 13.0+
- Swift 5.0+
- Xcode 14.0+
Installation
CocoaPods
platform :ios, '13.0'
pod "TLPhotoPicker"
Swift Package Manager
Add TLPhotoPicker as a dependency in your Package.swift:
dependencies: [
.package(url: "https://github.com/tilltue/TLPhotoPicker.git", .upToNextMajor(from: "2.1.0"))
]
Privacy Configuration
Add the following keys to your Info.plist:
<key>NSPhotoLibraryUsageDescription</key>
<string>Access to photos is required to select images</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is required to take photos</string>
iOS 14+ Limited Photo Access
To suppress automatic prompting, add this to
Info.plist:<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key> <true/>
Quick Start
Basic Usage
import TLPhotoPicker
class ViewController: UIViewController {
@IBAction func openPhotoPicker() {
let picker = TLPhotosPickerViewController()
picker.delegate = self
present(picker, animated: true)
}
}
extension ViewController: TLPhotosPickerViewControllerDelegate {
func dismissPhotoPicker(withTLPHAssets: [TLPHAsset]) {
// Handle selected assets
for asset in withTLPHAssets {
print("Selected: \(asset.originalFileName ?? "Unknown")")
}
}
}
Modern Async/Await (iOS 13+)
// Load images asynchronously
Task {
if let image = await selectedAssets.first?.fullResolutionImage() {
await MainActor.run {
self.imageView.image = image
}
}
}
// Load multiple images concurrently
Task {
let images = await withTaskGroup(of: UIImage?.self) { group in
for asset in selectedAssets {
group.addTask { await asset.fullResolutionImage() }
}
var results: [UIImage] = []
for await image in group {
if let image = image { results.append(image) }
}
return results
}
await MainActor.run {
self.displayImages(images)
}
}
Configuration with Builder Pattern
let picker = TLPhotosPickerViewController()
// Use presets
picker.configure = .singlePhoto
picker.configure = .videoOnly
picker.configure = .compactGrid
// Or build custom configuration
picker.configure = TLPhotosPickerConfigure()
.numberOfColumns(3)
.maxSelection(20)
.allowVideo(true)
.allowLivePhotos(true)
.selectedColor(.systemPink)
.useCameraButton(true)
// Extend presets
picker.configure = .videoOnly
.numberOfColumns(4)
.selectedColor(.systemBlue)
present(picker, animated: true)
Documentation
For detailed information, see:
- Configuration Guide - Complete configuration options
- Advanced Usage - Custom cells, delegates, and rules
- API Reference - TLPHAsset and helper methods
- Migration Guide - Upgrading from older versions
Common Use Cases
Single Photo Selection
picker.configure = .singlePhoto
.selectedColor(.systemPurple)
Video Recording Only
picker.configure = TLPhotosPickerConfigure()
.mediaType(.video)
.allowPhotograph(false)
.allowVideoRecording(true)
Instagram-style Grid
picker.configure = .compactGrid
.maxSelection(10)
.selectedColor(UIColor(red: 0/255, green: 122/255, blue: 255/255, alpha: 1.0))
Custom Selection Rules
picker.canSelectAsset = { asset in
// Only allow images larger than 300x300
return asset.pixelWidth >= 300 && asset.pixelHeight >= 300
}
picker.didExceedMaximumNumberOfSelection = { picker in
// Show alert when limit reached
}
Capture Camera Media URL
let picker = TLPhotosPickerViewController()
picker.didCaptureMediaURL = { url in
// Move or copy this temporary file if you need to keep it.
}
present(picker, animated: true)
When set, camera captures are returned as temporary file URLs instead of being saved to the Photo Library.
Delegate Methods
protocol TLPhotosPickerViewControllerDelegate {
func shouldDismissPhotoPicker(withTLPHAssets: [TLPHAsset]) -> Bool
func dismissPhotoPicker(withTLPHAssets: [TLPHAsset])
func dismissPhotoPicker(withPHAssets: [PHAsset])
func photoPickerDidCancel()
func dismissComplete()
func canSelectAsset(phAsset: PHAsset) -> Bool
func didExceedMaximumNumberOfSelection(picker: TLPhotosPickerViewController)
func handleNoAlbumPermissions(picker: TLPhotosPickerViewController)
func handleNoCameraPermissions(picker: TLPhotosPickerViewController)
}
TLPHAsset
The library provides TLPHAsset, a wrapper around PHAsset with convenient helper methods:
public struct TLPHAsset {
public var phAsset: PHAsset?
public var selectedOrder: Int
public var type: AssetType // .photo, .video, .livePhoto
public var originalFileName: String?
public var isSelectedFromCamera: Bool
// Async image loading
public func fullResolutionImage() async -> UIImage?
// iCloud download
public func cloudImageDownload(
progressBlock: @escaping (Double) -> Void,
completionBlock: @escaping (UIImage?) -> Void
) -> PHImageRequestID?
// Export to file
public func tempCopyMediaFile(
convertLivePhotosToJPG: Bool = false,
progressBlock: ((Double) -> Void)? = nil,
completionBlock: @escaping ((URL, String) -> Void)
) -> PHImageRequestID?
// File size
public func photoSize(completion: @escaping (Int) -> Void)
public func videoSize(completion: @escaping (Int) -> Void)
// Static method
public static func asset(with localIdentifier: String) -> TLPHAsset?
}
See API Reference for complete documentation.
Contributing
Issues and pull requests are welcome! Please check existing issues before creating new ones.
๐ Support This Project
TLPhotoPicker is an open-source project maintained in my free time. If you find it useful, please consider supporting its development:
Your support helps me:
- ๐ Fix bugs and maintain compatibility with latest iOS versions
- โจ Develop new features and improvements
- ๐ Improve documentation and examples
- โก Performance optimizations and code quality
Every contribution is appreciated! ๐
Author
wade.hawk - junhyi.park@gmail.com
Does your organization use TLPhotoPicker? Let me know!
License
TLPhotoPicker is available under the MIT license. See the LICENSE file for details.







