Skip to content
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
24 changes: 24 additions & 0 deletions Sources/ToastSwift/Extension/UIColor+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// UIColor+Extension.swift
// ToastSwift
//
// Created by Jayvee on 7/11/25.
//

import UIKit

public extension UIColor {
static func colorWithHexString(_ hex: String) -> UIColor {
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines)
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")

var rgb: UInt64 = 0
Scanner(string: hexSanitized).scanHexInt64(&rgb)

let r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
let g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
let b = CGFloat(rgb & 0x0000FF) / 255.0

return UIColor(red: r, green: g, blue: b, alpha: 1.0)
}
}
157 changes: 69 additions & 88 deletions Sources/ToastSwift/ToastSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,10 @@ public class Delay: NSObject {
@objc(Long) public static let long: TimeInterval = 3.5
}

open class ToastSwift: Operation {

// MARK: Properties

@MainActor
@objc public var text: String? {
get { return self.view.text }
set { return self.view.text = newValue }
}

@MainActor
@objc public var attributedText: NSAttributedString? {
get { return self.view.attributedText }
set { return self.view.attributedText = newValue }
}

@MainActor
public var hideActionButton: Bool {
get { return self.view.hideActionButton ?? true }
set { return self.view.hideActionButton = newValue }
}

@objc public var delay: TimeInterval = 0
@objc public var duration: TimeInterval = 2.0
@objc public var action: (() -> Void)?
open class ToastSwift: Operation, @unchecked Sendable {

//MARK: - Properties

private var _executing = false
override public var isExecuting: Bool {
get { return _executing }
Expand All @@ -55,33 +33,20 @@ open class ToastSwift: Operation {
}
}

// MARK: User Interface
//MARK: - User Interface

@MainActor
@objc public var view: ToastView = ToastView()


// MARK: Initializing

@MainActor
@objc public init(text: String? = nil, backgroundColor: UIColor = UIColor(red: 0.24, green: 0.24, blue: 0.24, alpha: 1.00), willHideActionButton: Bool = true, delay: TimeInterval = 0, duration: TimeInterval = Delay.short) {
self.delay = delay
self.duration = duration
self.view.backgroundColor = backgroundColor
super.init()
self.text = text
}

// MARK: - Initialization

@MainActor
@objc public init(attributedText: NSAttributedString?, backgroundColor: UIColor = UIColor(red: 0.24, green: 0.24, blue: 0.24, alpha: 1.00), willHideActionButton: Bool = true, delay: TimeInterval = 0, duration: TimeInterval = Delay.short) {
self.delay = delay
self.duration = duration
self.view.backgroundColor = backgroundColor
@objc public init(with attributes: ToastAttributes, onButtonTap buttonAction: (() -> Void)? = nil) {
super.init()
self.attributedText = attributedText
view.bind(with: attributes)
}

// MARK: Actions
//MARK: - Actions

@MainActor @objc public func show() {
ToastManager.default.add(self)
Expand All @@ -99,64 +64,80 @@ open class ToastSwift: Operation {

override open func start() {
let isRunnable = !self.isFinished && !self.isCancelled && !self.isExecuting

guard isRunnable else { return }
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in
self?.start()
guard let self = self else { return }
self.start()
}

return
}

main()
}

override open func main() {
self.isExecuting = true

DispatchQueue.main.async {
self.view.setNeedsLayout()
self.view.alpha = 0
ToastWindow.shared.addSubview(self.view)

UIView.animate(
withDuration: 0.5,
delay: self.delay,
options: .beginFromCurrentState,
animations: {
self.view.alpha = 1
},
completion: { completed in
if ToastManager.default.isSupportAccessibility {
#if swift(>=4.2)
UIAccessibility.post(notification: .announcement, argument: self.view.text)
#else
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.view.text)
#endif
}
self.isExecuting = true

DispatchQueue.main.async { [weak self] in
guard let self = self else { return }

self.view.setNeedsLayout()
self.view.alpha = 0
ToastWindow.shared.addSubview(self.view)

UIView.animate(
withDuration: self.duration,
animations: {
self.view.alpha = 1.0001
},
completion: { completed in
self.finish()
UIView.animate(
withDuration: 0.5,
animations: {
self.view.alpha = 0
},
completion: { completed in
self.view.removeFromSuperview()
}
)
}
withDuration: 0.5,
delay: self.view.attributes.delay,
options: .beginFromCurrentState,
animations: {
self.view.alpha = 1
},
completion: { completed in
if ToastManager.default.isSupportAccessibility {
var message: String = ""

if self.view.attributes.shouldUseAttributedText {
if let val = self.view.attributes.messageLabelAttributedText?.string {
message = val
}
} else {
message = self.view.attributes.messageLabelText
}

#if swift(>=4.2)
UIAccessibility.post(notification: .announcement, argument: message)
#else
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, message)
#endif
}
UIView.animate(
withDuration: self.view.attributes.duration,
animations: {
self.view.alpha = 1.0001
},
completion: { completed in
self.finish()
UIView.animate(
withDuration: 0.5,
animations: {
self.view.alpha = 0
},
completion: { completed in
self.view.removeFromSuperview()
}
)
}
)
}
)
}
)
}
}
}

func finish() {
self.isExecuting = false
self.isFinished = true
self.isExecuting = false
self.isFinished = true
}
}
Loading
Loading