Commit 4b3642b2 authored by Geoff Pado's avatar Geoff Pado

Merge branch '2-show-collection-of-photos' into 'release/2.0'

Resolve "Show collection of photos"

See merge request highlighter/app!3
parents 8a4b71ff e57f8e1e
......@@ -24,6 +24,14 @@
041EFF592255A2FB0058D8EE /* Aleo-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 041EFF572255A2EE0058D8EE /* Aleo-Regular.otf */; };
041EFF5B2255A39D0058D8EE /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF5A2255A39D0058D8EE /* Fonts.swift */; };
041EFF5D2255A85F0058D8EE /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF5C2255A85F0058D8EE /* NavigationController.swift */; };
041EFF6B225C36350058D8EE /* PhotoLibraryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF6A225C36350058D8EE /* PhotoLibraryViewController.swift */; };
041EFF6E225C38800058D8EE /* PhotoSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF6D225C38800058D8EE /* PhotoSelectionViewController.swift */; };
041EFF70225C3D830058D8EE /* PhotoLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF6F225C3D830058D8EE /* PhotoLibraryView.swift */; };
041EFF72225C3DC00058D8EE /* PhotoLibraryDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF71225C3DC00058D8EE /* PhotoLibraryDataSource.swift */; };
041EFF74225C3DD20058D8EE /* PhotoLibraryViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF73225C3DD20058D8EE /* PhotoLibraryViewCell.swift */; };
041EFF76225C3DF10058D8EE /* PhotoLibraryViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 041EFF75225C3DF10058D8EE /* PhotoLibraryViewLayout.swift */; };
04D68BC5225EE2C000D09BBD /* GeometryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D68BC4225EE2C000D09BBD /* GeometryExtensions.swift */; };
04D68BC7225EE2FD00D09BBD /* PhotoLibraryViewCellImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04D68BC6225EE2FD00D09BBD /* PhotoLibraryViewCellImageView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -58,6 +66,14 @@
041EFF572255A2EE0058D8EE /* Aleo-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Aleo-Regular.otf"; sourceTree = "<group>"; };
041EFF5A2255A39D0058D8EE /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
041EFF5C2255A85F0058D8EE /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
041EFF6A225C36350058D8EE /* PhotoLibraryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryViewController.swift; sourceTree = "<group>"; };
041EFF6D225C38800058D8EE /* PhotoSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoSelectionViewController.swift; sourceTree = "<group>"; };
041EFF6F225C3D830058D8EE /* PhotoLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryView.swift; sourceTree = "<group>"; };
041EFF71225C3DC00058D8EE /* PhotoLibraryDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryDataSource.swift; sourceTree = "<group>"; };
041EFF73225C3DD20058D8EE /* PhotoLibraryViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryViewCell.swift; sourceTree = "<group>"; };
041EFF75225C3DF10058D8EE /* PhotoLibraryViewLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryViewLayout.swift; sourceTree = "<group>"; };
04D68BC4225EE2C000D09BBD /* GeometryExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeometryExtensions.swift; sourceTree = "<group>"; };
04D68BC6225EE2FD00D09BBD /* PhotoLibraryViewCellImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryViewCellImageView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -99,14 +115,14 @@
041EFEF72251A9F30058D8EE /* Highlighter */ = {
isa = PBXGroup;
children = (
041EFF6C225C38600058D8EE /* Photo Selection */,
041EFF532255A0930058D8EE /* Style */,
041EFF4C225302B90058D8EE /* Intro View */,
041EFEF82251A9F30058D8EE /* AppDelegate.swift */,
041EFEFA2251A9F30058D8EE /* AppViewController.swift */,
041EFEFF2251A9F50058D8EE /* Assets.xcassets */,
041EFF012251A9F50058D8EE /* LaunchScreen.storyboard */,
041EFF042251A9F50058D8EE /* Info.plist */,
041EFF182251AAFE0058D8EE /* UIViewControllerExtensions.swift */,
04D68BC3225EE2A500D09BBD /* Extensions */,
041EFF1A2251AB8D0058D8EE /* AppWindow.swift */,
041EFF4A225300030058D8EE /* Localizable.strings */,
041EFF5C2255A85F0058D8EE /* NavigationController.swift */,
......@@ -145,6 +161,38 @@
path = Style;
sourceTree = "<group>";
};
041EFF69225C36130058D8EE /* Photo Library */ = {
isa = PBXGroup;
children = (
041EFF6A225C36350058D8EE /* PhotoLibraryViewController.swift */,
041EFF6F225C3D830058D8EE /* PhotoLibraryView.swift */,
041EFF71225C3DC00058D8EE /* PhotoLibraryDataSource.swift */,
041EFF73225C3DD20058D8EE /* PhotoLibraryViewCell.swift */,
04D68BC6225EE2FD00D09BBD /* PhotoLibraryViewCellImageView.swift */,
041EFF75225C3DF10058D8EE /* PhotoLibraryViewLayout.swift */,
);
path = "Photo Library";
sourceTree = "<group>";
};
041EFF6C225C38600058D8EE /* Photo Selection */ = {
isa = PBXGroup;
children = (
041EFF4C225302B90058D8EE /* Intro View */,
041EFF69225C36130058D8EE /* Photo Library */,
041EFF6D225C38800058D8EE /* PhotoSelectionViewController.swift */,
);
path = "Photo Selection";
sourceTree = "<group>";
};
04D68BC3225EE2A500D09BBD /* Extensions */ = {
isa = PBXGroup;
children = (
041EFF182251AAFE0058D8EE /* UIViewControllerExtensions.swift */,
04D68BC4225EE2C000D09BBD /* GeometryExtensions.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
......@@ -250,15 +298,23 @@
files = (
041EFF5D2255A85F0058D8EE /* NavigationController.swift in Sources */,
041EFF492252FAF50058D8EE /* IntroViewController.swift in Sources */,
041EFF6B225C36350058D8EE /* PhotoLibraryViewController.swift in Sources */,
041EFF70225C3D830058D8EE /* PhotoLibraryView.swift in Sources */,
041EFF552255A0B00058D8EE /* Colors.swift in Sources */,
041EFF72225C3DC00058D8EE /* PhotoLibraryDataSource.swift in Sources */,
041EFEFB2251A9F30058D8EE /* AppViewController.swift in Sources */,
041EFF6E225C38800058D8EE /* PhotoSelectionViewController.swift in Sources */,
041EFF192251AAFE0058D8EE /* UIViewControllerExtensions.swift in Sources */,
041EFF52225303430058D8EE /* PromptButton.swift in Sources */,
041EFEF92251A9F30058D8EE /* AppDelegate.swift in Sources */,
041EFF76225C3DF10058D8EE /* PhotoLibraryViewLayout.swift in Sources */,
04D68BC7225EE2FD00D09BBD /* PhotoLibraryViewCellImageView.swift in Sources */,
041EFF5B2255A39D0058D8EE /* Fonts.swift in Sources */,
041EFF50225303120058D8EE /* PromptLabel.swift in Sources */,
041EFF1B2251AB8D0058D8EE /* AppWindow.swift in Sources */,
041EFF4E225302CC0058D8EE /* IntroView.swift in Sources */,
041EFF74225C3DD20058D8EE /* PhotoLibraryViewCell.swift in Sources */,
04D68BC5225EE2C000D09BBD /* GeometryExtensions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -7,7 +7,7 @@ class AppViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
let navigationController = NavigationController(rootViewController: IntroViewController())
let navigationController = NavigationController(rootViewController: PhotoSelectionViewController())
embed(navigationController)
}
......
// Created by Geoff Pado on 4/10/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
extension CGSize {
static func * (size: CGSize, multiplier: CGFloat) -> CGSize {
return CGSize(width: size.width * multiplier, height: size.height * multiplier)
}
}
......@@ -23,7 +23,7 @@ extension UIViewController {
newChildView.heightAnchor.constraint(equalTo: view.heightAnchor),
newChildView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
newChildView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
])
}
func transition(to child: UIViewController, completion: ((Bool) -> Void)? = nil) {
......
......@@ -6,6 +6,6 @@
Copyright © 2019 Cocoatype, LLC. All rights reserved.
*/
"IntroViewController.navigationItemTitle" = "Welcome";
"IntroView.promptLabelText" = "Black Highlighter needs permission to edit your photos.";
"IntroView.promptButtonTitle" = "Grant Access";
"PhotoSelectionViewController.navigationItemTitle" = "Photos";
......@@ -7,7 +7,6 @@ import UIKit
class IntroViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
navigationItem.title = IntroViewController.navigationItemTitle
}
override func loadView() {
......@@ -17,6 +16,12 @@ class IntroViewController: UIViewController {
@objc func requestPermission() {
PHPhotoLibrary.requestAuthorization { status in
print("got status: \(status)")
if status == .authorized {
DispatchQueue.main.async {
UIApplication.shared.sendAction(#selector(PhotoSelectionViewController.showPhotoLibrary), to: nil, from: self, for: nil)
}
}
}
print("permission requested")
......@@ -24,8 +29,6 @@ class IntroViewController: UIViewController {
// MARK: Boilerplate
private static let navigationItemTitle = NSLocalizedString("IntroViewController.navigationItemTitle", comment: "Navigation title for the intro view")
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
......
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import Photos
import UIKit
class PhotoLibraryDataSource: NSObject, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allPhotos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PhotoLibraryViewCell.identifier, for: indexPath)
guard let photoCell = cell as? PhotoLibraryViewCell else {
fatalError("Got incorrect type of cell for photo picker: \(String(describing: type(of: cell)))")
}
photoCell.asset = allPhotos[indexPath.item]
return cell
}
// MARK: Photos
lazy var allPhotos: PHFetchResult<PHAsset> = {
let fetchOptions = PHFetchOptions()
return PHAsset.fetchAssets(with: fetchOptions)
}()
}
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoLibraryView: UICollectionView {
init() {
let layout = PhotoLibraryViewLayout()
super.init(frame: .zero, collectionViewLayout: layout)
register(PhotoLibraryViewCell.self, forCellWithReuseIdentifier: PhotoLibraryViewCell.identifier)
backgroundColor = .primary
}
// MARK: Boilerplate
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import Photos
import UIKit
class PhotoLibraryViewCell: UICollectionViewCell {
static let identifier = "PhotoLibraryViewCell.identifier"
var asset: PHAsset? {
didSet {
guard let asset = asset else {
assetRequestID = nil
imageView.image = nil
return
}
let targetSize = bounds.size * UIScreen.main.scale
assetRequestID = imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: nil) { [weak self] image, info in
guard let image = image,
let requestID = (info?[PHImageResultRequestIDKey] as? NSNumber)?.int32Value,
self?.assetRequestID == requestID
else { return }
self?.imageView.image = image
}
}
}
override init(frame: CGRect) {
imageView = PhotoLibraryViewCellImageView()
super.init(frame: .zero)
contentView.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalTo: contentView.widthAnchor),
imageView.heightAnchor.constraint(equalTo: contentView.heightAnchor),
imageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
])
backgroundColor = .red
}
override func prepareForReuse() {
super.prepareForReuse()
asset = nil
}
// MARK: Boilerplate
private let imageManager = PHImageManager.default()
private var imageView: UIImageView
private var assetRequestID: PHImageRequestID? {
didSet {
guard let oldValue = oldValue else { return }
imageManager.cancelImageRequest(oldValue)
}
}
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/10/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoLibraryViewCellImageView: UIImageView {
init() {
super.init(frame: .zero)
clipsToBounds = true
contentMode = .scaleAspectFill
translatesAutoresizingMaskIntoConstraints = false
}
// MARK: Boilerplate
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoLibraryViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
override func loadView() {
let libraryView = PhotoLibraryView()
libraryView.dataSource = dataSource
view = libraryView
}
// MARK: Boilerplate
private let dataSource = PhotoLibraryDataSource()
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoLibraryViewLayout: UICollectionViewFlowLayout {
override init() {
super.init()
}
override func prepare() {
super.prepare()
minimumInteritemSpacing = 1.0
itemSize = PhotoLibraryViewLayout.itemSize(for: collectionView?.traitCollection)
}
// MARK: Boilerplate
private static let smallCellSize = CGSize(width: 74.0, height: 74.0)
private static let largeCellSize = CGSize(width: 190.0, height: 190.0)
private static func itemSize(for traitCollection: UITraitCollection?) -> CGSize {
let horizontalSizeClass = traitCollection?.horizontalSizeClass ?? .unspecified
let verticalSizeClass = traitCollection?.verticalSizeClass ?? .unspecified
if (horizontalSizeClass, verticalSizeClass) == (.regular, .regular) {
return largeCellSize
} else { return smallCellSize }
}
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
// Created by Geoff Pado on 4/8/19.
// Copyright © 2019 Cocoatype, LLC. All rights reserved.
import UIKit
class PhotoSelectionViewController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
navigationItem.title = PhotoSelectionViewController.navigationItemTitle
embed(IntroViewController())
}
@objc func showPhotoLibrary() {
transition(to: PhotoLibraryViewController())
}
// MARK: Boilerplate
private static let navigationItemTitle = NSLocalizedString("PhotoSelectionViewController.navigationItemTitle", comment: "Navigation title for the photo selector")
@available(*, unavailable)
required init(coder: NSCoder) {
let className = String(describing: type(of: self))
fatalError("\(className) does not implement init(coder:)")
}
}
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