I have made a Toast function as below
CommonFunctions.swift
import UIKit
func showToast(toastTitle: String, toastText: String, type : ToastType, position : ToastPosition = .center, style : ToastStyle = .defaultToast){
if (style == .defaultToast) {
if let foundView = topVC?.view.viewWithTag(toastTagNumber) {
foundView.removeFromSuperview()
}
toastTimer?.invalidate()
toastTimer = nil
if let currentView : UIView = topVC?.view {
let toastView : UIView = UIView()
currentView.addSubview(toastView)
toastView.translatesAutoresizingMaskIntoConstraints = false
toastView.tag = toastTagNumber
toastView.backgroundColor = AppColors.topBarBackgroundColor
toastView.fillHorizontally(padding: 10*iPhoneFactorX)
toastView.Bottom == currentView.Bottom - (bottomBarInnerHeight + safeAreaBottomHeight + (10*iPhoneFactorX))
toastView.addRadius(radius: 15*iPhoneFactorX)
let toast_headerLabel : UILabel = UILabel()
toastView.addSubview(toast_headerLabel)
toast_headerLabel.translatesAutoresizingMaskIntoConstraints = false
toast_headerLabel.Top == toastView.Top + (10*iPhoneFactorX)
toast_headerLabel.fillHorizontally(padding: 5*iPhoneFactorX)
toast_headerLabel.adjustDefaultLabel(fontColor: AppColors.whiteColor_FFFFFF, fontSize: 15, fontType: .bold)
toast_headerLabel.text = toastTitle
toast_headerLabel.isHidden = toastTitle.isEmpty
toast_headerLabel.textAlignment = .center
let toast_textLabel : UILabel = UILabel()
toastView.addSubview(toast_textLabel)
toast_textLabel.translatesAutoresizingMaskIntoConstraints = false
toast_textLabel.Top == toast_headerLabel.Bottom + (10*iPhoneFactorX)
toast_textLabel.fillHorizontally(padding: 5*iPhoneFactorX)
toast_textLabel.adjustDefaultLabel(fontColor: AppColors.whiteColor_FFFFFF, fontSize: 14)
toast_textLabel.text = toastText
toast_textLabel.textAlignment = .center
toastView.Bottom == toast_textLabel.Bottom - (10*iPhoneFactorX)
toastTimer = Timer.scheduledTimer(withTimeInterval: TimeInterval(toastDismissDuration), repeats: false) { (Timer) in
hideToast()
}
let toast_button : UIButton = UIButton(type: .custom)
toastView.addSubview(toast_button)
toast_button.translatesAutoresizingMaskIntoConstraints = false
toast_button.setTitle("", for: .normal)
toast_button.fillContainer()
toast_button.backgroundColor = .clear
// problem comes here
toast_button.addTarget(currentView, action: #selector(clickedDefaultToast), for: .touchUpInside)
toastView.addSubview(toast_button)
}
}
}
func clickedDefaultToast() {
print("fapa=clickedDefaultToast==0001")
}
Calling it as below.
showToast(toastTitle: "error", toastText: "toast text comes here", type: .error)
Toast is showing and goes away in 3 seconds which is not an issue.
I want to remove Toast if anyone click on it. So I add a button. However issue coming when adding target on it.
Error I get is as below.
Argument of '#selector' cannot refer to global function 'clickedDefaultToast()'
If I add @objc
for function, I get error as
@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
How can I achieve this to dismiss when clicked?
Targets respond to selectors. It doesn't make sense to pass a selector of clickedDefaultToast
, when the target currentView
doesn't respond to it, regardless of whether Swift allows you to do this.
Selectors are usually methods declared in a class (and instances of that class would respond to the selector), and so Swift has the #selector(...)
syntax to let you get a Selector
from an instance method.
If you just want to call a global function when the button is clicked, addAction
instead of addTarget
. This allows you to just pass a Swift closure instead of a selector.
toast_button.addAction(UIAction { clickedDefaultToast() }, for: .touchUpInside)
Otherwise, you would need to change your design to use target-action pairs. The usage could be something like:
// declare a Toast property at the class level
let toast = Toast()
// in some method...
toast.showToast(...)
You'd move showToast
and clickedDefaultToast
into a new Toast
class. You would use self
as the target of the button, and self
can respond to the clickedDefaultToast
selector.
In my opinion, this design is better because you can also store the currently displayed toast UIView
in the Toast
instance, so dismissing it is trivial - just remove the UIView
from its superview. With your current design, finding the currently displayed toast would be rather hard.