forked from shadowfacts/Tusker
Merge gallery and large image animations
This commit is contained in:
parent
955f9e5916
commit
2e8c416e04
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue