mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
5.0 KiB
Swift
164 lines
5.0 KiB
Swift
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
|
|
import UIKit
|
|
import SessionUIKit
|
|
|
|
public class ToastController: ToastViewDelegate {
|
|
public static let font: UIFont = .systemFont(ofSize: Values.mediumFontSize)
|
|
public static let boldFont: UIFont = .boldSystemFont(ofSize: Values.mediumFontSize)
|
|
static var currentToastController: ToastController?
|
|
|
|
private let id: UUID
|
|
private let toastView: ToastView
|
|
private var isDismissing: Bool
|
|
|
|
// MARK: Initializers
|
|
|
|
public convenience init(text: String, background: ThemeValue) {
|
|
self.init(text: NSAttributedString(string: text), background: background)
|
|
}
|
|
|
|
required public init(text: NSAttributedString, background: ThemeValue) {
|
|
id = UUID()
|
|
toastView = ToastView(background: background)
|
|
toastView.attributedText = text
|
|
isDismissing = false
|
|
toastView.delegate = self
|
|
}
|
|
|
|
// MARK: Public
|
|
|
|
public func presentToastView(
|
|
fromBottomOfView view: UIView,
|
|
inset: CGFloat,
|
|
duration: DispatchTimeInterval = .milliseconds(1500)
|
|
) {
|
|
toastView.alpha = 0
|
|
view.addSubview(toastView)
|
|
|
|
toastView.setContentCompressionResistancePriority(.required, for: .vertical)
|
|
toastView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
toastView.center(.horizontal, in: view)
|
|
toastView.pin(.bottom, to: .bottom, of: view, withInset: -inset)
|
|
toastView.widthAnchor
|
|
.constraint(
|
|
lessThanOrEqualTo: view.widthAnchor,
|
|
constant: -(Values.mediumSpacing * 2)
|
|
)
|
|
.isActive = true
|
|
|
|
if let currentToastController = ToastController.currentToastController {
|
|
currentToastController.dismissToastView()
|
|
ToastController.currentToastController = nil
|
|
}
|
|
ToastController.currentToastController = self
|
|
|
|
UIView.animate(withDuration: 0.1) {
|
|
self.toastView.alpha = 1
|
|
}
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
|
|
// intentional strong reference to self.
|
|
// As with an AlertController, the caller likely expects toast to
|
|
// be presented and dismissed without maintaining a strong reference to ToastController
|
|
self.dismissToastView()
|
|
}
|
|
}
|
|
|
|
// MARK: ToastViewDelegate
|
|
|
|
func didTapToastView(_ toastView: ToastView) {
|
|
self.dismissToastView()
|
|
}
|
|
|
|
func didSwipeToastView(_ toastView: ToastView) {
|
|
self.dismissToastView()
|
|
}
|
|
|
|
// MARK: Internal
|
|
|
|
func dismissToastView() {
|
|
guard !isDismissing else { return }
|
|
|
|
isDismissing = true
|
|
|
|
if ToastController.currentToastController?.id == self.id {
|
|
ToastController.currentToastController = nil
|
|
}
|
|
|
|
UIView.animate(
|
|
withDuration: 0.1,
|
|
animations: {
|
|
self.toastView.alpha = 0
|
|
},
|
|
completion: { [weak self] _ in
|
|
self?.toastView.removeFromSuperview()
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
protocol ToastViewDelegate: AnyObject {
|
|
func didTapToastView(_ toastView: ToastView)
|
|
func didSwipeToastView(_ toastView: ToastView)
|
|
}
|
|
|
|
class ToastView: UIView {
|
|
|
|
var attributedText: NSAttributedString? {
|
|
get { return label.attributedText }
|
|
set { label.attributedText = newValue }
|
|
}
|
|
weak var delegate: ToastViewDelegate?
|
|
|
|
private let label: UILabel = {
|
|
let result: UILabel = UILabel()
|
|
result.font = ToastController.font
|
|
result.themeTextColor = .textPrimary
|
|
result.textAlignment = .center
|
|
result.numberOfLines = 0
|
|
|
|
return result
|
|
}()
|
|
|
|
// MARK: Initializers
|
|
|
|
init(background: ThemeValue) {
|
|
super.init(frame: .zero)
|
|
|
|
self.themeBackgroundColor = background
|
|
|
|
self.addSubview(label)
|
|
label.pin(.top, to: .top, of: self, withInset: Values.smallSpacing)
|
|
label.pin(.leading, to: .leading, of: self, withInset: Values.largeSpacing)
|
|
label.pin(.trailing, to: .trailing, of: self, withInset: -Values.largeSpacing)
|
|
label.pin(.bottom, to: .bottom, of: self, withInset: -Values.smallSpacing)
|
|
|
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(gesture:)))
|
|
self.addGestureRecognizer(tapGesture)
|
|
|
|
let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(didSwipe(gesture:)))
|
|
self.addGestureRecognizer(swipeGesture)
|
|
}
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
|
|
self.layer.cornerRadius = (self.frame.height / 2)
|
|
}
|
|
|
|
// MARK: Gestures
|
|
|
|
@objc func didTap(gesture: UITapGestureRecognizer) {
|
|
self.delegate?.didTapToastView(self)
|
|
}
|
|
|
|
@objc func didSwipe(gesture: UISwipeGestureRecognizer) {
|
|
self.delegate?.didSwipeToastView(self)
|
|
}
|
|
}
|