diff --git a/Tusker/Extensions/UIViewController+Delegates.swift b/Tusker/Extensions/UIViewController+Delegates.swift index 25df153d..68728c87 100644 --- a/Tusker/Extensions/UIViewController+Delegates.swift +++ b/Tusker/Extensions/UIViewController+Delegates.swift @@ -12,7 +12,8 @@ extension UIViewController: UIViewControllerTransitioningDelegate { public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { if presented is LargeImageViewController { return LargeImageExpandAnimationController() - } else if presented is GalleryViewController { + } else if let presented = presented as? GalleryViewController, + presented.sourcesInfo[presented.startIndex] != nil { return GalleryExpandAnimationController() } return nil @@ -21,7 +22,8 @@ extension UIViewController: UIViewControllerTransitioningDelegate { public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { if let dismissed = dismissed as? LargeImageViewController { return LargeImageShrinkAnimationController(interactionController: dismissed.dismissInteractionController) - } else if let dismissed = dismissed as? GalleryViewController { + } else if let dismissed = dismissed as? GalleryViewController, + dismissed.sourcesInfo[dismissed.currentIndex] != nil { return GalleryShrinkAnimationController(interactionController: dismissed.dismissInteractionController) } return nil diff --git a/Tusker/Views/Attachments/AttachmentView.swift b/Tusker/Views/Attachments/AttachmentView.swift index 415ad79b..e777676d 100644 --- a/Tusker/Views/Attachments/AttachmentView.swift +++ b/Tusker/Views/Attachments/AttachmentView.swift @@ -11,7 +11,7 @@ import Pachyderm import Gifu protocol AttachmentViewDelegate { - func showLargeAttachment(for attachmentView: AttachmentView) + func showAttachmentsGallery(startingAt index: Int) } class AttachmentView: UIImageView, GIFAnimatable { @@ -19,15 +19,18 @@ class AttachmentView: UIImageView, GIFAnimatable { var delegate: AttachmentViewDelegate? var attachment: Attachment! - var gifData: Data? + var index: Int! + var gifData: Data? + public lazy var animator: Animator? = Animator(withDelegate: self) - init(attachment: Attachment) { + init(attachment: Attachment, index: Int) { super.init(image: nil) commonInit() self.attachment = attachment + self.index = index loadImage() } @@ -62,7 +65,8 @@ class AttachmentView: UIImageView, GIFAnimatable { } @objc func imagePressed() { - delegate?.showLargeAttachment(for: self) +// delegate?.showLargeAttachment(for: self) + delegate?.showAttachmentsGallery(startingAt: index) } } diff --git a/Tusker/Views/Attachments/AttachmentsContainerView.swift b/Tusker/Views/Attachments/AttachmentsContainerView.swift index be5d580c..34ef9a15 100644 --- a/Tusker/Views/Attachments/AttachmentsContainerView.swift +++ b/Tusker/Views/Attachments/AttachmentsContainerView.swift @@ -14,6 +14,7 @@ class AttachmentsContainerView: UIView { var delegate: AttachmentViewDelegate? var statusID: String! + var attachments: [Attachment]! let attachmentViews: NSHashTable = .weakObjects() @@ -47,7 +48,7 @@ class AttachmentsContainerView: UIView { func updateUI(status: Status) { self.statusID = status.id - let attachments = status.attachments.filter { $0.kind == .image } + attachments = status.attachments.filter { $0.kind == .image } attachmentViews.removeAllObjects() subviews.forEach { $0.removeFromSuperview() } @@ -57,24 +58,24 @@ class AttachmentsContainerView: UIView { switch attachments.count { case 1: - fillView(createAttachmentView(attachments[0])) + fillView(createAttachmentView(index: 0)) case 2: - let left = createAttachmentView(attachments[0]) + let left = createAttachmentView(index: 0) fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ left, - createAttachmentView(attachments[1]) + createAttachmentView(index: 1) ])) NSLayoutConstraint.activate([ left.halfWidth() ]) case 3: - let left = createAttachmentView(attachments[0]) - let topRight = createAttachmentView(attachments[1]) + let left = createAttachmentView(index: 0) + let topRight = createAttachmentView(index: 1) fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ left, createAttachmentsStack(axis: .vertical, arrangedSubviews: [ topRight, - createAttachmentView(attachments[2]) + createAttachmentView(index: 2) ]) ])) NSLayoutConstraint.activate([ @@ -82,17 +83,17 @@ class AttachmentsContainerView: UIView { topRight.halfHeight(), ]) case 4: - let topLeft = createAttachmentView(attachments[0]) + let topLeft = createAttachmentView(index: 0) let left = createAttachmentsStack(axis: .vertical, arrangedSubviews: [ topLeft, - createAttachmentView(attachments[2]) + createAttachmentView(index: 2) ]) - let topRight = createAttachmentView(attachments[1]) + let topRight = createAttachmentView(index: 1) fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ left, createAttachmentsStack(axis: .vertical, arrangedSubviews: [ topRight, - createAttachmentView(attachments[3]) + createAttachmentView(index: 3) ]) ])) NSLayoutConstraint.activate([ @@ -100,8 +101,41 @@ class AttachmentsContainerView: UIView { topLeft.halfHeight(), topRight.halfHeight(), ]) - default: - fatalError("Too many attachments") + default: // more than 4 + let moreView = UIView() + moreView.backgroundColor = .secondarySystemBackground + moreView.translatesAutoresizingMaskIntoConstraints = false + moreView.isUserInteractionEnabled = true + moreView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(moreViewTapped))) + let moreLabel = UILabel() + moreLabel.text = "\(attachments.count - 3) more..." + moreLabel.textColor = .secondaryLabel + moreLabel.textAlignment = .center + moreLabel.translatesAutoresizingMaskIntoConstraints = false + moreView.addSubview(moreLabel) + + let topLeft = createAttachmentView(index: 0) + let left = createAttachmentsStack(axis: .vertical, arrangedSubviews: [ + topLeft, + createAttachmentView(index: 2) + ]) + let topRight = createAttachmentView(index: 1) + fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ + left, + createAttachmentsStack(axis: .vertical, arrangedSubviews: [ + topRight, + moreView + ]) + ])) + NSLayoutConstraint.activate([ + left.halfWidth(), + topLeft.halfHeight(), + topRight.halfHeight(), + moreView.leadingAnchor.constraint(equalTo: moreLabel.leadingAnchor), + moreLabel.trailingAnchor.constraint(equalTo: moreView.trailingAnchor), + moreView.topAnchor.constraint(equalTo: moreLabel.topAnchor), + moreLabel.bottomAnchor.constraint(equalTo: moreView.bottomAnchor), + ]) } } else { self.isHidden = true @@ -114,8 +148,8 @@ class AttachmentsContainerView: UIView { } } - private func createAttachmentView(_ attachment: Attachment) -> AttachmentView { - let attachmentView = AttachmentView(attachment: attachment) + private func createAttachmentView(index: Int) -> AttachmentView { + let attachmentView = AttachmentView(attachment: attachments[index], index: index) attachmentView.delegate = delegate attachmentView.translatesAutoresizingMaskIntoConstraints = false attachmentViews.add(attachmentView) @@ -133,17 +167,12 @@ class AttachmentsContainerView: UIView { private func createBlurView() { let blur = UIBlurEffect(style: .dark) let blurView = UIVisualEffectView(effect: blur) - // let blurView = UIVisualEffectView(frame: bounds) blurView.effect = blur blurView.translatesAutoresizingMaskIntoConstraints = false fillView(blurView) - // addSubview(blurView) let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blur, style: .label)) - // let vibrancyView = UIVisualEffectView(frame: blurView.bounds) - // vibrancyView.effect = UIVibrancyEffect(blurEffect: blur, style: .label) vibrancyView.translatesAutoresizingMaskIntoConstraints = false fillView(vibrancyView, in: blurView.contentView) - // addSubview(vibrancyView) blurView.contentView.addSubview(vibrancyView) let image = UIImage(systemName: "eye")! @@ -179,8 +208,6 @@ class AttachmentsContainerView: UIView { hideButton.alpha = 0 hideButton.layer.cornerRadius = 2 hideButton.layer.masksToBounds = true -// hideButton.backgroundColor = tintColor -// hideButton.tintColor = hideButton.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal) hideButton.addTarget(self, action: #selector(hideButtonTapped), for: .touchUpInside) @@ -231,6 +258,12 @@ class AttachmentsContainerView: UIView { blurView.alpha = 1 } } + + @objc func moreViewTapped() { + guard attachments.count > 4 else { return } + // the more view shows up in place of the fourth attachemtn view, show tapping it should start at the fourth attachment + delegate?.showAttachmentsGallery(startingAt: 3) + } } diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift index 15c83eb5..5c57a1fc 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift @@ -192,11 +192,10 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive } extension ConversationMainStatusTableViewCell: AttachmentViewDelegate { - func showLargeAttachment(for attachmentView: AttachmentView) { + func showAttachmentsGallery(startingAt index: Int) { 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 sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:)) - delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: startIndex) + delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: index) } } diff --git a/Tusker/Views/Status/StatusTableViewCell.swift b/Tusker/Views/Status/StatusTableViewCell.swift index d4ea1470..cc949314 100644 --- a/Tusker/Views/Status/StatusTableViewCell.swift +++ b/Tusker/Views/Status/StatusTableViewCell.swift @@ -304,11 +304,10 @@ extension StatusTableViewCell: TableViewSwipeActionProvider { } extension StatusTableViewCell: AttachmentViewDelegate { - func showLargeAttachment(for attachmentView: AttachmentView) { + func showAttachmentsGallery(startingAt index: Int) { 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 sourceViews = status.attachments.map(attachmentsView.getAttachmentView(for:)) - delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: startIndex) + delegate?.showGallery(attachments: status.attachments, sourceViews: sourceViews, startIndex: index) } }