Merge gallery and large image animations

This commit is contained in:
Shadowfacts 2020-03-17 21:05:45 -04:00
parent 955f9e5916
commit 2e8c416e04
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
8 changed files with 43 additions and 224 deletions

View File

@ -13,8 +13,6 @@
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0427033922B31269000D31B6 /* AdvancedPrefsView.swift */; };
0427037C22B316B9000D31B6 /* SilentActionPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */; };
0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450531E22B0097E00100BA2 /* Timline+UI.swift */; };
0454DDAF22B462EF00B8BB8E /* GalleryExpandAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0454DDAE22B462EF00B8BB8E /* GalleryExpandAnimationController.swift */; };
0454DDB122B467AA00B8BB8E /* GalleryShrinkAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0454DDB022B467AA00B8BB8E /* GalleryShrinkAnimationController.swift */; };
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04586B4022B2FFB10021BD04 /* PreferencesView.swift */; };
04586B4322B301470021BD04 /* AppearancePrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04586B4222B301470021BD04 /* AppearancePrefsView.swift */; };
0461A3902163CBAE00C0A807 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0461A38F2163CBAE00C0A807 /* Cache.framework */; };
@ -297,8 +295,6 @@
0427033922B31269000D31B6 /* AdvancedPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedPrefsView.swift; sourceTree = "<group>"; };
0427037B22B316B9000D31B6 /* SilentActionPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SilentActionPrefs.swift; sourceTree = "<group>"; };
0450531E22B0097E00100BA2 /* Timline+UI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Timline+UI.swift"; sourceTree = "<group>"; };
0454DDAE22B462EF00B8BB8E /* GalleryExpandAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryExpandAnimationController.swift; sourceTree = "<group>"; };
0454DDB022B467AA00B8BB8E /* GalleryShrinkAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryShrinkAnimationController.swift; sourceTree = "<group>"; };
04586B4022B2FFB10021BD04 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
04586B4222B301470021BD04 /* AppearancePrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearancePrefsView.swift; sourceTree = "<group>"; };
0461A38F2163CBAE00C0A807 /* Cache.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Cache.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -584,21 +580,11 @@
0411610522B457290030A9B7 /* Gallery */ = {
isa = PBXGroup;
children = (
0411610622B457360030A9B7 /* Transitions */,
04D14BAE22B34A2800642648 /* GalleryViewController.swift */,
);
path = Gallery;
sourceTree = "<group>";
};
0411610622B457360030A9B7 /* Transitions */ = {
isa = PBXGroup;
children = (
0454DDAE22B462EF00B8BB8E /* GalleryExpandAnimationController.swift */,
0454DDB022B467AA00B8BB8E /* GalleryShrinkAnimationController.swift */,
);
path = Transitions;
sourceTree = "<group>";
};
D61099AC2144B0CC00432DC2 /* Pachyderm */ = {
isa = PBXGroup;
children = (
@ -1630,7 +1616,6 @@
D6BC9DB1232C61BC002CA326 /* NotificationsPageViewController.swift in Sources */,
D6969E9E240C81B9002843CE /* NSTextAttachment+Emoji.swift in Sources */,
D6BC9DB3232D4C07002CA326 /* WellnessPrefsView.swift in Sources */,
0454DDAF22B462EF00B8BB8E /* GalleryExpandAnimationController.swift in Sources */,
D64BC18F23C18B9D000D0238 /* FollowRequestNotificationTableViewCell.swift in Sources */,
D693DE5923FE24310061E07D /* InteractivePushTransition.swift in Sources */,
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
@ -1664,7 +1649,6 @@
D64BC18823C1640A000D0238 /* PinStatusActivity.swift in Sources */,
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */,
D646C956213B365700269FB5 /* LargeImageExpandAnimationController.swift in Sources */,
0454DDB122B467AA00B8BB8E /* GalleryShrinkAnimationController.swift in Sources */,
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */,
D67C57B421E2910700C3118B /* ComposeStatusReplyView.swift in Sources */,
D6B053A623BD2D0C00A066FA /* AssetCollectionViewController.swift in Sources */,

View File

@ -10,23 +10,17 @@ import UIKit
extension UIViewController: UIViewControllerTransitioningDelegate {
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if let presented = presented as? LargeImageViewController,
presented.sourceInfo?.image != nil {
if let presented = presented as? LargeImageAnimatableViewController,
presented.animationImage != nil {
return LargeImageExpandAnimationController()
} else if let presented = presented as? GalleryViewController,
presented.sourcesInfo[presented.startIndex]?.image != nil {
return GalleryExpandAnimationController()
}
return nil
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if let dismissed = dismissed as? LargeImageViewController,
dismissed.imageForDismissalAnimation() != nil {
if let dismissed = dismissed as? LargeImageAnimatableViewController,
dismissed.animationImage != nil {
return LargeImageShrinkAnimationController(interactionController: dismissed.dismissInteractionController)
} else if let dismissed = dismissed as? GalleryViewController,
dismissed.imageForDismissalAnimation() != nil {
return GalleryShrinkAnimationController(interactionController: dismissed.dismissInteractionController)
}
return nil
}
@ -36,10 +30,6 @@ extension UIViewController: UIViewControllerTransitioningDelegate {
let interactionController = animator.interactionController,
interactionController.inProgress {
return interactionController
} else if let animator = animator as? GalleryShrinkAnimationController,
let interactionController = animator.interactionController,
interactionController.inProgress {
return interactionController
}
return nil
}

View File

@ -10,10 +10,8 @@ import Pachyderm
import AVFoundation
import AVKit
class GalleryViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
class GalleryViewController: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate, LargeImageAnimatableViewController {
var dismissInteractionController: LargeImageInteractionController?
let attachments: [Attachment]
let sourcesInfo: [LargeImageViewController.SourceInfo?]
let startIndex: Int
@ -27,6 +25,24 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
}
return index
}
var animationSourceInfo: LargeImageViewController.SourceInfo? { sourcesInfo[currentIndex] }
var animationImage: UIImage? {
if let sourceImage = sourcesInfo[currentIndex]?.image {
return sourceImage
} else {
return (pages[currentIndex] as? AttachmentViewController)?.largeImageVC?.image
}
}
var animationGifData: Data? {
let attachment = attachments[currentIndex]
if attachment.url.pathExtension == "gif" {
return ImageCache.attachments.get(attachment.url)
} else {
return nil
}
}
var dismissInteractionController: LargeImageInteractionController?
override var prefersStatusBarHidden: Bool {
return true
@ -96,14 +112,6 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
vc.player?.play()
}
}
func imageForDismissalAnimation() -> UIImage? {
if let sourceImage = sourcesInfo[currentIndex]?.image {
return sourceImage
} else {
return (pages[currentIndex] as? AttachmentViewController)?.largeImageVC?.image
}
}
// MARK: - Page View Controller Data Source

View File

@ -1,84 +0,0 @@
// GalleryExpandAnimationController.swift
// Tusker
//
// Created by Shadowfacts on 6/14/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import Gifu
class GalleryExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.2
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) as? GalleryViewController else {
return
}
let containerView = transitionContext.containerView
containerView.addSubview(toVC.view)
let finalVCFrame = transitionContext.finalFrame(for: toVC)
guard let sourceInfo = toVC.sourcesInfo[toVC.startIndex],
let image = sourceInfo.image else {
toVC.view.frame = finalVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
let attachment = toVC.attachments[toVC.startIndex]
let ratio = image.size.width / image.size.height
var width = finalVCFrame.width
var height = width / ratio
let maxHeight = fromVC.view.bounds.height - fromVC.view.safeAreaInsets.top - fromVC.view.safeAreaInsets.bottom
if height > maxHeight {
let scaleFactor = maxHeight / height
width *= scaleFactor
height = maxHeight
}
let finalFrame = CGRect(x: finalVCFrame.midX - width / 2, y: finalVCFrame.midY - height / 2, width: width, height: height)
let imageView = GIFImageView(frame: sourceInfo.frame)
imageView.image = image
if attachment.url.pathExtension == "gif",
let data = ImageCache.attachments.get(attachment.url) {
imageView.animate(withGIFData: data)
}
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = sourceInfo.cornerRadius
imageView.layer.masksToBounds = true
let blackView = UIView(frame: finalVCFrame)
blackView.backgroundColor = .black
blackView.alpha = 0
containerView.addSubview(blackView)
containerView.addSubview(imageView)
toVC.view.isHidden = true
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, animations: {
imageView.frame = finalFrame
imageView.layer.cornerRadius = 0
blackView.alpha = 1
}, completion: { _ in
toVC.view.frame = finalVCFrame
toVC.view.isHidden = false
fromVC.view.isHidden = false
blackView.removeFromSuperview()
imageView.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}

View File

@ -1,84 +0,0 @@
// GalleryShrinkAnimationController.swift
// Tusker
//
// Created by Shadowfacts on 6/14/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import Gifu
class GalleryShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
let interactionController: LargeImageInteractionController?
init(interactionController: LargeImageInteractionController?) {
self.interactionController = interactionController
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.2
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from) as? GalleryViewController,
let toVC = transitionContext.viewController(forKey: .to) else {
return
}
guard let sourceInfo = fromVC.sourcesInfo[fromVC.currentIndex],
let image = fromVC.imageForDismissalAnimation() else {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
let originalVCFrame = fromVC.view.frame
let attachment = fromVC.attachments[fromVC.currentIndex]
let ratio = image.size.width / image.size.height
var width = originalVCFrame.width
var height = width / ratio
let maxHeight = fromVC.view.bounds.height - fromVC.view.safeAreaInsets.top - fromVC.view.safeAreaInsets.bottom
if height > maxHeight {
let scaleFactor = maxHeight / height
width *= scaleFactor
height = maxHeight
}
let originalFrame = CGRect(x: originalVCFrame.midX - width / 2, y: originalVCFrame.midY - height / 2, width: width, height: height)
let imageView = GIFImageView(frame: originalFrame)
imageView.image = image
if attachment.url.pathExtension == "gif",
let data = ImageCache.attachments.get(attachment.url) {
imageView.animate(withGIFData: data)
}
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 0
imageView.layer.masksToBounds = true
let blackView = UIView(frame: originalVCFrame)
blackView.backgroundColor = .black
blackView.alpha = 1
let containerView = transitionContext.containerView
containerView.addSubview(toVC.view)
containerView.addSubview(blackView)
containerView.addSubview(imageView)
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, animations: {
imageView.frame = sourceInfo.frame
imageView.layer.cornerRadius = sourceInfo.cornerRadius
blackView.alpha = 0
}, completion: { _ in
blackView.removeFromSuperview()
imageView.removeFromSuperview()
if transitionContext.transitionWasCancelled {
toVC.view.removeFromSuperview()
}
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}

View File

@ -11,11 +11,13 @@ import Pachyderm
import Photos
import Gifu
class LargeImageViewController: UIViewController, UIScrollViewDelegate {
class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeImageAnimatableViewController {
typealias SourceInfo = (image: UIImage?, frame: CGRect, cornerRadius: CGFloat)
var sourceInfo: SourceInfo?
var animationSourceInfo: SourceInfo?
var animationImage: UIImage? { animationSourceInfo?.image ?? image }
var animationGifData: Data? { gifData }
var dismissInteractionController: LargeImageInteractionController?
@IBOutlet weak var scrollView: UIScrollView!
@ -62,7 +64,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
init(image: UIImage, description: String?, sourceInfo: SourceInfo?) {
self.image = image
self.imageDescription = description
self.sourceInfo = sourceInfo
self.animationSourceInfo = sourceInfo
super.init(nibName: "LargeImageViewController", bundle: nil)
@ -127,10 +129,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
closeButtonTrailingConstraint.constant = offset
}
}
func imageForDismissalAnimation() -> UIImage? {
return sourceInfo?.image ?? image
}
func setControlsVisible(_ controlsVisible: Bool, animated: Bool) {
self.controlsVisible = controlsVisible

View File

@ -9,6 +9,13 @@
import UIKit
import Gifu
protocol LargeImageAnimatableViewController: UIViewController {
var animationSourceInfo: LargeImageViewController.SourceInfo? { get }
var animationImage: UIImage? { get }
var animationGifData: Data? { get }
var dismissInteractionController: LargeImageInteractionController? { get }
}
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
@ -17,7 +24,7 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) as? LargeImageViewController else {
let toVC = transitionContext.viewController(forKey: .to) as? LargeImageAnimatableViewController else {
return
}
@ -25,8 +32,8 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
containerView.addSubview(toVC.view)
let finalVCFrame = transitionContext.finalFrame(for: toVC)
guard let sourceInfo = toVC.sourceInfo,
let image = sourceInfo.image else {
guard let sourceInfo = toVC.animationSourceInfo,
let image = toVC.animationImage else {
toVC.view.frame = finalVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
@ -39,7 +46,7 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
let imageView = GIFImageView(frame: sourceInfo.frame)
imageView.image = image
if let gifData = toVC.gifData {
if let gifData = toVC.animationGifData {
imageView.animate(withGIFData: gifData)
}
imageView.contentMode = .scaleAspectFill

View File

@ -22,13 +22,13 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageViewController,
guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageAnimatableViewController,
let toVC = transitionContext.viewController(forKey: .to) else {
return
}
guard let sourceInfo = fromVC.sourceInfo,
let image = fromVC.imageForDismissalAnimation() else {
guard let sourceInfo = fromVC.animationSourceInfo,
let image = fromVC.animationImage else {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
@ -43,7 +43,7 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
let imageView = GIFImageView(frame: originalFrame)
imageView.image = image
if let gifData = fromVC.gifData {
if let gifData = fromVC.animationGifData {
imageView.animate(withGIFData: gifData)
}
imageView.contentMode = .scaleAspectFill