Compare commits

..

No commits in common. "cd5b59319991210bffa144eab316a8cc4ae0e706" and "55fc032f3644a20295145aedd90868c86241be2b" have entirely different histories.

11 changed files with 60 additions and 127 deletions

View File

@ -62,7 +62,7 @@ class AttachmentViewController: UIViewController {
func createLargeImage(data: Data) { func createLargeImage(data: Data) {
guard let image = UIImage(data: data) else { return } guard let image = UIImage(data: data) else { return }
largeImageVC = LargeImageViewController(image: image, description: attachment.description, sourceInfo: nil) largeImageVC = LargeImageViewController(image: image, description: attachment.description, sourceFrame: nil, sourceCornerRadius: nil)
largeImageVC!.initialControlsVisible = initialControlsVisible largeImageVC!.initialControlsVisible = initialControlsVisible
largeImageVC!.shrinkGestureEnabled = false largeImageVC!.shrinkGestureEnabled = false
if attachment.url.pathExtension == "gif" { if attachment.url.pathExtension == "gif" {

View File

@ -13,7 +13,7 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
var dismissInteractionController: LargeImageInteractionController? var dismissInteractionController: LargeImageInteractionController?
let attachments: [Attachment] let attachments: [Attachment]
let sourcesInfo: [LargeImageViewController.SourceInfo?] let sourcesInfo: [(CGRect, CGFloat)]
let startIndex: Int let startIndex: Int
let pages: [AttachmentViewController] let pages: [AttachmentViewController]
@ -34,7 +34,7 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
viewControllers?.first viewControllers?.first
} }
init(attachments: [Attachment], sourcesInfo: [LargeImageViewController.SourceInfo?], startIndex: Int) { init(attachments: [Attachment], sourcesInfo: [(CGRect, CGFloat)], startIndex: Int) {
self.attachments = attachments self.attachments = attachments
self.sourcesInfo = sourcesInfo self.sourcesInfo = sourcesInfo
self.startIndex = startIndex self.startIndex = startIndex

View File

@ -19,15 +19,10 @@ class GalleryExpandAnimationController: NSObject, UIViewControllerAnimatedTransi
let toVC = transitionContext.viewController(forKey: .to) as? GalleryViewController else { let toVC = transitionContext.viewController(forKey: .to) as? GalleryViewController else {
return return
} }
let attachment = toVC.attachments[toVC.startIndex]
let (sourceFrame, sourceCornerRadius) = toVC.sourcesInfo[toVC.startIndex]
let finalVCFrame = transitionContext.finalFrame(for: toVC) let finalVCFrame = transitionContext.finalFrame(for: toVC)
guard let (sourceFrame, sourceCornerRadius) = toVC.sourcesInfo[toVC.startIndex] else {
toVC.view.frame = finalVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
let attachment = toVC.attachments[toVC.startIndex]
guard let data = ImageCache.attachments.get(attachment.url), let image = UIImage(data: data) else { guard let data = ImageCache.attachments.get(attachment.url), let image = UIImage(data: data) else {
toVC.view.frame = finalVCFrame toVC.view.frame = finalVCFrame

View File

@ -26,10 +26,7 @@ class GalleryShrinkAnimationController: NSObject, UIViewControllerAnimatedTransi
return return
} }
guard let (sourceFrame, sourceCornerRadius) = fromVC.sourcesInfo[fromVC.currentIndex] else { let (sourceFrame, sourceCornerRadius) = fromVC.sourcesInfo[fromVC.currentIndex]
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
let originalVCFrame = fromVC.view.frame let originalVCFrame = fromVC.view.frame
let attachment = fromVC.attachments[fromVC.currentIndex] let attachment = fromVC.attachments[fromVC.currentIndex]

View File

@ -13,9 +13,8 @@ import Gifu
class LargeImageViewController: UIViewController, UIScrollViewDelegate { class LargeImageViewController: UIViewController, UIScrollViewDelegate {
typealias SourceInfo = (frame: CGRect, cornerRadius: CGFloat) var originFrame: CGRect?
var originCornerRadius: CGFloat?
var sourceInfo: SourceInfo?
var dismissInteractionController: LargeImageInteractionController? var dismissInteractionController: LargeImageInteractionController?
@IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var scrollView: UIScrollView!
@ -61,10 +60,11 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
return !controlsVisible return !controlsVisible
} }
init(image: UIImage, description: String?, sourceInfo: SourceInfo?) { init(image: UIImage, description: String?, sourceFrame: CGRect?, sourceCornerRadius: CGFloat?) {
self.image = image self.image = image
self.imageDescription = description self.imageDescription = description
self.sourceInfo = sourceInfo self.originFrame = sourceFrame
self.originCornerRadius = sourceCornerRadius
super.init(nibName: "LargeImageViewController", bundle: nil) super.init(nibName: "LargeImageViewController", bundle: nil)

View File

@ -17,18 +17,13 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from), guard let fromVC = transitionContext.viewController(forKey: .from),
let toVC = transitionContext.viewController(forKey: .to) as? LargeImageViewController else { let toVC = transitionContext.viewController(forKey: .to) as? LargeImageViewController,
return let originFrame = toVC.originFrame else {
}
let finalVCFrame = transitionContext.finalFrame(for: toVC)
guard let (originFrame, originCornerRadius) = toVC.sourceInfo else {
toVC.view.frame = finalVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return return
} }
let containerView = transitionContext.containerView let containerView = transitionContext.containerView
let finalVCFrame = transitionContext.finalFrame(for: toVC)
let image = toVC.imageView.image! let image = toVC.imageView.image!
let ratio = image.size.width / image.size.height let ratio = image.size.width / image.size.height
let width = finalVCFrame.width let width = finalVCFrame.width
@ -41,7 +36,7 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
imageView.animate(withGIFData: gifData) imageView.animate(withGIFData: gifData)
} }
imageView.contentMode = .scaleAspectFill imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = originCornerRadius imageView.layer.cornerRadius = toVC.originCornerRadius!
imageView.layer.masksToBounds = true imageView.layer.masksToBounds = true
let blackView = UIView(frame: finalVCFrame) let blackView = UIView(frame: finalVCFrame)

View File

@ -23,12 +23,8 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageViewController, guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageViewController,
let toVC = transitionContext.viewController(forKey: .to) else { let toVC = transitionContext.viewController(forKey: .to),
return let finalFrame = fromVC.originFrame else {
}
guard let (finalFrame, finalCornerRadius) = fromVC.sourceInfo else {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return return
} }
@ -61,7 +57,7 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra
let duration = transitionDuration(using: transitionContext) let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, animations: { UIView.animate(withDuration: duration, animations: {
imageView.frame = finalFrame imageView.frame = finalFrame
imageView.layer.cornerRadius = finalCornerRadius imageView.layer.cornerRadius = fromVC.originCornerRadius!
blackView.alpha = 0 blackView.alpha = 0
}, completion: { _ in }, completion: { _ in
blackView.removeFromSuperview() blackView.removeFromSuperview()

View File

@ -34,9 +34,9 @@ protocol TuskerNavigationDelegate {
func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView) func showLargeImage(gifData: Data, description: String?, animatingFrom sourceView: UIView)
func gallery(attachments: [Attachment], sourceViews: [UIView?], startIndex: Int) -> GalleryViewController func gallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) -> GalleryViewController
func showGallery(attachments: [Attachment], sourceViews: [UIView?], startIndex: Int) func showGallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int)
func showMoreOptions(forStatus statusID: String) func showMoreOptions(forStatus statusID: String)
@ -99,9 +99,7 @@ extension TuskerNavigationDelegate where Self: UIViewController {
present(vc, animated: true) present(vc, animated: true)
} }
private func sourceViewInfo(_ sourceView: UIView?) -> LargeImageViewController.SourceInfo? { private func sourceViewInfo(_ sourceView: UIView) -> (CGRect, CGFloat) {
guard let sourceView = sourceView else { return nil }
var sourceFrame = sourceView.convert(sourceView.bounds, to: view) var sourceFrame = sourceView.convert(sourceView.bounds, to: view)
if let scrollView = view as? UIScrollView { if let scrollView = view as? UIScrollView {
let scale = scrollView.zoomScale let scale = scrollView.zoomScale
@ -111,17 +109,19 @@ extension TuskerNavigationDelegate where Self: UIViewController {
let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY
sourceFrame = CGRect(x: x, y: y, width: width, height: height) sourceFrame = CGRect(x: x, y: y, width: width, height: height)
} }
return (frame: sourceFrame, cornerRadius: sourceView.layer.cornerRadius) return (sourceFrame, sourceView.layer.cornerRadius)
} }
func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController { func largeImage(_ image: UIImage, description: String?, sourceView: UIView) -> LargeImageViewController {
let vc = LargeImageViewController(image: image, description: description, sourceInfo: sourceViewInfo(sourceView)) let (sourceFrame, sourceCornerRadius) = sourceViewInfo(sourceView)
let vc = LargeImageViewController(image: image, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius)
vc.transitioningDelegate = self vc.transitioningDelegate = self
return vc return vc
} }
func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController { func largeImage(gifData: Data, description: String?, sourceView: UIView) -> LargeImageViewController {
let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceInfo: sourceViewInfo(sourceView)) let (sourceFrame, sourceCornerRadius) = sourceViewInfo(sourceView)
let vc = LargeImageViewController(image: UIImage(data: gifData)!, description: description, sourceFrame: sourceFrame, sourceCornerRadius: sourceCornerRadius)
vc.transitioningDelegate = self vc.transitioningDelegate = self
vc.gifData = gifData vc.gifData = gifData
return vc return vc
@ -135,14 +135,14 @@ extension TuskerNavigationDelegate where Self: UIViewController {
present(largeImage(gifData: gifData, description: description, sourceView: sourceView), animated: true) present(largeImage(gifData: gifData, description: description, sourceView: sourceView), animated: true)
} }
func gallery(attachments: [Attachment], sourceViews: [UIView?], startIndex: Int) -> GalleryViewController { func gallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) -> GalleryViewController {
let sourcesInfo = sourceViews.map(sourceViewInfo) let sourcesInfo = sourceViews.map(sourceViewInfo)
let vc = GalleryViewController(attachments: attachments, sourcesInfo: sourcesInfo, startIndex: startIndex) let vc = GalleryViewController(attachments: attachments, sourcesInfo: sourcesInfo, startIndex: startIndex)
vc.transitioningDelegate = self vc.transitioningDelegate = self
return vc return vc
} }
func showGallery(attachments: [Attachment], sourceViews: [UIView?], startIndex: Int) { func showGallery(attachments: [Attachment], sourceViews: [UIView], startIndex: Int) {
present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true) present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true)
} }

View File

@ -13,79 +13,60 @@ class AttachmentsContainerView: UIView {
var delegate: AttachmentViewDelegate? var delegate: AttachmentViewDelegate?
let attachmentViews: NSHashTable<AttachmentView> = .weakObjects()
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
self.isUserInteractionEnabled = true self.isUserInteractionEnabled = true
} }
func getAttachmentView(for attachment: Attachment) -> AttachmentView? {
return attachmentViews.allObjects.first { $0.attachment.id == attachment.id }
}
// MARK: - User Interaface
func updateUI(status: Status) { func updateUI(status: Status) {
let attachments = status.attachments.filter { $0.kind == .image } let attachments = status.attachments.filter { $0.kind == .image }
attachmentViews.removeAllObjects()
subviews.forEach { $0.removeFromSuperview() }
if attachments.count > 0 { if attachments.count > 0 {
self.isHidden = false self.isHidden = false
let mainView: UIView
switch attachments.count { switch attachments.count {
case 1: case 1:
makeMainView(createAttachmentView(attachments[0])) mainView = createAttachmentView(attachments[0])
case 2: case 2:
let left = createAttachmentView(attachments[0]) mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
makeMainView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ createAttachmentView(attachments[0]),
left,
createAttachmentView(attachments[1]) createAttachmentView(attachments[1])
]))
NSLayoutConstraint.activate([
left.halfWidth()
]) ])
case 3: case 3:
let left = createAttachmentView(attachments[0]) mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
let topRight = createAttachmentView(attachments[1]) createAttachmentView(attachments[0]),
makeMainView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [ createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight, createAttachmentView(attachments[1]),
createAttachmentView(attachments[2]) createAttachmentView(attachments[2])
]) ])
]))
NSLayoutConstraint.activate([
left.halfWidth(),
topRight.halfHeight(),
]) ])
case 4: case 4:
let topLeft = createAttachmentView(attachments[0]) mainView = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
let left = createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topLeft,
createAttachmentView(attachments[2])
])
let topRight = createAttachmentView(attachments[1])
makeMainView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [ createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight, createAttachmentView(attachments[0]),
createAttachmentView(attachments[2])
]),
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
createAttachmentView(attachments[1]),
createAttachmentView(attachments[3]) createAttachmentView(attachments[3])
]) ])
]))
NSLayoutConstraint.activate([
left.halfWidth(),
topLeft.halfHeight(),
topRight.halfHeight(),
]) ])
default: default:
fatalError("Too many attachments") fatalError("Too many attachments")
} }
addSubview(mainView)
NSLayoutConstraint.activate([
mainView.leadingAnchor.constraint(equalTo: leadingAnchor),
mainView.trailingAnchor.constraint(equalTo: trailingAnchor),
mainView.topAnchor.constraint(equalTo: topAnchor),
mainView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
} else { } else {
self.isHidden = true self.isHidden = true
subviews.forEach { $0.removeFromSuperview() }
} }
} }
@ -93,49 +74,15 @@ class AttachmentsContainerView: UIView {
let attachmentView = AttachmentView(attachment: attachment) let attachmentView = AttachmentView(attachment: attachment)
attachmentView.delegate = delegate attachmentView.delegate = delegate
attachmentView.translatesAutoresizingMaskIntoConstraints = false attachmentView.translatesAutoresizingMaskIntoConstraints = false
attachmentViews.add(attachmentView)
return attachmentView return attachmentView
} }
private func createAttachmentsStack(axis: NSLayoutConstraint.Axis, arrangedSubviews: [UIView]) -> UIStackView { private func createAttachmentsStack(axis: NSLayoutConstraint.Axis, arrangedSubviews: [UIView]) -> UIStackView {
let stack = UIStackView(arrangedSubviews: arrangedSubviews) let stack = UIStackView(arrangedSubviews: arrangedSubviews)
stack.axis = axis stack.axis = axis
stack.spacing = 4 stack.spacing = 8
stack.translatesAutoresizingMaskIntoConstraints = false stack.translatesAutoresizingMaskIntoConstraints = false
return stack return stack
} }
private func makeMainView(_ view: UIView) {
addSubview(view)
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: leadingAnchor),
view.trailingAnchor.constraint(equalTo: trailingAnchor),
view.topAnchor.constraint(equalTo: topAnchor),
view.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
}
fileprivate extension UIView {
enum RelativeSize {
case full, half
var multiplier: CGFloat {
switch self {
case .full:
return 1
case .half:
return 0.5
}
}
}
func halfWidth(spacing: CGFloat = 4) -> NSLayoutConstraint {
return widthAnchor.constraint(equalTo: superview!.widthAnchor, multiplier: 0.5, constant: -spacing / 2)
}
func halfHeight(spacing: CGFloat = 4) -> NSLayoutConstraint {
return heightAnchor.constraint(equalTo: superview!.heightAnchor, multiplier: 0.5, constant: -spacing / 2)
}
} }

