Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support constraintsBlock for showToast method #206

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ extension ViewController {
if section == 0 {
return 2
} else {
return 11
return 12
}
}

Expand Down Expand Up @@ -149,6 +149,7 @@ extension ViewController {
case 8: cell.textLabel?.text = showingActivity ? "Hide toast activity" : "Show toast activity"
case 9: cell.textLabel?.text = "Hide toast"
case 10: cell.textLabel?.text = "Hide all toasts"
case 11: cell.textLabel?.text = "Make toast with autolayout"
default: cell.textLabel?.text = nil
}

Expand Down Expand Up @@ -219,8 +220,48 @@ extension ViewController {
case 10:
// Hide all toasts
self.navigationController?.view.hideAllToasts()

case 11:
self.navigationController?.view.showToast(CustomToast(), constraints: { container, toast in
toast.translatesAutoresizingMaskIntoConstraints = false
toast.leftAnchor.constraint(equalTo: container.leftAnchor, constant: 20).isActive = true
toast.rightAnchor.constraint(equalTo: container.rightAnchor, constant: -20).isActive = true
toast.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -40).isActive = true
container.layoutIfNeeded()
})
default:
break
}
}
}


class CustomToast: UIView {

let titleLabel = {
let lb = UILabel()
lb.text = "Hello World"
return lb
}()

override var intrinsicContentSize: CGSize {
return .init(width: UIView.noIntrinsicMetric, height: 60)
}

override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

private func setup() {
self.backgroundColor = .red
self.addSubview(self.titleLabel)
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
}
73 changes: 69 additions & 4 deletions Toast/Toast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import ObjectiveC

/**
Toast is a Swift extension that adds toast notifications to the `UIView` object class.
It is intended to be simple, lightweight, and easy to use. Most toast notifications
It is intended to be simple, lightweight, and easy to use. Most toast notifications
can be triggered with a single line of code.

The `makeToast` methods create a new view and then display it as toast.
Expand All @@ -46,6 +46,7 @@ public extension UIView {
static var duration = "com.toast-swift.duration"
static var point = "com.toast-swift.point"
static var completion = "com.toast-swift.completion"
static var constraints = "com.toast-swift.constraints"
static var activeToasts = "com.toast-swift.activeToasts"
static var activityView = "com.toast-swift.activityView"
static var queue = "com.toast-swift.queue"
Expand All @@ -64,6 +65,14 @@ public extension UIView {
}
}

private class ToastConstraintsWrapper {
let constraints: ((_ container: UIView, _ toast: UIView) -> Void)?

init(_ constraints: ((_ container: UIView, _ toast: UIView) -> Void)?) {
self.constraints = constraints
}
}

private enum ToastError: Error {
case missingParameters
}
Expand Down Expand Up @@ -177,6 +186,31 @@ public extension UIView {
showToast(toast, duration: duration, point: point)
}
}

/**
Displays any view as toast at a provided constraintsBlock and duration. The completion closure
executes when the toast view completes. `didTap` will be `true` if the toast view was
dismissed from a tap.

@param toast The view to be displayed as toast
@param duration The notification duration
@param constarints The constraints block, executed after the toast view added
@param completion The completion block, executed after the toast view disappears.
didTap will be `true` if the toast view was dismissed from a tap.
*/
func showToast(_ toast: UIView, duration: TimeInterval = ToastManager.shared.duration, constraints: @escaping (_ container: UIView, _ toast: UIView) -> Void, completion: ((_ didTap: Bool) -> Void)? = nil) {

objc_setAssociatedObject(toast, &ToastKeys.completion, ToastCompletionWrapper(completion), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

if ToastManager.shared.isQueueEnabled, activeToasts.count > 0 {
objc_setAssociatedObject(toast, &ToastKeys.duration, NSNumber(value: duration), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(toast, &ToastKeys.constraints, ToastConstraintsWrapper(constraints), .OBJC_ASSOCIATION_RETAIN_NONATOMIC);

queue.add(toast)
} else {
showToast(toast, duration: duration, constraints: constraints)
}
}

// MARK: - Hide Toast Methods

Expand Down Expand Up @@ -334,20 +368,44 @@ public extension UIView {

// MARK: - Private Show/Hide Methods


private func showToast(_ toast: UIView, duration: TimeInterval, constraints: (_ container: UIView, _ toast: UIView) -> Void) {
toast.alpha = 0.0

if ToastManager.shared.isTapToDismissEnabled {
let recognizer = UITapGestureRecognizer(target: self, action: #selector(UIView.handleToastTapped(_:)))
toast.addGestureRecognizer(recognizer)
toast.isUserInteractionEnabled = true
toast.isExclusiveTouch = true
}
activeToasts.add(toast)
self.addSubview(toast)

constraints(self, toast)

UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: {
toast.alpha = 1.0
}) { _ in
let timer = Timer(timeInterval: duration, target: self, selector: #selector(UIView.toastTimerDidFinish(_:)), userInfo: toast, repeats: false)
RunLoop.main.add(timer, forMode: .common)
objc_setAssociatedObject(toast, &ToastKeys.timer, timer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}

private func showToast(_ toast: UIView, duration: TimeInterval, point: CGPoint) {
toast.center = point
toast.alpha = 0.0

if ToastManager.shared.isTapToDismissEnabled {
let recognizer = UITapGestureRecognizer(target: self, action: #selector(UIView.handleToastTapped(_:)))
toast.addGestureRecognizer(recognizer)
toast.isUserInteractionEnabled = true
toast.isExclusiveTouch = true
}

activeToasts.add(toast)
self.addSubview(toast)

UIView.animate(withDuration: ToastManager.shared.style.fadeDuration, delay: 0.0, options: [.curveEaseOut, .allowUserInteraction], animations: {
toast.alpha = 1.0
}) { _ in
Expand Down Expand Up @@ -375,6 +433,13 @@ public extension UIView {
if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.duration) as? NSNumber, let point = objc_getAssociatedObject(nextToast, &ToastKeys.point) as? NSValue {
self.queue.removeObject(at: 0)
self.showToast(nextToast, duration: duration.doubleValue, point: point.cgPointValue)
return
}

if let nextToast = self.queue.firstObject as? UIView, let duration = objc_getAssociatedObject(nextToast, &ToastKeys.duration) as? NSNumber, let wrapper = objc_getAssociatedObject(toast, &ToastKeys.constraints) as? ToastConstraintsWrapper, let constraints = wrapper.constraints {
self.queue.removeObject(at: 0)
self.showToast(nextToast, duration: duration.doubleValue, constraints: constraints)
return
}
}
}
Expand Down