Skip to content

Commit

Permalink
Merge pull request #43 from BastiaanJansen/dev
Browse files Browse the repository at this point in the history
v1.6.0
  • Loading branch information
BastiaanJansen authored Sep 30, 2023
2 parents c153709 + 99bda6b commit 79acd07
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 98 deletions.
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ A Swift Toast view - iOS 14 style - built with UIKit. 🍞
You can use The Swift Package Manager to install Toast-Swift by adding the description to your Package.swift file:
```swift
dependencies: [
.package(url: "https://github.com/BastiaanJansen/toast-swift", from: "1.5.0")
.package(url: "https://github.com/BastiaanJansen/toast-swift", from: "1.6.0")
]
```

Expand Down Expand Up @@ -78,9 +78,7 @@ The `text`, `default` and `custom` methods support custom configuration options.
| Name | Description | Type | Default |
|-----------------|-----------------------------------------------------------------------------------------------------|----------------|---------|
| `direction` | Where the toast will be shown. | `.bottom` or `.up` | `.up` |
| `autoHide` | When set to true, the toast will automatically close itself after display time has elapsed. | `Bool` | `true` |
| `enablePanToClose` | When set to true, the toast will be able to close by swiping up. | `Bool` | `true` |
| `displayTime` | The duration the toast will be displayed before it will close when autoHide set to true in seconds. | `TimeInterval` | `4` |
| `dismissBy` | Choose when the toast dismisses. | `Dismissable` | [`.time`, `.swipe`] |
| `animationTime` | Duration of the show and close animation in seconds. | `TimeInterval` | `0.2` |
| `enteringAnimation` | The type of animation that will be used when toast is showing | `.slide`, `.fade`, `.scaleAndSlide`, `.scale` and `.custom` | `.default`|
| `exitingAnimation` | The type of animation that will be used when toast is exiting | `.slide`, `.fade`, `.scaleAndSlide`, `.scale` and `.custom` | `.default`|
Expand All @@ -90,9 +88,7 @@ The `text`, `default` and `custom` methods support custom configuration options.
```swift
let config = ToastConfiguration(
direction: .top,
autoHide: true,
enablePanToClose: true,
displayTime: 5,
dismissBy: [.time(time: 4.0), .swipe(direction: .natural), .longPress],
animationTime: 0.2
)

Expand Down
2 changes: 1 addition & 1 deletion Sources/Toast/AnimationType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension Toast {
/// Use this type for fading in/out animations.
///
/// alphaValue must be greater or equal to 0 and less or equal to 1.
case fade(alphaValue: CGFloat)
case fade(alpha: CGFloat)

/// Use this type for scaling and slide in/out animations.
case scaleAndSlide(scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat)
Expand Down
22 changes: 22 additions & 0 deletions Sources/Toast/Direction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,26 @@ extension Toast {
case top, bottom
}

public enum DismissSwipeDirection: Equatable {
case toTop,
toBottom,
natural

func shouldApply(_ delta: CGFloat, direction: Direction) -> Bool {
switch self {
case .toTop:
return delta <= 0
case .toBottom:
return delta >= 0
case .natural:
switch direction {
case .top:
return delta <= 0
case .bottom:
return delta >= 0
}
}
}
}

}
18 changes: 9 additions & 9 deletions Sources/Toast/Queue/ToastQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,21 @@ public class ToastQueue {
}

public func show() -> Void {
if (queue.isEmpty) {
return
}

show(index: 0)
}

private func show(index: Int) -> Void {
private func show(index: Int, after: Double = 0.0) -> Void {
if queue.isEmpty {
return
}

let toast: Toast = queue.remove(at: index)
let delegate = QueuedToastDelegate(queue: self)

multicast.invoke { $0.willShowAnyToast(toast) }
multicast.invoke { $0.willShowAnyToast(toast, queuedToasts: queue) }

toast.addDelegate(delegate: delegate)
toast.show()
toast.show(after: after)
}


Expand All @@ -65,8 +65,8 @@ public class ToastQueue {
}

public func didCloseToast(_ toast: Toast) {
queue.multicast.invoke { $0.didShowAnyToast(toast) }
queue.show()
queue.multicast.invoke { $0.didShowAnyToast(toast, queuedToasts: queue.queue) }
queue.show(index: 0, after: 0.5)
}

}
Expand Down
8 changes: 4 additions & 4 deletions Sources/Toast/Queue/ToastQueueDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import Foundation

public protocol ToastQueueDelegate: AnyObject {

func willShowAnyToast(_ toast: Toast) -> Void
func willShowAnyToast(_ toast: Toast, queuedToasts: [Toast]) -> Void

func didShowAnyToast(_ toast: Toast) -> Void
func didShowAnyToast(_ toast: Toast, queuedToasts: [Toast]) -> Void

}

extension ToastQueueDelegate {

public func willShowAnyToast(toast: Toast) {}
public func willShowAnyToast(toast: Toast, queuedToasts: [Toast]) {}

public func didShowAnyToast(toast: Toast) {}
public func didShowAnyToast(toast: Toast, queuedToasts: [Toast]) {}

}
116 changes: 53 additions & 63 deletions Sources/Toast/Toast.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ public class Toast {

private var multicast = MulticastDelegate<ToastDelegate>()

private let config: ToastConfiguration

private(set) var direction: Direction
private(set) var config: ToastConfiguration

/// Creates a new Toast with the default Apple style layout with a title and an optional subtitle.
/// - Parameters:
Expand Down Expand Up @@ -129,10 +127,18 @@ public class Toast {
public required init(view: ToastView, config: ToastConfiguration) {
self.config = config
self.view = view
self.direction = config.direction

if config.enablePanToClose {
enablePanToClose()

for dismissable in config.dismissables {
switch dismissable {
case .tap:
enableTapToClose()
case .longPress:
enableLongPressToClose()
case .swipe:
enablePanToClose()
default:
break
}
}
}

Expand All @@ -150,7 +156,7 @@ public class Toast {
/// Show the toast
/// - Parameter delay: Time after which the toast is shown
public func show(after delay: TimeInterval = 0) {
config.view?.addSubview(view) ?? topController()?.view.addSubview(view)
config.view?.addSubview(view) ?? ToastHelper.topController()?.view.addSubview(view)
view.createView(for: self)

multicast.invoke { $0.willShowToast(self) }
Expand All @@ -160,11 +166,8 @@ public class Toast {
self.config.enteringAnimation.undo(from: self.view)
} completion: { [self] _ in
multicast.invoke { $0.didShowToast(self) }
closeTimer = Timer.scheduledTimer(withTimeInterval: .init(config.displayTime), repeats: false) { [self] _ in
if config.autoHide {
close()
}
}

configureCloseTimer()
}
}

Expand All @@ -190,36 +193,6 @@ public class Toast {
multicast.add(delegate)
}

private func topController() -> UIViewController? {
if var topController = keyWindow()?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
return nil
}

private func keyWindow() -> UIWindow? {
if #available(iOS 13.0, *) {
for scene in UIApplication.shared.connectedScenes {
guard let windowScene = scene as? UIWindowScene else {
continue
}
if windowScene.windows.isEmpty {
continue
}
guard let window = windowScene.windows.first(where: { $0.isKeyWindow }) else {
continue
}
return window
}
return nil
} else {
return UIApplication.shared.windows.first(where: { $0.isKeyWindow })
}
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Expand All @@ -232,27 +205,28 @@ public extension Toast {
}

@objc private func toastOnPan(_ gesture: UIPanGestureRecognizer) {
guard let topVc = topController() else {
guard let topVc = ToastHelper.topController() else {
return
}

switch gesture.state{
switch gesture.state {
case .began:
startY = self.view.frame.origin.y
startShiftY = gesture.location(in: topVc.view).y
closeTimer?.invalidate()
case .changed:
let delta = gesture.location(in: topVc.view).y - startShiftY
switch direction {
case .top:
if delta <= 0 {
self.view.frame.origin.y = startY + delta
}
case .bottom:
if delta >= 0 {
self.view.frame.origin.y = startY + delta

for dismissable in config.dismissables {
if case .swipe(let dismissSwipeDirection) = dismissable {
let shouldApply = dismissSwipeDirection.shouldApply(delta, direction: config.direction)
if shouldApply {
self.view.frame.origin.y = startY + delta
}
}
}

case .ended:
let threshold = 15.0 // if user drags more than threshold the toast will be dismissed
let ammountOfUserDragged = abs(startY - self.view.frame.origin.y)
Expand All @@ -264,20 +238,12 @@ public extension Toast {
UIView.animate(withDuration: config.animationTime, delay: 0, options: [.curveEaseOut, .allowUserInteraction]) {
self.view.frame.origin.y = self.startY
} completion: { [self] _ in
closeTimer = Timer.scheduledTimer(withTimeInterval: .init(config.displayTime), repeats: false) { [self] _ in
if config.autoHide {
close()
}
}
configureCloseTimer()
}
}

case .cancelled, .failed:
closeTimer = Timer.scheduledTimer(withTimeInterval: .init(config.displayTime), repeats: false) { [self] _ in
if config.autoHide {
close()
}
}
configureCloseTimer()
default:
break
}
Expand All @@ -288,8 +254,32 @@ public extension Toast {
self.view.addGestureRecognizer(tap)
}

func enableLongPressToClose() {
let tap = UILongPressGestureRecognizer(target: self, action: #selector(toastOnTap))
self.view.addGestureRecognizer(tap)
}

@objc func toastOnTap(_ gesture: UITapGestureRecognizer) {
closeTimer?.invalidate()
close()
}

private func configureCloseTimer() {
for dismissable in config.dismissables {
if case .time(let displayTime) = dismissable {
closeTimer = Timer.scheduledTimer(withTimeInterval: .init(displayTime), repeats: false) { [self] _ in
close()
}
}
}
}
}

extension Toast {
public enum Dismissable: Equatable {
case tap,
longPress,
time(time: TimeInterval),
swipe(direction: DismissSwipeDirection)
}
}
16 changes: 4 additions & 12 deletions Sources/Toast/ToastConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import UIKit

public struct ToastConfiguration {
public let direction: Toast.Direction
public let autoHide: Bool
public let enablePanToClose: Bool
public let displayTime: TimeInterval
public let dismissables: [Toast.Dismissable]
public let animationTime: TimeInterval
public let enteringAnimation: Toast.AnimationType
public let exitingAnimation: Toast.AnimationType
Expand All @@ -22,27 +20,21 @@ public struct ToastConfiguration {
/// Creates a new Toast configuration object.
/// - Parameters:
/// - direction: The position the toast will be displayed.
/// - autoHide: When set to true, the toast will automatically close itself after display time has elapsed.
/// - enablePanToClose: When set to true, the toast will be able to close by swiping up.
/// - displayTime: The duration the toast will be displayed before it will close when autoHide set to true.
/// - dismissBy: Choose when the toast dismisses.
/// - animationTime:Duration of the animation
/// - enteringAnimation: The entering animation of the toast.
/// - exitingAnimation: The exiting animation of the toast.
/// - attachTo: The view on which the toast view will be attached.
public init(
direction: Toast.Direction = .top,
autoHide: Bool = true,
enablePanToClose: Bool = true,
displayTime: TimeInterval = 4,
dismissBy: [Toast.Dismissable] = [.time(time: 4.0), .swipe(direction: .natural)],
animationTime: TimeInterval = 0.2,
enteringAnimation: Toast.AnimationType = .default,
exitingAnimation: Toast.AnimationType = .default,
attachTo view: UIView? = nil
) {
self.direction = direction
self.autoHide = autoHide
self.enablePanToClose = enablePanToClose
self.displayTime = displayTime
self.dismissables = dismissBy
self.animationTime = animationTime
self.enteringAnimation = enteringAnimation.isDefault ? Self.defaultEnteringAnimation(with: direction) : enteringAnimation
self.exitingAnimation = exitingAnimation.isDefault ? Self.defaultExitingAnimation(with: direction) : exitingAnimation
Expand Down
Loading

0 comments on commit 79acd07

Please sign in to comment.