View File

@ -193,10 +193,11 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
extension ConversationMainStatusTableViewCell: AttachmentViewDelegate { extension ConversationMainStatusTableViewCell: AttachmentViewDelegate {
func showLargeAttachment(for attachmentView: AttachmentView) { func showLargeAttachment(for attachmentView: AttachmentView) {
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } if let gifData = attachmentView.gifData {
let startIndex = status.attachments.firstIndex { $0.id == attachmentView.attachment.id } ?? 0 delegate?.showLargeImage(gifData: gifData, description: attachmentView.attachment.description, animatingFrom: attachmentView)
let sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:)) } else {
delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: startIndex) delegate?.showLargeImage(attachmentView.image!, description: attachmentView.attachment.description, animatingFrom: attachmentView)
}
} }
} }

View File

@ -307,7 +307,9 @@ extension StatusTableViewCell: AttachmentViewDelegate {
func showLargeAttachment(for attachmentView: AttachmentView) { func showLargeAttachment(for attachmentView: AttachmentView) {
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
let startIndex = status.attachments.firstIndex { $0.id == attachmentView.attachment.id } ?? 0 let startIndex = status.attachments.firstIndex { $0.id == attachmentView.attachment.id } ?? 0
let sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:)) let sourceViews = status.attachments.map { attachment in
attachmentsView.subviews.first { ($0 as! AttachmentView).attachment.id == attachment.id }!
}
delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: startIndex) delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: startIndex)
} }
} }