Image Picker Controller for iOS written in Swift
DKImagePickerController
DKImagePickerController is a highly customizable, pure-Swift library.
Features
- Supports both single and multiple selection.
- Supports filtering albums and sorting by type.
- Supports landscape, iPad, and orientation switching.
- Supports iCloud.
- Supports batch exports
PHAsset
to files. - Inline mode.
- Customizable
UICollectionViewLayout
. - Customizable
camera
,photo gallery
andphoto editor
.
Requirements
- iOS 8.0+
- ARC
- Swift 3.2 & 4.2
Installation
CocoaPods
iOS 8 and newer
DKImagePickerController is available on CocoaPods. Simply add the following line to your podfile:
# For latest release in cocoapods
pod 'DKImagePickerController'
For Swift 4.1
pod 'DKImagePickerController', :git => 'https://github.com/zhangao0086/DKImagePickerController.git', :branch => 'Swift4'
Subspecs
There are 7 subspecs available now:
Subspec | Description |
---|---|
Core | Required. |
ImageDataManager | Required. The subspec provides data to DKImagePickerController . |
Resource | Required. The subspec provides resource management and internationalization. |
PhotoGallery | Optional. The subspec provides preview feature for PHAsset. |
Camera | Optional. The subspec provides camera feature. |
InlineCamera | Optional. The subspec should be pushed by UINavigationController , like UIImagePickerController with UIImagePickerControllerSourceType.camera . |
PhotoEditor | Optional. The subspec provides basic image editing features. |
This means you can install only some of the DKImagePickerController
modules. By default, you get all subspecs.
If you need to use your own photo editor, simply specify subspecs other than PhotoEditor
:
pod 'DKImagePickerController', :subspecs => ['PhotoGallery', 'Camera', 'InlineCamera']
Carthage
github "zhangao0086/DKImagePickerController"
If you use Carthage to build your dependencies, make sure you have added CropViewController.framework
, DKCamera.framework
, DKImagePickerController.framework
, DKPhotoGallery.framework
and SDWebImage.framework
to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase.
Getting Started
Initialization and presentation
let pickerController = DKImagePickerController()
pickerController.didSelectAssets = { (assets: [DKAsset]) in
print("didSelectAssets")
print(assets)
}
self.presentViewController(pickerController, animated: true) {}
Configurations
/// Use UIDelegate to Customize the picker UI.
@objc public var UIDelegate: DKImagePickerControllerBaseUIDelegate!
/// Forces deselect of previous selected image. allowSwipeToSelect will be ignored.
@objc public var singleSelect = false
/// Auto close picker on single select
@objc public var autoCloseOnSingleSelect = true
/// The maximum count of assets which the user will be able to select, a value of 0 means no limit.
@objc public var maxSelectableCount = 0
/// Photos will be tagged with the location where they are taken.
/// If true, your Info.plist should include the "Privacy - Location XXX" tag.
open var containsGPSInMetadata = false
/// Set the defaultAssetGroup to specify which album is the default asset group.
public var defaultAssetGroup: PHAssetCollectionSubtype?
/// Allow swipe to select images.
@objc public var allowSwipeToSelect: Bool = false
/// Allow select all
@objc public var allowSelectAll: Bool = false
/// A Bool value indicating whether the inline mode is enabled.
@objc public var inline: Bool = false
/// The type of picker interface to be displayed by the controller.
@objc public var assetType: DKImagePickerControllerAssetType = .allAssets
/// If sourceType is Camera will cause the assetType & maxSelectableCount & allowMultipleTypes & defaultSelectedAssets to be ignored.
@objc public var sourceType: DKImagePickerControllerSourceType = .both
/// A Bool value indicating whether allows to select photos and videos at the same time.
@objc public var allowMultipleTypes = true
/// A Bool value indicating whether to allow the picker auto-rotate the screen.
@objc public var allowsLandscape = false
/// Set the showsEmptyAlbums to specify whether or not the empty albums is shown in the picker.
@objc public var showsEmptyAlbums = true
/// A Bool value indicating whether to allow the picker shows the cancel button.
@objc public var showsCancelButton = false
/// The block is executed when the user presses the cancel button.
@objc public var didCancel: (() -> Void)?
/// The block is executed when the user presses the select button.
@objc public var didSelectAssets: ((_ assets: [DKAsset]) -> Void)?
/// The block is executed when the number of selected assets is changed.
@objc public var selectedChanged: (() -> Void)?
/// A Bool value indicating whether to allow the picker to auto-export the selected assets to the specified directory when done is called.
/// picker will creating a default exporter if exportsWhenCompleted is true and the exporter is nil.
@objc public var exportsWhenCompleted = false
@objc public var exporter: DKImageAssetExporter?
/// Indicates the status of the exporter.
@objc public private(set) var exportStatus = DKImagePickerControllerExportStatus.none {
willSet {
if self.exportStatus != newValue {
self.willChangeValue(forKey: #keyPath(DKImagePickerController.exportStatus))
}
}
didSet {
if self.exportStatus != oldValue {
self.didChangeValue(forKey: #keyPath(DKImagePickerController.exportStatus))
self.exportStatusChanged?(self.exportStatus)
}
}
}
/// The block is executed when the exportStatus is changed.
@objc public var exportStatusChanged: ((DKImagePickerControllerExportStatus) -> Void)?
/// The object that acts as the data source of the picker.
@objc public private(set) lazy var groupDataManager: DKImageGroupDataManager
Inline Mode
let groupDataManagerConfiguration = DKImageGroupDataManagerConfiguration()
groupDataManagerConfiguration.fetchLimit = 10
groupDataManagerConfiguration.assetGroupTypes = [.smartAlbumUserLibrary]
let groupDataManager = DKImageGroupDataManager(configuration: groupDataManagerConfiguration)
self.pickerController = DKImagePickerController(groupDataManager: groupDataManager)
pickerController.inline = true
pickerController.UIDelegate = CustomInlineLayoutUIDelegate(imagePickerController: pickerController)
pickerController.assetType = .allPhotos
pickerController.sourceType = .photo
let pickerView = self.pickerController.view!
pickerView.frame = CGRect(x: 0, y: 170, width: self.view.bounds.width, height: 200)
self.view.addSubview(pickerView)
Customizable UI
For example, see CustomUIDelegate.
Customizable Layout
For example, see CustomLayoutUIDelegate.
Conforms UIAppearance protocol
You can easily customize the appearance of the navigation bar using the appearance proxy.
UINavigationBar.appearance().titleTextAttributes = [
NSFontAttributeName : UIFont(name: "Optima-BoldItalic", size: 21)!,
NSForegroundColorAttributeName : UIColor.redColor()
]
Exporting to file
By default, the picker uses a singleton object of DKImageAssetExporter
to export DKAsset
to local files.
/*
Configuration options for a DKImageAssetExporter. When an exporter is created,
a copy of the configuration object is made - you cannot modify the configuration
of an exporter after it has been created.
*/
@objc
public class DKImageAssetExporterConfiguration: NSObject, NSCopying {
@objc public var imageExportPreset = DKImageExportPresent.compatible
/// videoExportPreset can be used to specify the transcoding quality for videos (via a AVAssetExportPreset* string).
@objc public var videoExportPreset = AVAssetExportPresetHighestQuality
#if swift(>=4.0)
@objc public var avOutputFileType = AVFileType.mov
#else
@objc public var avOutputFileType = AVFileTypeQuickTimeMovie
#endif
@objc public var exportDirectory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("DKImageAssetExporter")
}
/*
A DKImageAssetExporter object exports DKAsset(PHAsset) from album (or iCloud) to the app's tmp directory (by default).
It automatically deletes the exported directories when it receives a UIApplicationWillTerminate notification.
*/
@objc
open class DKImageAssetExporter: DKBaseManager {
/// This method starts an asynchronous export operation of a batch of asset.
@discardableResult
@objc public func exportAssetsAsynchronously(assets: [DKAsset], completion: ((_ info: [AnyHashable : Any]) -> Void)?) -> DKImageAssetExportRequestID
}
This exporter can automatically convert HEIF to JPEG:
@objc
public enum DKImageExportPresent: Int {
case
compatible, // A preset for converting HEIF formatted images to JPEG.
current // A preset for passing image data as-is to the client.
}
You also can observe the export progress of each asset:
@objc
public protocol DKImageAssetExporterObserver {
@objc optional func exporterWillBeginExporting(exporter: DKImageAssetExporter, asset: DKAsset)
/// The progress can be obtained from the DKAsset.
@objc optional func exporterDidUpdateProgress(exporter: DKImageAssetExporter, asset: DKAsset)
/// When the asset's error is not nil, it indicates that an error occurred while exporting.
@objc optional func exporterDidEndExporting(exporter: DKImageAssetExporter, asset: DKAsset)
}
extension DKAsset {
/// The exported file will be placed in this location.
/// All exported files can be automatically cleaned by the DKImageAssetDiskPurger when appropriate.
@objc public var localTemporaryPath: URL?
@objc public var fileName: String?
/// Indicates the file's size in bytes.
@objc public var fileSize: UInt
/// If you export an asset whose data is not on the local device, and you have enabled downloading with the isNetworkAccessAllowed property, the progress indicates the progress of the download. A value of 0.0 indicates that the download has just started, and a value of 1.0 indicates the download is complete.
@objc public var progress: Double
/// Describes the error that occurred if the export has failed or been cancelled.
@objc public var error: Error?
}
For example, see Export automatically
and Export manually
.
Extensions
This picker uses DKImageExtensionController
manages all extensions, you can register it with a DKImageBaseExtension
and a specified DKImageExtensionType
to customize camera
, photo gallery
and photo editor
:
/// Registers an extension for the specified type.
public class func registerExtension(extensionClass: DKImageBaseExtension.Type, for type: DKImageExtensionType)
public class func unregisterExtension(for type: DKImageExtensionType)
The perform
function will be called with a dictionary providing current context information when an extension is triggered:
/// Starts the extension.
func perform(with extraInfo: [AnyHashable: Any])
/// Completes the extension.
func finish()
The extraInfo
will provide different information for different DKImageExtensionType
:
Camera
let didFinishCapturingImage = extraInfo["didFinishCapturingImage"] as? ((UIImage, [AnyHashable : Any]?) -> Void)
let didCancel = extraInfo["didCancel"] as? (() -> Void)
For a custom camera example, see CustomCameraExtension.
InlineCamera
The extraInfo
is the same as for Camera
.
Photo Gallery
let groupId = extraInfo["groupId"] as? String
let presentationIndex = extraInfo["presentationIndex"] as? Int
let presentingFromImageView = extraInfo["presentingFromImageView"] as? UIImageView
Photo Editor
let image = extraInfo["image"] as? UIImage
let didFinishEditing = extraInfo["didFinishEditing"] as? ((UIImage, [AnyHashable : Any]?) -> Void)
let metadata = extraInfo["metadata"] as? [AnyHashable : Any]
How to use in Objective-C
If you use CocoaPods
-
Add the following two lines into your
Podfile
:pod 'DKImagePickerController' use_frameworks!
-
Import the library into your Objective-C file:
#import <DKImagePickerController/DKImagePickerController-Swift.h>
If you use it directly in your project
-
Drag and drop the DKCamera,
DKImageManager
andDKImagePickerController
to your project -
Import the library into your Objective-C file:
#import "YourProductModuleName-Swift.h"
then you can:
DKImagePickerController *pickerController = [DKImagePickerController new];
[pickerController setDidSelectAssets:^(NSArray * __nonnull assets) {
NSLog(@"didSelectAssets");
}];
[self presentViewController:pickerController animated:YES completion:nil];
Localization
The default supported languages:
en, es, da, de, fr, hu, ja, ko, nb-NO, pt_BR, ru, tr, ur, vi, ar, it, zh-Hans, zh-Hant
You can also add a hook to return your own localized string:
DKImagePickerControllerResource.customLocalizationBlock = { title in
if title == "picker.select.title" {
return "Test(%@)"
} else {
return nil
}
}
or images:
DKImagePickerControllerResource.customImageBlock = { imageName in
if imageName == "camera" {
return DKImagePickerControllerResource.photoGalleryCheckedImage()
} else {
return nil
}
}