I am trying to do a toast notification by adding a view to the window and it has a progress bar and a button to click if it wants to dismiss before the time expires.
The button click is not working, it didn't show that it was clicked.
I set up isUserInteractionEnabled
for both view and button, I checked the view hierarchy to see if any view is blocking the toastView
, but the toastView
is on top of all the others and still the button doesn't click.
Here's is my code for the toastView
and the function that is inside my UIViewController
subclass:
ToastView
class iFractalToastView: UIView {
private let tituloLabel = {
let label = iFractalTituloLabel(title: "STATUS", color: .PGF)
label.textAlignment = .center
label.font = UIFont(name: "MuseoSans-700", size: 14)
return label
}()
private let messageLabel = {
let label = iFractalBodyLabel(title: "", isBold: false)
label.textAlignment = .center
return label
}()
let progressBar = UIProgressView(progressViewStyle: .default)
let dismissButton = iFractalAlertButton(title: "OK")
var dismissAction: (() -> Void)?
private var message = String()
private var button = Bool()
init(message: String, button: Bool) {
super.init(frame: .zero)
self.isUserInteractionEnabled = true
dismissButton.isUserInteractionEnabled = true
dismissButton.addTarget(self, action: #selector(dismissButtonTapped), for: .touchUpInside)
self.message = message
self.button = button
self.translatesAutoresizingMaskIntoConstraints = false
iniciateSubViews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setProgress(_ progress: Float) {
progressBar.setProgress(progress, animated: true)
}
@objc func dismissButtonTapped() {
print("ENTROUU dismissButtonTapped")
dismissAction?()
}
func imageWithColor(_ color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = 1
let image = UIGraphicsImageRenderer(size: size, format: format).image { rendererContext in
color.setFill()
rendererContext.fill(CGRect(origin: .zero, size: size))
}
return image
}
private func iniciateSubViews() {
addSubViews()
constraintsSubViews()
configureSubviews()
}
private func addSubViews() {
addSubview(tituloLabel)
addSubview(messageLabel)
addSubview(progressBar)
if button == true {
addSubview(dismissButton)
}
}
private func constraintsSubViews() {
tituloLabel.translatesAutoresizingMaskIntoConstraints = false
messageLabel.translatesAutoresizingMaskIntoConstraints = false
progressBar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tituloLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10),
tituloLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10),
tituloLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10),
messageLabel.topAnchor.constraint(equalTo: tituloLabel.bottomAnchor, constant: 10),
messageLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10),
messageLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10),
progressBar.topAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 10),
progressBar.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0),
progressBar.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0),
progressBar.heightAnchor.constraint(equalToConstant: 4)
])
if button == true {
dismissButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
dismissButton.topAnchor.constraint(equalTo: progressBar.bottomAnchor, constant: 10),
dismissButton.centerXAnchor.constraint(equalTo: self.centerXAnchor),
dismissButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10),
dismissButton.widthAnchor.constraint(equalToConstant: 100)
])
}
else {
NSLayoutConstraint.activate([
progressBar.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10)
])
}
}
private func configureSubviews() {
backgroundColor = .TD90
layer.cornerRadius = 10
layer.borderColor = UIColor.PGF.cgColor
layer.borderWidth = 1.0
clipsToBounds = true
// Configure the message label
messageLabel.text = message
messageLabel.textAlignment = .center
messageLabel.numberOfLines = 0
// Configure the progress bar
progressBar.progress = 0.0
progressBar.progressImage = imageWithColor(.PGF)
//dismissButton.setTitle("OK", for: .normal)
//dismissButton.setTitleColor(.white, for: .normal)
dismissButton.clipsToBounds = true
}
}
UIViewController function
func showToast(message: String, duration: TimeInterval = 60.0, button: Bool = false) {
let toastView = iFractalToastView(message: message, button: button)
// Configure toast view appearance
toastView.alpha = 0.0
toastView.translatesAutoresizingMaskIntoConstraints = false
if button == true {
toastView.dismissAction = {
UIView.animate(withDuration: 0.5, animations: {
toastView.alpha = 0.0
}) { _ in
toastView.removeFromSuperview()
}
}
}
// Add the toast view to the view controller's view
let window = UIApplication.shared.keyWindow!
window.addSubview(toastView)
// Set up constraints
if button == false {
NSLayoutConstraint.activate([
toastView.leadingAnchor.constraint(equalTo: window.leadingAnchor, constant: 10),
toastView.trailingAnchor.constraint(equalTo: window.trailingAnchor, constant: -10),
toastView.topAnchor.constraint(equalTo: window.topAnchor, constant: 41)
])
}
else {
NSLayoutConstraint.activate([
toastView.leadingAnchor.constraint(equalTo: window.leadingAnchor, constant: 10),
toastView.trailingAnchor.constraint(equalTo: window.trailingAnchor, constant: -10),
toastView.centerXAnchor.constraint(equalTo: window.centerXAnchor),
toastView.centerYAnchor.constraint(equalTo: window.centerYAnchor)
])
}
window.bringSubviewToFront(toastView)
// Animate the toast view appearance
UIView.animate(withDuration: 0.5, animations: {
toastView.alpha = 1.0
}) { _ in
// Animate the progress bar
let progress = Float(duration)
toastView.setProgress(progress)
// Animate the toast view disappearance
UIView.animate(withDuration: 0.5, delay: duration, options: .curveEaseOut, animations: {
toastView.alpha = 0.0
}) { _ in
toastView.removeFromSuperview()
}
}
}
The problem lies in this part:
// Animate the toast view disappearance
UIView.animate(withDuration: 0.5, delay: duration, options: .curveEaseOut, animations: {
toastView.alpha = 0.0
}) { _ in
toastView.removeFromSuperview()
}
allowUserInteraction
to the animation options.toastView.layer.opacity
upon toast view appearance completion, it will be zero.How to fix it:
// Animate the toast view disappearance
UIView.animate(
withDuration: 0.5,
delay: duration,
// Add allowUserInteraction
options: [.curveEaseOut, .allowUserInteraction],
animations: {
// Set alpha more then 0.01
toastView.alpha = 0.2
}) { _ in
toastView.removeFromSuperview()
}
or:
// In this case, there's no need to include "allowUserInteraction" or "0.02 alpha"
// Because UIView.animate will not be called immediately.
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
UIView.animate(
withDuration: 0.5,
delay: 0,
options: .curveEaseOut,
animations: {
toastView.alpha = 0.0
}) { _ in
toastView.removeFromSuperview()
}
}