Commit 18e10652 by Daniel Dahan

development: updated PhotoLibrary sample

parent d4a97c1e
......@@ -8,18 +8,39 @@
/* Begin PBXBuildFile section */
96784F561D901F7C0061C06C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784F551D901F7C0061C06C /* AppDelegate.swift */; };
96784F581D901F7C0061C06C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784F571D901F7C0061C06C /* ViewController.swift */; };
96784F5D1D901F7C0061C06C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96784F5C1D901F7C0061C06C /* Assets.xcassets */; };
96784F601D901F7C0061C06C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 96784F5E1D901F7C0061C06C /* LaunchScreen.storyboard */; };
96784FB61D905DBF0061C06C /* PhotoLibraryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784FB51D905DBF0061C06C /* PhotoLibraryViewController.swift */; };
96784FB81D905E3A0061C06C /* PhotoLibraryCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784FB71D905E3A0061C06C /* PhotoLibraryCollectionView.swift */; };
96784FBA1D905E750061C06C /* PhotoLibraryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784FB91D905E750061C06C /* PhotoLibraryCollectionViewCell.swift */; };
96784FC01D9061920061C06C /* AppToolbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784FBF1D9061920061C06C /* AppToolbarController.swift */; };
96784FC21D9063A50061C06C /* PhotoLibraryCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96784FC11D9063A50061C06C /* PhotoLibraryCollectionReusableView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
96784FBE1D905EEA0061C06C /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
96784F521D901F7C0061C06C /* PhotoLibraryController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PhotoLibraryController.app; sourceTree = BUILT_PRODUCTS_DIR; };
96784F551D901F7C0061C06C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
96784F571D901F7C0061C06C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
96784F5C1D901F7C0061C06C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
96784F5F1D901F7C0061C06C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
96784F611D901F7C0061C06C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
96784FB51D905DBF0061C06C /* PhotoLibraryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryViewController.swift; sourceTree = "<group>"; };
96784FB71D905E3A0061C06C /* PhotoLibraryCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryCollectionView.swift; sourceTree = "<group>"; };
96784FB91D905E750061C06C /* PhotoLibraryCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryCollectionViewCell.swift; sourceTree = "<group>"; };
96784FBF1D9061920061C06C /* AppToolbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppToolbarController.swift; sourceTree = "<group>"; };
96784FC11D9063A50061C06C /* PhotoLibraryCollectionReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryCollectionReusableView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -53,7 +74,11 @@
isa = PBXGroup;
children = (
96784F551D901F7C0061C06C /* AppDelegate.swift */,
96784F571D901F7C0061C06C /* ViewController.swift */,
96784FBF1D9061920061C06C /* AppToolbarController.swift */,
96784FB51D905DBF0061C06C /* PhotoLibraryViewController.swift */,
96784FB71D905E3A0061C06C /* PhotoLibraryCollectionView.swift */,
96784FC11D9063A50061C06C /* PhotoLibraryCollectionReusableView.swift */,
96784FB91D905E750061C06C /* PhotoLibraryCollectionViewCell.swift */,
96784F5C1D901F7C0061C06C /* Assets.xcassets */,
96784F5E1D901F7C0061C06C /* LaunchScreen.storyboard */,
96784F611D901F7C0061C06C /* Info.plist */,
......@@ -71,6 +96,7 @@
96784F4E1D901F7C0061C06C /* Sources */,
96784F4F1D901F7C0061C06C /* Frameworks */,
96784F501D901F7C0061C06C /* Resources */,
96784FBE1D905EEA0061C06C /* Embed Frameworks */,
);
buildRules = (
);
......@@ -93,6 +119,7 @@
TargetAttributes = {
96784F511D901F7C0061C06C = {
CreatedOnToolsVersion = 8.0;
DevelopmentTeam = 9Z76XCNLGL;
ProvisioningStyle = Automatic;
};
};
......@@ -132,8 +159,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
96784F581D901F7C0061C06C /* ViewController.swift in Sources */,
96784FC21D9063A50061C06C /* PhotoLibraryCollectionReusableView.swift in Sources */,
96784F561D901F7C0061C06C /* AppDelegate.swift in Sources */,
96784FBA1D905E750061C06C /* PhotoLibraryCollectionViewCell.swift in Sources */,
96784FB81D905E3A0061C06C /* PhotoLibraryCollectionView.swift in Sources */,
96784FB61D905DBF0061C06C /* PhotoLibraryViewController.swift in Sources */,
96784FC01D9061920061C06C /* AppToolbarController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -248,6 +279,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 9Z76XCNLGL;
INFOPLIST_FILE = PhotoLibraryController/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.PhotoLibraryController;
......@@ -260,6 +292,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = 9Z76XCNLGL;
INFOPLIST_FILE = PhotoLibraryController/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = io.cosmicmind.PhotoLibraryController;
......@@ -287,6 +320,7 @@
96784F661D901F7C0061C06C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
......
//
// AppDelegate.swift
// PhotoLibraryController
//
// Created by Daniel Dahan on 2016-09-19.
// Copyright © 2016 CosmicMind. All rights reserved.
//
/*
* 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 UIKit
import Material
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
func applicationDidFinishLaunching(_ application: UIApplication) {
window = UIWindow(frame: Device.bounds)
window!.rootViewController = AppToolbarController(rootViewController: PhotoLibraryViewController())
window!.makeKeyAndVisible()
}
func applicationWillResignActive(_ application: UIApplication) {
......
/*
* 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 UIKit
import Material
class AppToolbarController: ToolbarController {
open override func prepare() {
super.prepare()
statusBarStyle = .default
prepareToolbar()
}
private func prepareToolbar() {
toolbar.title = "Photo Library"
toolbar.depthPreset = .none
toolbar.divider.color = Color.grey.lighten3
}
}
......@@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSPhotoLibraryUsageDescription</key>
<string>May I access the Photo Library?</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
......
/*
* 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 UIKit
import Material
class PhotoLibraryCollectionViewCell: CollectionViewCell {
open override func prepare() {
super.prepare()
pulseAnimation = .backing
contentsGravityPreset = .ResizeAspectFill
}
}
/*
* 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 UIKit
import Material
class PhotoLibraryCollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
prepare()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
func prepare() {
register(PhotoLibraryCollectionReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "PhotoLibraryCollectionReusableView")
register(PhotoLibraryCollectionViewCell.self, forCellWithReuseIdentifier: "PhotoLibraryCollectionViewCell")
}
}
/*
* 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 UIKit
import Material
class PhotoLibraryCollectionReusableView: CollectionReusableView {
/// A reference to the toolbar.
private(set) var toolbar: Toolbar!
open override func prepare() {
super.prepare()
backgroundColor = nil
prepareToolbar()
}
/// Prepares the toolbar.
private func prepareToolbar() {
toolbar = Toolbar()
toolbar.titleLabel.font = RobotoFont.regular(with: 14)
toolbar.titleLabel.textAlignment = .left
toolbar.contentEdgeInsets.left = 16
toolbar.contentEdgeInsets.right = 16
toolbar.depthPreset = .none
toolbar.divider.color = Color.grey.lighten3
layout(toolbar).edges()
}
}
/*
* 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 UIKit
import Material
import Photos
public struct PhotoLibraryDataSource {
/// A reference to a PHAssetCollection returned from the fetchResult.
public private(set) var collection: PHAssetCollection
/// A reference to an Array of PHAssets for the PHAssetCollection.
public private(set) var assets: [PHAsset]
}
class PhotoLibraryViewController: PhotoLibraryController {
/// A collectionView used to display entries.
internal var collectionView: PhotoLibraryCollectionView!
/// A reference to the images cache.
internal lazy var images = [IndexPath: PHAsset]()
/// A reference to the current collection.
internal var currentDataSource: PhotoLibraryDataSource?
/// The assets used in the album.
public private(set) var dataSourceItems = [PhotoLibraryDataSource]() {
willSet {
guard .authorized == photoLibrary.authorizationStatus else {
return
}
photoLibrary.cachingImageManager.stopCachingImagesForAllAssets()
}
didSet {
guard .authorized == photoLibrary.authorizationStatus else {
return
}
for dataSource in dataSourceItems {
photoLibrary.cachingImageManager.startCachingImages(for: dataSource.assets, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: nil)
}
}
}
open override func prepare() {
super.prepare()
view.backgroundColor = Color.grey.lighten5
prepareCollectionView()
photoLibrary.requestAuthorization()
}
/**
Fetch all the PHAssetCollections asynchronously based on a type and subtype.
- Parameter type: A PHAssetCollectionType.
- Parameter subtype: A PHAssetCollectionSubtype.
- Parameter completion: A completion block.
*/
internal func fetchAssetCollections(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, completion: @escaping ([PhotoLibraryDataSource]) -> Void) {
DispatchQueue.global(qos: .default).async { [weak self] in
guard let s = self else {
return
}
let options = PHFetchOptions()
options.includeHiddenAssets = true
options.includeAllBurstAssets = true
options.wantsIncrementalChangeDetails = true
s.photoLibrary.fetchAssetCollections(with: type, subtype: subtype, options: options) { [weak self, completion = completion] (assetCollections, _) in
guard let s = self else {
return
}
assetCollections.forEach { [weak self] (assetCollection) in
guard let s = self else {
return
}
let options = PHFetchOptions()
let descriptor = NSSortDescriptor(key: "creationDate", ascending: false)
options.sortDescriptors = [descriptor]
options.includeHiddenAssets = true
options.includeAllBurstAssets = true
options.wantsIncrementalChangeDetails = true
s.photoLibrary.fetchAssets(in: assetCollection, options: options) { [weak self] (assets, _) in
guard let s = self else {
return
}
s.dataSourceItems.append(PhotoLibraryDataSource(collection: assetCollection, assets: assets))
}
}
completion(s.dataSourceItems)
}
}
}
/// Prepares the collectionView.
internal func prepareCollectionView() {
let columns: CGFloat = .phone == Device.userInterfaceIdiom ? 3 : 11
let w: CGFloat = (view.bounds.width - (columns - 1)) / columns
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 1
layout.minimumInteritemSpacing = 1
layout.scrollDirection = .vertical
layout.itemSize = CGSize(width: w, height: w)
layout.headerReferenceSize = CGSize(width: view.bounds.width, height: 44)
layout.sectionHeadersPinToVisibleBounds = true
collectionView = PhotoLibraryCollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = Color.clear
collectionView.delegate = self
collectionView.dataSource = self
view.layout(collectionView).edges()
}
/// Prepares dataSourceItems.
internal func prepareDataSource(dataSource: PhotoLibraryDataSource) {
DispatchQueue.main.async { [weak self] in
guard let s = self else {
return
}
s.collectionView.reloadData()
}
}
/// Prepares the collection.
internal func prepareCollection() {
dataSourceItems.removeAll()
fetchAssetCollections(with: .album, subtype: .any) { [weak self] (assetCollections) in
guard let s = self else {
return
}
for dataSource in assetCollections {
print(dataSource.collection.localizedTitle, dataSource.assets.count)
}
if let v = assetCollections.first {
s.currentDataSource = v
s.prepareDataSource(dataSource: v)
}
}
}
}
extension PhotoLibraryViewController {
func photoLibrary(photoLibrary: PhotoLibrary, didChange changeInfo: PHChange) {
print("Did Change", changeInfo)
}
func photoLibrary(authorized photoLibrary: PhotoLibrary) {
print("Authorized")
prepareCollection()
}
func photoLibrary(denied photoLibrary: PhotoLibrary) {
print("Denied")
}
func photoLibrary(notDetermined photoLibrary: PhotoLibrary) {
print("notDetermined")
}
func photoLibrary(restricted photoLibrary: PhotoLibrary) {
print("restricted")
}
func photoLibrary(photoLibrary: PhotoLibrary, status: PHAuthorizationStatus) {
print("Status", status)
}
func photoLibrary(photoLibrary: PhotoLibrary, beforeChanges: PHObject, afterChanges: PHObject, assetContentChanged: Bool, objectWasDeleted: Bool) {
print("Before", beforeChanges, "After", afterChanges, "Content Change", assetContentChanged, "Was Deleted", objectWasDeleted)
}
func photoLibrary(photoLibrary: PhotoLibrary, fetchBeforeChanges: PHFetchResult<PHObject>, fetchAfterChanges: PHFetchResult<PHObject>) {
print("Fetch Before", fetchBeforeChanges, "Fetch After", fetchAfterChanges, "Has Incremental Changes")
}
func photoLibrary(photoLibrary: PhotoLibrary, removed indexes: IndexSet, for objects: [PHObject]) {
print("Removed", indexes, objects)
}
func photoLibrary(photoLibrary: PhotoLibrary, inserted indexes: IndexSet, for objects: [PHObject]) {
print("Inserted", indexes, objects)
}
func photoLibrary(photoLibrary: PhotoLibrary, changed indexes: IndexSet, for objects: [PHObject]) {
print("Changed", indexes, objects)
}
func photoLibrary(photoLibrary: PhotoLibrary, removedIndexes: IndexSet?, insertedIndexes: IndexSet?, changedIndexes: IndexSet?, has moves: [PhotoLibraryMove]) {
print("Removed", removedIndexes, "Inserted", insertedIndexes, "Changed", changedIndexes, "Moves", moves)
if nil == removedIndexes && nil == insertedIndexes && nil == changedIndexes {
return
}
prepareCollection()
}
}
/// UICollectionViewDelegate methods.
extension PhotoLibraryViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
_ = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoLibraryCollectionViewCell", for: indexPath) as! PhotoLibraryCollectionViewCell
let dataSource = dataSourceItems[indexPath.section]
let asset = dataSource.assets[indexPath.item]
print("Did Select DataSource:", dataSource, "Asset:", asset)
}
}
/// CollectionViewDataSource methods.
extension PhotoLibraryViewController: UICollectionViewDataSource {
/// Determines the number of items in the collectionView.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSourceItems[section].assets.count
}
/// Returns the number of sections.
func numberOfSections(in collectionView: UICollectionView) -> Int {
return dataSourceItems.count
}
/// Prepares the cells within the collectionView.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoLibraryCollectionViewCell", for: indexPath) as! PhotoLibraryCollectionViewCell
let dataSource = dataSourceItems[indexPath.section]
let asset = dataSource.assets[indexPath.item]
if 0 != cell.tag {
photoLibrary.cancelImageRequest(for: PHImageRequestID(cell.tag))
}
guard let itemSize = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.itemSize else {
return cell
}
let options = PHImageRequestOptions()
options.deliveryMode = .opportunistic
options.isNetworkAccessAllowed = true
// Progress handler, called in an arbitrary serial queue. Only called
// when the data is not available locally and is retrieved from iCloud.
options.progressHandler = { (progress, error, stop, info) in
print("Downloading from iCloud", progress, error, stop, info)
}
cell.tag = Int(photoLibrary.requestImage(for: asset, targetSize: itemSize, contentMode: .aspectFit, options: options) { (image, info) in
cell.image = image
})
return cell
}
/// Prepares the header within the collectionView.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let reusableview = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "PhotoLibraryCollectionReusableView", for: indexPath) as! PhotoLibraryCollectionReusableView
let dataSource = dataSourceItems[indexPath.section]
reusableview.toolbar.title = dataSource.collection.localizedTitle
return reusableview
}
}
//
// ViewController.swift
// PhotoLibraryController
//
// Created by Daniel Dahan on 2016-09-19.
// Copyright © 2016 CosmicMind. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
open override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
......@@ -30,8 +30,8 @@
import UIKit
@objc(MaterialCollectionReusableView)
open class MaterialCollectionReusableView: UICollectionReusableView {
@objc(CollectionReusableView)
open class CollectionReusableView: UICollectionReusableView {
/**
A CAShapeLayer used to manage elements that would be affected by
the clipToBounds property of the backing layer. For example, this
......
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