Show toast when there are no new posts

This commit is contained in:
Shadowfacts 2021-08-15 18:19:25 -04:00
parent 1fda4248ec
commit f109253bba
4 changed files with 79 additions and 19 deletions

View File

@ -236,6 +236,18 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController<Timel
snapshot.appendItems(newIdentifiers, toSection: .statuses)
}
completion(.success(snapshot))
if statuses.isEmpty {
DispatchQueue.main.async {
var config = ToastConfiguration(title: "You're all caught up")
config.edge = .top
config.dismissAutomaticallyAfter = 2
config.action = { (toast) in
toast.dismissToast(animated: true)
}
self.showToast(configuration: config, animated: true)
}
}
}
}
}

View File

@ -10,6 +10,7 @@ import UIKit
struct ToastConfiguration {
var systemImageName: String?
var titleFont: UIFont = .boldSystemFont(ofSize: 14)
var title: String
var subtitle: String?
var actionTitle: String?
@ -17,6 +18,7 @@ struct ToastConfiguration {
var edgeSpacing: CGFloat = 8
var edge: Edge = .automatic
var dismissOnScroll = true
var dismissAutomaticallyAfter: TimeInterval? = nil
init(title: String) {
self.title = title

View File

@ -14,6 +14,8 @@ class ToastView: UIView {
private var shrinkAnimator: UIViewPropertyAnimator?
private var recognizedGesture = false
private var shouldDismissOnScroll = false
private(set) var shouldDismissAutomatically = true
private var offscreenTranslation: CGFloat {
var translation = bounds.height + configuration.edgeSpacing
@ -62,7 +64,7 @@ class ToastView: UIView {
let titleLabel = UILabel()
titleLabel.text = configuration.title
titleLabel.textColor = .white
titleLabel.font = .boldSystemFont(ofSize: 14)
titleLabel.font = configuration.titleFont
titleLabel.adjustsFontSizeToFitWidth = true
if let subtitle = configuration.subtitle {
@ -109,10 +111,40 @@ class ToastView: UIView {
layer.shadowPath = CGPath(roundedRect: bounds, cornerWidth: layer.cornerRadius, cornerHeight: layer.cornerRadius, transform: nil)
}
func dismissToast(animated: Bool) {
guard animated else {
removeFromSuperview()
return
}
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
self.transform = CGAffineTransform(translationX: 0, y: self.offscreenTranslation)
} completion: { (_) in
self.removeFromSuperview()
}
}
func animateAppearance() {
self.transform = CGAffineTransform(translationX: 0, y: offscreenTranslation)
let duration = 0.5
let velocity = 0.5
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.65, initialSpringVelocity: velocity, options: []) {
self.transform = .identity
}
}
func setupDismissOnScroll(connectedTo scrollView: UIScrollView) {
guard configuration.dismissOnScroll else { return }
scrollView.panGestureRecognizer.addTarget(self, action: #selector(scrollViewPanGestureRecognized))
}
// MARK: - Interaction
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
recognizedGesture = false
shouldDismissAutomatically = false
shrinkAnimator = UIViewPropertyAnimator(duration: 0.1, curve: .easeInOut) {
self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
}
@ -145,6 +177,7 @@ class ToastView: UIView {
switch recognizer.state {
case .began:
recognizedGesture = true
shouldDismissAutomatically = false
UIView.animate(withDuration: 0.1, delay: 0, options: [.curveEaseInOut, .allowUserInteraction]) {
self.transform = .identity
}
@ -199,24 +232,23 @@ class ToastView: UIView {
}
}
func dismissToast(animated: Bool) {
guard animated else {
removeFromSuperview()
return
}
UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseInOut) {
self.transform = CGAffineTransform(translationX: 0, y: self.offscreenTranslation)
} completion: { (_) in
self.removeFromSuperview()
}
}
func animateAppearance() {
self.transform = CGAffineTransform(translationX: 0, y: offscreenTranslation)
let duration = 0.5
let velocity = 0.5
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.65, initialSpringVelocity: velocity, options: []) {
self.transform = .identity
@objc private func scrollViewPanGestureRecognized(_ recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case .began:
shouldDismissOnScroll = true
case .changed:
let translation = recognizer.translation(in: recognizer.view).y
if shouldDismissOnScroll && abs(translation) > 50 {
dismissToast(animated: true)
shouldDismissOnScroll = false
}
case .ended, .cancelled:
shouldDismissOnScroll = false
default:
break
}
}

View File

@ -11,6 +11,7 @@ import UIKit
protocol ToastableViewController: UIViewController {
var toastParentView: UIView { get }
var toastScrollView: UIScrollView? { get }
}
@ -33,6 +34,7 @@ extension ToastableViewController {
}
var toastParentView: UIView { view }
var toastScrollView: UIScrollView? { view as? UIScrollView }
func showToast(configuration config: ToastConfiguration, animated: Bool) {
currentToast?.dismissToast(animated: false)
@ -67,6 +69,18 @@ extension ToastableViewController {
if animated {
toast.animateAppearance()
}
if config.dismissOnScroll,
let scrollView = toastScrollView {
toast.setupDismissOnScroll(connectedTo: scrollView)
}
if let time = config.dismissAutomaticallyAfter {
DispatchQueue.main.asyncAfter(deadline: .now() + time) { [weak toast] in
guard let toast = toast, toast.shouldDismissAutomatically else { return }
toast.dismissToast(animated: true)
}
}
}
private func effectiveEdge(edge: ToastConfiguration.Edge) -> ToastConfiguration.Edge {