Commit 658026d5 by Daniel Dahan

updated initial PhotoLibrary codebase to include a caching instance as well as…

updated initial PhotoLibrary codebase to include a caching instance as well as collection dictionary
parent 9da25cb1
...@@ -172,6 +172,7 @@ ...@@ -172,6 +172,7 @@
96E3C39C1D3A1CC20086A024 /* Offset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 968C99461D377849000074FF /* Offset.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 96E3C39C1D3A1CC20086A024 /* Offset.swift in Headers */ = {isa = PBXBuildFile; fileRef = 968C99461D377849000074FF /* Offset.swift */; settings = {ATTRIBUTES = (Public, ); }; };
96E3C39E1D3A1D0C0086A024 /* BasicAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E3C39D1D3A1D0C0086A024 /* BasicAnimation.swift */; }; 96E3C39E1D3A1D0C0086A024 /* BasicAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E3C39D1D3A1D0C0086A024 /* BasicAnimation.swift */; };
96EA9A431D4E68F80052C74D /* PhotoLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */; }; 96EA9A431D4E68F80052C74D /* PhotoLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */; };
96EA9A4B1D4E7A430052C74D /* PhotoLibraryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96EA9A4A1D4E7A430052C74D /* PhotoLibraryController.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
...@@ -283,6 +284,7 @@ ...@@ -283,6 +284,7 @@
96E3C3931D397AE90086A024 /* Material+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIView.swift"; sourceTree = "<group>"; }; 96E3C3931D397AE90086A024 /* Material+UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Material+UIView.swift"; sourceTree = "<group>"; };
96E3C39D1D3A1D0C0086A024 /* BasicAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicAnimation.swift; sourceTree = "<group>"; }; 96E3C39D1D3A1D0C0086A024 /* BasicAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicAnimation.swift; sourceTree = "<group>"; };
96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibrary.swift; sourceTree = "<group>"; }; 96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibrary.swift; sourceTree = "<group>"; };
96EA9A4A1D4E7A430052C74D /* PhotoLibraryController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -652,6 +654,7 @@ ...@@ -652,6 +654,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */, 96EA9A421D4E68F80052C74D /* PhotoLibrary.swift */,
96EA9A4A1D4E7A430052C74D /* PhotoLibraryController.swift */,
); );
name = PhotoLibrary; name = PhotoLibrary;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -946,6 +949,7 @@ ...@@ -946,6 +949,7 @@
96BCB7BA1CB40DC500C806FE /* CollectionView.swift in Sources */, 96BCB7BA1CB40DC500C806FE /* CollectionView.swift in Sources */,
96BCB7A31CB40DC500C806FE /* CapturePreview.swift in Sources */, 96BCB7A31CB40DC500C806FE /* CapturePreview.swift in Sources */,
96BCB7BC1CB40DC500C806FE /* CollectionViewDataSource.swift in Sources */, 96BCB7BC1CB40DC500C806FE /* CollectionViewDataSource.swift in Sources */,
96EA9A4B1D4E7A430052C74D /* PhotoLibraryController.swift in Sources */,
96E3C3941D397AE90086A024 /* Material+UIView.swift in Sources */, 96E3C3941D397AE90086A024 /* Material+UIView.swift in Sources */,
96BCB7C71CB40DC500C806FE /* KeyframeAnimation.swift in Sources */, 96BCB7C71CB40DC500C806FE /* KeyframeAnimation.swift in Sources */,
96BCB7BE1CB40DC500C806FE /* CollectionViewLayout.swift in Sources */, 96BCB7BE1CB40DC500C806FE /* CollectionViewLayout.swift in Sources */,
......
...@@ -78,16 +78,40 @@ public protocol PhotoLibraryDelegate { ...@@ -78,16 +78,40 @@ public protocol PhotoLibraryDelegate {
optional func photoLibrary(photoLibrary: PhotoLibrary, didChange changeInfo: PHChange) optional func photoLibrary(photoLibrary: PhotoLibrary, didChange changeInfo: PHChange)
/** /**
Get changes objects in album. A delegation method that is executed when changes are detected.
- Parameter photoLibrary: A reference to the PhotoLibrary. - Parameter photoLibrary: A reference to the PhotoLibrary.
- Parameter object: A PHObject instance. - Parameter beforeChanges: A PHObject before changes.
- Parameter afterChanges: A PHObject after changes.
- Parameter assetContentChanged: A Bool that is true if the image or video content for this
object has changed.
- Parameter objectWasDeleted: A Bool that is true if the object was deleted.
*/ */
@objc @objc
optional func photoLibrary(photoLibrary: PhotoLibrary, afterChanges object: PHObject) optional func photoLibrary(photoLibrary: PhotoLibrary, beforeChanges: PHObject, afterChanges: PHObject, assetContentChanged: Bool, objectWasDeleted: Bool)
} }
@objc(PhotoLibrary) @objc(PhotoLibrary)
public class PhotoLibrary: NSObject { public class PhotoLibrary: NSObject {
/// A reference to the PHCachingImageManager.
public private(set) lazy var cachingImageManager = PHCachingImageManager()
/// A reference to the currently fetched album.
public private(set) var fetchResult: PHFetchResult<PHAssetCollection>?
/// The assets used in the album.
public private(set) var collections: [PHAssetCollection: [PHAsset]]! {
willSet {
cachingImageManager.stopCachingImagesForAllAssets()
}
didSet {
cachingImageManager.allowsCachingHighQualityImages = true
for (_, assets) in collections {
cachingImageManager.startCachingImages(for: assets, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: nil)
}
}
}
/// A reference to a PhotoLibraryDelegate. /// A reference to a PhotoLibraryDelegate.
public weak var delegate: PhotoLibraryDelegate? public weak var delegate: PhotoLibraryDelegate?
...@@ -107,16 +131,6 @@ public class PhotoLibrary: NSObject { ...@@ -107,16 +131,6 @@ public class PhotoLibrary: NSObject {
prepare() prepare()
} }
/// A method used to prepare the instance object.
public func prepare() {
prepareChangeObservers()
}
/// A method used to enable change observation.
public func prepareChangeObservers() {
PHPhotoLibrary.shared().register(self)
}
/** /**
A method to request authorization from the user to enable photo library access. In order A method to request authorization from the user to enable photo library access. In order
for this to work, set the "Privacy - Photo Library Usage Description" value in the for this to work, set the "Privacy - Photo Library Usage Description" value in the
...@@ -154,29 +168,83 @@ public class PhotoLibrary: NSObject { ...@@ -154,29 +168,83 @@ public class PhotoLibrary: NSObject {
} }
} }
public func moments(in momentList: PHCollectionList, options: PHFetchOptions?) -> [PHAssetCollection] { /**
var v = [PHAssetCollection]() Fetch different PHAssetCollections asynchronously based on different types and subtypes
PHAssetCollection.fetchMoments(inMomentList: momentList, options: options).enumerateObjects(options: EnumerationOptions.concurrent) { (collection, _, _) in with an optional completion block.
v.append(collection) - Parameter type: A PHAssetCollectionType.
- Parameter subtype: A PHAssetCollectionSubtype.
- Parameter completion: An optional completion block.
*/
public func fetch(type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, completion: ([PHAssetCollection: [PHAsset]]) -> Void) {
DispatchQueue.global(attributes: DispatchQueue.GlobalAttributes.qosDefault).async { [weak self, type = type, subtype = subtype, completion = completion] in
guard let s = self else {
return
}
defer {
DispatchQueue.main.async { [weak self] in
completion(s.collections)
}
}
let options = PHFetchOptions()
options.includeHiddenAssets = true
options.includeAllBurstAssets = true
options.wantsIncrementalChangeDetails = false
s.fetchResult = PHAssetCollection.fetchAssetCollections(with: type, subtype: subtype, options: options)
s.fetchResult?.enumerateObjects(options: []) { [weak self] (collection, _, _) in
guard let s = self else {
return
}
let options = PHFetchOptions()
let descriptor = SortDescriptor(key: "creationDate", ascending: false)
options.sortDescriptors = [descriptor]
options.includeHiddenAssets = true
options.includeAllBurstAssets = true
var assets = [PHAsset]()
PHAsset.fetchAssets(in: collection, options: options).enumerateObjects(options: []) { (asset, _, _) in
assets.append(asset)
}
s.collections[collection] = assets
}
} }
return v
} }
public func fetch(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection> { /// A method used to prepare the instance object.
let result = PHAssetCollection.fetchAssetCollections(with: type, subtype: subtype, options: options) private func prepare() {
return result prepareCollections()
prepareChangeObservers()
}
/// Prepares the collections.
private func prepareCollections() {
collections = [PHAssetCollection: [PHAsset]]()
} }
public func performChanges(_ changeBlock: () -> Void, completionHandler: ((Bool, NSError?) -> Void)? = nil) { /// A method used to enable change observation.
PHPhotoLibrary.shared().performChanges(changeBlock, completionHandler: completionHandler) private func prepareChangeObservers() {
PHPhotoLibrary.shared().register(self)
} }
public func assets(in album: PHAssetCollection, options: PHFetchOptions?) -> [PHAsset] { // public func moments(in momentList: PHCollectionList, options: PHFetchOptions?) -> [PHAssetCollection] {
var v = [PHAsset]() // var v = [PHAssetCollection]()
PHAsset.fetchAssets(in: album, options: options).enumerateObjects(options: []) { (asset, _, _) in // PHAssetCollection.fetchMoments(inMomentList: momentList, options: options).enumerateObjects(options: EnumerationOptions.concurrent) { (collection, _, _) in
v.append(asset) // v.append(collection)
} // }
return v // return v
// }
//
// public func fetch(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, options: PHFetchOptions?) -> PHFetchResult<PHAssetCollection> {
// let result = PHAssetCollection.fetchAssetCollections(with: type, subtype: subtype, options: options)
// return result
// }
//
public func performChanges(_ changeBlock: () -> Void, completionHandler: ((Bool, NSError?) -> Void)? = nil) {
PHPhotoLibrary.shared().performChanges(changeBlock, completionHandler: completionHandler)
} }
public func image(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, completion: (UIImage?, [NSObject: AnyObject]?) -> Void) -> PHImageRequestID { public func image(for asset: PHAsset, targetSize: CGSize, contentMode: PHImageContentMode, options: PHImageRequestOptions?, completion: (UIImage?, [NSObject: AnyObject]?) -> Void) -> PHImageRequestID {
...@@ -186,24 +254,12 @@ public class PhotoLibrary: NSObject { ...@@ -186,24 +254,12 @@ public class PhotoLibrary: NSObject {
public func data(for asset: PHAsset, options: PHImageRequestOptions?, completion: (Data?, String?, UIImageOrientation, [NSObject: AnyObject]?) -> Void) -> PHImageRequestID { public func data(for asset: PHAsset, options: PHImageRequestOptions?, completion: (Data?, String?, UIImageOrientation, [NSObject: AnyObject]?) -> Void) -> PHImageRequestID {
return PHImageManager.default().requestImageData(for: asset, options: options, resultHandler: completion) return PHImageManager.default().requestImageData(for: asset, options: options, resultHandler: completion)
} }
public func cancel(for requestID: PHImageRequestID) { public func cancel(for requestID: PHImageRequestID) {
PHImageManager.default().cancelImageRequest(requestID) PHImageManager.default().cancelImageRequest(requestID)
} }
} }
/// Albums.
extension PhotoLibrary {
/**
Creates an album with a given title.
- Parameter album title: A String.
- Returns: A PHObjectPlaceholder.
*/
public func create(album title: String) -> PHObjectPlaceholder {
return PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: title).placeholderForCreatedAssetCollection
}
}
/// PHPhotoLibraryChangeObserver extension. /// PHPhotoLibraryChangeObserver extension.
extension PhotoLibrary: PHPhotoLibraryChangeObserver { extension PhotoLibrary: PHPhotoLibraryChangeObserver {
/** /**
...@@ -211,7 +267,27 @@ extension PhotoLibrary: PHPhotoLibraryChangeObserver { ...@@ -211,7 +267,27 @@ extension PhotoLibrary: PHPhotoLibraryChangeObserver {
- Parameter _ changeInstance: A PHChange obejct describing the changes in the - Parameter _ changeInstance: A PHChange obejct describing the changes in the
photo library. photo library.
*/ */
public func photoLibraryDidChange(_ changeInstance: PHChange) { public func photoLibraryDidChange(_ changeInfo: PHChange) {
delegate?.photoLibrary?(photoLibrary: self, didChange: changeInstance) delegate?.photoLibrary?(photoLibrary: self, didChange: changeInfo)
guard let result = fetchResult else {
return
}
result.enumerateObjects(options: []) { [weak self, changeInfo = changeInfo] (collection, _, _) in
guard let s = self else {
return
}
guard let details = changeInfo.changeDetails(for: collection) else {
return
}
guard let afterChanges = details.objectAfterChanges else {
return
}
s.delegate?.photoLibrary?(photoLibrary: s, beforeChanges: details.objectBeforeChanges, afterChanges: afterChanges, assetContentChanged: details.assetContentChanged, objectWasDeleted: details.objectWasDeleted)
}
} }
} }
/*
* Copyright (C) 2015 - 2016, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.io>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import Photos
public struct Item {
var album: PHAssetCollection
var assets: [PHAsset]
}
public class PhotoLibraryController: UIViewController, PhotoLibraryDelegate {
/// A reference to a PhotoLibrary.
public private(set) var photoLibrary: PhotoLibrary!
public override func viewDidLoad() {
super.viewDidLoad()
preparePhotoLibrary()
}
/**
Executes an authorization request with an optional completion block.
- Parameter completion: An optional completion block that is executed when
authorization status is determined.
*/
public func authorizePhotoLibrary(_ completion: ((PHAuthorizationStatus) -> Void)? = nil) {
photoLibrary.requestAuthorization(completion)
}
/// Prepares the photoLibrary.
private func preparePhotoLibrary() {
photoLibrary = PhotoLibrary()
photoLibrary.delegate = self
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment