180 lines
6.7 KiB
Swift
180 lines
6.7 KiB
Swift
//
|
|
// GalleryViewController.swift
|
|
// GalleryVC
|
|
//
|
|
// Created by Shadowfacts on 12/28/23.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
public class GalleryViewController: UIPageViewController {
|
|
|
|
let galleryDataSource: GalleryDataSource
|
|
let initialItemIndex: Int
|
|
private let _itemsCount: Int
|
|
private var itemsCount: Int {
|
|
get {
|
|
precondition(_itemsCount == galleryDataSource.galleryItemsCount(), "GalleryDataSource item count cannot change")
|
|
return _itemsCount
|
|
}
|
|
}
|
|
|
|
var currentItemViewController: GalleryItemViewController {
|
|
viewControllers![0] as! GalleryItemViewController
|
|
}
|
|
|
|
private var dismissInteraction: GalleryDismissInteraction!
|
|
private var presentationAnimationCompletionHandlers: [() -> Void] = []
|
|
|
|
override public var prefersStatusBarHidden: Bool {
|
|
true
|
|
}
|
|
override public var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
|
.none
|
|
}
|
|
override public var childForHomeIndicatorAutoHidden: UIViewController? {
|
|
currentItemViewController
|
|
}
|
|
|
|
public init(dataSource: GalleryDataSource, initialItemIndex: Int) {
|
|
self.galleryDataSource = dataSource
|
|
self.initialItemIndex = initialItemIndex
|
|
self._itemsCount = dataSource.galleryItemsCount()
|
|
precondition(initialItemIndex >= 0 && initialItemIndex < _itemsCount, "initialItemIndex is out of bounds")
|
|
|
|
super.init(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [
|
|
.interPageSpacing: 50
|
|
])
|
|
|
|
modalPresentationStyle = .fullScreen
|
|
transitioningDelegate = self
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
public override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
dismissInteraction = GalleryDismissInteraction(viewController: self)
|
|
|
|
view.backgroundColor = .black
|
|
overrideUserInterfaceStyle = .dark
|
|
|
|
dataSource = self
|
|
delegate = self
|
|
|
|
setViewControllers([makeItemVC(index: initialItemIndex)], direction: .forward, animated: false)
|
|
}
|
|
|
|
public override func viewDidAppear(_ animated: Bool) {
|
|
super.viewDidAppear(animated)
|
|
|
|
if animated {
|
|
// Wait until the transition is no longer in-progress, otherwise things will just get deferred again.
|
|
DispatchQueue.main.async {
|
|
self.presentationAnimationCompleted()
|
|
}
|
|
}
|
|
}
|
|
|
|
public override func viewWillDisappear(_ animated: Bool) {
|
|
super.viewWillDisappear(animated)
|
|
|
|
if isBeingDismissed {
|
|
currentItemViewController.content.galleryContentWillDisappear()
|
|
}
|
|
}
|
|
|
|
private func makeItemVC(index: Int) -> GalleryItemViewController {
|
|
let content = galleryDataSource.galleryContentViewController(forItemAt: index)
|
|
return GalleryItemViewController(delegate: self, itemIndex: index, content: content)
|
|
}
|
|
|
|
func presentationAnimationCompleted() {
|
|
for block in presentationAnimationCompletionHandlers {
|
|
block()
|
|
}
|
|
currentItemViewController.content.galleryContentDidAppear()
|
|
}
|
|
}
|
|
|
|
extension GalleryViewController: UIPageViewControllerDataSource {
|
|
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
|
guard let viewController = viewController as? GalleryItemViewController else {
|
|
preconditionFailure("VC must be GalleryItemViewController")
|
|
}
|
|
guard viewController.itemIndex > 0 else {
|
|
return nil
|
|
}
|
|
return makeItemVC(index: viewController.itemIndex - 1)
|
|
}
|
|
|
|
public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
|
guard let viewController = viewController as? GalleryItemViewController else {
|
|
preconditionFailure("VC must be GalleryItemViewController")
|
|
}
|
|
guard viewController.itemIndex < itemsCount - 1 else {
|
|
return nil
|
|
}
|
|
return makeItemVC(index: viewController.itemIndex + 1)
|
|
}
|
|
}
|
|
|
|
extension GalleryViewController: UIPageViewControllerDelegate {
|
|
public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) {
|
|
currentItemViewController.content.galleryContentWillDisappear()
|
|
}
|
|
|
|
public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
|
|
currentItemViewController.content.galleryContentDidAppear()
|
|
}
|
|
}
|
|
|
|
extension GalleryViewController: GalleryItemViewControllerDelegate {
|
|
func isGalleryBeingPresented() -> Bool {
|
|
isBeingPresented
|
|
}
|
|
|
|
func addPresentationAnimationCompletion(_ block: @escaping () -> Void) {
|
|
presentationAnimationCompletionHandlers.append(block)
|
|
}
|
|
|
|
func galleryItemClose(_ item: GalleryItemViewController) {
|
|
dismiss(animated: true)
|
|
}
|
|
|
|
func galleryItemApplicationActivities(_ item: GalleryItemViewController) -> [UIActivity]? {
|
|
galleryDataSource.galleryApplicationActivities(forItemAt: item.itemIndex)
|
|
}
|
|
}
|
|
|
|
extension GalleryViewController: UIViewControllerTransitioningDelegate {
|
|
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
if let sourceView = galleryDataSource.galleryContentTransitionSourceView(forItemAt: initialItemIndex) {
|
|
return GalleryPresentationAnimationController(sourceView: sourceView)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
|
if let sourceView = galleryDataSource.galleryContentTransitionSourceView(forItemAt: currentItemViewController.itemIndex) {
|
|
let translation: CGPoint?
|
|
let velocity: CGPoint?
|
|
if let dismissInteraction,
|
|
dismissInteraction.isActive {
|
|
translation = dismissInteraction.dismissTranslation
|
|
velocity = dismissInteraction.dismissVelocity
|
|
} else {
|
|
translation = nil
|
|
velocity = nil
|
|
}
|
|
return GalleryDismissAnimationController(sourceView: sourceView, interactiveTranslation: translation, interactiveVelocity: velocity)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
}
|