Toast.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import UIKit
  2. public class Delay: NSObject {
  3. @available(*, unavailable) private override init() {}
  4. // `short` and `long` (lowercase) are reserved words in Objective-C
  5. // so we capitalize them instead of the default `short_` and `long_`
  6. @objc(Short) public static let short: TimeInterval = 2.0
  7. @objc(Long) public static let long: TimeInterval = 3.5
  8. }
  9. open class Toast: Operation {
  10. // MARK: Properties
  11. @objc public var text: String? {
  12. get { return self.view.text }
  13. set { self.view.text = newValue }
  14. }
  15. @objc public var attributedText: NSAttributedString? {
  16. get { return self.view.attributedText }
  17. set { self.view.attributedText = newValue }
  18. }
  19. @objc public var delay: TimeInterval
  20. @objc public var duration: TimeInterval
  21. private var _executing = false
  22. override open var isExecuting: Bool {
  23. get {
  24. return self._executing
  25. }
  26. set {
  27. self.willChangeValue(forKey: "isExecuting")
  28. self._executing = newValue
  29. self.didChangeValue(forKey: "isExecuting")
  30. }
  31. }
  32. private var _finished = false
  33. override open var isFinished: Bool {
  34. get {
  35. return self._finished
  36. }
  37. set {
  38. self.willChangeValue(forKey: "isFinished")
  39. self._finished = newValue
  40. self.didChangeValue(forKey: "isFinished")
  41. }
  42. }
  43. // MARK: UI
  44. @objc public var view: ToastView = ToastView()
  45. // MARK: Initializing
  46. /// Initializer.
  47. /// Instantiates `self.view`, so must be called on main thread.
  48. @objc public init(text: String?, delay: TimeInterval = 0, duration: TimeInterval = Delay.short) {
  49. self.delay = delay
  50. self.duration = duration
  51. super.init()
  52. self.text = text
  53. }
  54. @objc public init(attributedText: NSAttributedString?, delay: TimeInterval = 0, duration: TimeInterval = Delay.short) {
  55. self.delay = delay
  56. self.duration = duration
  57. super.init()
  58. self.attributedText = attributedText
  59. }
  60. // MARK: Factory (Deprecated)
  61. @available(*, deprecated, message: "Use 'init(text:)' instead.")
  62. public class func makeText(_ text: String) -> Toast {
  63. return Toast(text: text)
  64. }
  65. @available(*, deprecated, message: "Use 'init(text:duration:)' instead.")
  66. public class func makeText(_ text: String, duration: TimeInterval) -> Toast {
  67. return Toast(text: text, duration: duration)
  68. }
  69. @available(*, deprecated, message: "Use 'init(text:delay:duration:)' instead.")
  70. public class func makeText(_ text: String?, delay: TimeInterval, duration: TimeInterval) -> Toast {
  71. return Toast(text: text, delay: delay, duration: duration)
  72. }
  73. // MARK: Showing
  74. @objc public func show() {
  75. ToastCenter.default.add(self)
  76. }
  77. // MARK: Cancelling
  78. open override func cancel() {
  79. super.cancel()
  80. self.finish()
  81. self.view.removeFromSuperview()
  82. }
  83. // MARK: Operation Subclassing
  84. override open func start() {
  85. let isRunnable = !self.isFinished && !self.isCancelled && !self.isExecuting
  86. guard isRunnable else { return }
  87. guard Thread.isMainThread else {
  88. DispatchQueue.main.async { [weak self] in
  89. self?.start()
  90. }
  91. return
  92. }
  93. main()
  94. }
  95. override open func main() {
  96. self.isExecuting = true
  97. DispatchQueue.main.async {
  98. self.view.setNeedsLayout()
  99. self.view.alpha = 0
  100. ToastWindow.shared.addSubview(self.view)
  101. UIView.animate(
  102. withDuration: 0.5,
  103. delay: self.delay,
  104. options: .beginFromCurrentState,
  105. animations: {
  106. self.view.alpha = 1
  107. },
  108. completion: { completed in
  109. if ToastCenter.default.isSupportAccessibility {
  110. #if swift(>=4.2)
  111. UIAccessibility.post(notification: .announcement, argument: self.view.text)
  112. #else
  113. UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, self.view.text)
  114. #endif
  115. }
  116. UIView.animate(
  117. withDuration: self.duration,
  118. animations: {
  119. self.view.alpha = 1.0001
  120. },
  121. completion: { completed in
  122. self.finish()
  123. UIView.animate(
  124. withDuration: 0.5,
  125. animations: {
  126. self.view.alpha = 0
  127. },
  128. completion: { completed in
  129. self.view.removeFromSuperview()
  130. }
  131. )
  132. }
  133. )
  134. }
  135. )
  136. }
  137. }
  138. func finish() {
  139. self.isExecuting = false
  140. self.isFinished = true
  141. }
  142. }