blob: 06247a91ba181d5181f35636bc1b8cfc60b40671 [file] [log] [blame] [edit]
/*
Copyright 2017-present The Material Motion Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
import MotionTransitioning
// This example demonstrates how to build a photo album contextual transition.
private let photoCellIdentifier = "photoCell"
public class PhotoAlbumExampleViewController: UICollectionViewController, PhotoAlbumTransitionBackDelegate {
let album = PhotoAlbum()
init() {
super.init(collectionViewLayout: UICollectionViewFlowLayout())
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func viewDidLoad() {
super.viewDidLoad()
collectionView!.backgroundColor = .white
collectionView!.register(PhotoCollectionViewCell.self,
forCellWithReuseIdentifier: photoCellIdentifier)
}
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateLayout()
}
func updateLayout() {
let layout = collectionView!.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionInset = .init(top: 4, left: 4, bottom: 4, right: 4)
layout.minimumInteritemSpacing = 4
layout.minimumLineSpacing = 4
let numberOfColumns: CGFloat = 3
let squareDimension = (view.bounds.width - layout.sectionInset.left - layout.sectionInset.right - (numberOfColumns - 1) * layout.minimumInteritemSpacing) / numberOfColumns
layout.itemSize = CGSize(width: squareDimension, height: squareDimension)
}
public override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return album.photos.count
}
public override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellIdentifier,
for: indexPath) as! PhotoCollectionViewCell
let photo = album.photos[indexPath.row]
cell.imageView.image = photo.image
return cell
}
public override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let viewController = PhotoAlbumViewController(album: album)
viewController.currentPhoto = album.photos[indexPath.row]
viewController.mdm_transitionController.transition = PhotoAlbumTransition(backDelegate: self,
foreDelegate: viewController)
viewController.modalPresentationStyle = .custom
present(viewController, animated: true)
}
func backContextView(for transition: PhotoAlbumTransition,
with foreViewController: UIViewController) -> UIImageView? {
let currentPhoto = (foreViewController as! PhotoAlbumViewController).currentPhoto
guard let photoIndex = album.identifierToIndex[currentPhoto.uuid] else {
return nil
}
let photoIndexPath = IndexPath(item: photoIndex, section: 0)
if collectionView?.cellForItem(at: photoIndexPath) == nil {
collectionView?.scrollToItem(at: photoIndexPath, at: .top, animated: false)
collectionView?.reloadItems(at: [photoIndexPath])
}
guard let cell = collectionView?.cellForItem(at: photoIndexPath) as? PhotoCollectionViewCell else {
return nil
}
return cell.imageView
}
}
class PhotoAlbumViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, PhotoAlbumTransitionForeDelegate {
var collectionView: UICollectionView!
let toolbar = UIToolbar()
var currentPhoto: Photo
let album: PhotoAlbum
init(album: PhotoAlbum) {
self.album = album
self.currentPhoto = self.album.photos.first!
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
automaticallyAdjustsScrollViewInsets = false
let layout = UICollectionViewFlowLayout()
layout.itemSize = view.bounds.size
layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 8
layout.footerReferenceSize = CGSize(width: layout.minimumLineSpacing / 2,
height: view.bounds.size.height)
layout.headerReferenceSize = layout.footerReferenceSize
layout.scrollDirection = .horizontal
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.isPagingEnabled = true
collectionView.backgroundColor = .backgroundColor
collectionView.showsHorizontalScrollIndicator = false
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(PhotoCollectionViewCell.self,
forCellWithReuseIdentifier: photoCellIdentifier)
var extendedBounds = view.bounds
extendedBounds.size.width = extendedBounds.width + layout.minimumLineSpacing
collectionView.bounds = extendedBounds
view.addSubview(collectionView)
let toolbarSize = toolbar.sizeThatFits(view.bounds.size)
toolbar.frame = .init(x: 0, y: view.bounds.height - toolbarSize.height,
width: toolbarSize.width, height: toolbarSize.height)
view.addSubview(toolbar)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
let photoIndex = album.photos.firstIndex { $0.image == currentPhoto.image }!
let indexPath = IndexPath(item: photoIndex, section: 0)
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: PhotoAlbumTransitionForeDelegate
func foreContextView(for transition: PhotoAlbumTransition) -> UIImageView? {
return (collectionView.cellForItem(at: indexPathForCurrentPhoto()) as! PhotoCollectionViewCell).imageView
}
func toolbar(for transition: PhotoAlbumTransition) -> UIToolbar? {
return toolbar
}
// MARK: UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return album.photos.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: photoCellIdentifier,
for: indexPath) as! PhotoCollectionViewCell
let photo = album.photos[indexPath.row]
cell.imageView.image = photo.image
cell.imageView.contentMode = .scaleAspectFit
return cell
}
// MARK: UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
dismiss(animated: true)
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
currentPhoto = album.photos[indexPathForCurrentPhoto().item]
}
// MARK: Private
private func indexPathForCurrentPhoto() -> IndexPath {
return collectionView.indexPathsForVisibleItems.first!
}
}