Skip to content

Commit

Permalink
feat: SwiftUI view modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeWeidmann committed Jan 20, 2025
1 parent eee39db commit f6c08d1
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 3 deletions.
90 changes: 90 additions & 0 deletions Sources/NotificationToast/ToastView+SwiftUI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//
// ToastView+SwiftUI.swift
// NotificationToast
//
// Created by Philippe Weidmann on 20.01.2025.
//

import Foundation
import SwiftUI

@available(iOS 14.0, *)
public extension View {
func toast(
isPresented: Binding<Bool>,
title: String,
titleFont: UIFont = .systemFont(ofSize: 13, weight: .regular),
subtitle: String? = nil,
subtitleFont: UIFont = .systemFont(ofSize: 11, weight: .light),
icon: UIImage? = nil,
iconSpacing: CGFloat = 16,
position: ToastView.Position = .top,
haptic: UINotificationFeedbackGenerator.FeedbackType? = nil,
onTap: (() -> Void)? = nil
) -> some View {
modifier(
ToastViewModifier(
isPresented: isPresented,
title: title,
titleFont: titleFont,
subtitle: subtitle,
subtitleFont: subtitleFont,
icon: icon,
iconSpacing: iconSpacing,
position: position,
haptic: haptic,
onTap: onTap
)
)
}
}

@available(iOS 14.0, *)
struct ToastViewModifier: ViewModifier {
@State private var toastView: ToastView?
@Binding var isPresented: Bool

let title: String
var titleFont: UIFont = .systemFont(ofSize: 13, weight: .regular)
let subtitle: String?
var subtitleFont: UIFont = .systemFont(ofSize: 11, weight: .light)
let icon: UIImage?
var iconSpacing: CGFloat = 16
var position: ToastView.Position = .top
var haptic: UINotificationFeedbackGenerator.FeedbackType? = nil
var onTap: (() -> Void)? = nil

func body(content: Content) -> some View {
content
.onChange(of: isPresented) { isPresented in
if isPresented {
let toastView = ToastView(
title: title,
titleFont: titleFont,
subtitle: subtitle,
subtitleFont: subtitleFont,
icon: icon,
iconSpacing: iconSpacing,
position: position,
onTap: onTap
)
self.toastView = toastView
toastView.show(haptic: haptic) {
self.isPresented = false
}
} else {
toastView?.hide()
}
}
}
}

@available(iOS 17.0, *)
#Preview {
@Previewable @State var isPresented = false

Button("Present toast") {
isPresented = true
}
.toast(isPresented: $isPresented, title: "AirPods Pro", subtitle: "Connected", icon: UIImage(systemName: "airpodspro"))
}
7 changes: 4 additions & 3 deletions Sources/NotificationToast/ToastView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public class ToastView: UIView {
layer.cornerRadius = bounds.height / 2
}

public func show(originWindowScene: UIWindowScene? = nil, haptic: UINotificationFeedbackGenerator.FeedbackType? = nil) {
public func show(originWindowScene: UIWindowScene? = nil, haptic: UINotificationFeedbackGenerator.FeedbackType? = nil, onHide: (() -> Void)? = nil) {
if let haptic {
UINotificationFeedbackGenerator().notificationOccurred(haptic)
}
Expand All @@ -196,18 +196,19 @@ public class ToastView: UIView {
self.transform = .identity
}) { [self] _ in
if autoHide {
hide(after: displayTime)
hide(after: displayTime, onHide: onHide)
}
}
}

public func hide(after time: TimeInterval = 0.0) {
public func hide(after time: TimeInterval = 0.0, onHide: (() -> Void)? = nil) {
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
UIView.animate(withDuration: self.hideAnimationDuration, delay: 0, options: .curveEaseIn, animations: { [self] in
transform = initialTransform
}) { [self] _ in
removeFromSuperview()
overlayWindow = nil
onHide?()
}
}
}
Expand Down

0 comments on commit f6c08d1

Please sign in to comment.