diff --git a/Sources/AlertToast/AlertToast.swift b/Sources/AlertToast/AlertToast.swift index 17bf751..99b99af 100644 --- a/Sources/AlertToast/AlertToast.swift +++ b/Sources/AlertToast/AlertToast.swift @@ -89,7 +89,7 @@ fileprivate struct AnimatedXmark: View { //MARK: - Main View @available(iOS 14, macOS 11, *) -public struct AlertToast: View{ +public struct AlertToast: View{ public enum BannerAnimation{ case slide, pop @@ -106,6 +106,9 @@ public struct AlertToast: View{ ///Banner from the bottom of the view case banner(_ transition: BannerAnimation) + + /// custom View + case custom } /// Determine what the alert will display @@ -207,6 +210,9 @@ public struct AlertToast: View{ ///Customize your alert appearance public var style: AlertStyle? = nil + ///custom View + public var customView : (() -> CustomView)? + ///Full init public init(displayMode: DisplayMode = .alert, type: AlertType, @@ -231,6 +237,13 @@ public struct AlertToast: View{ self.title = title } + ///Short init with Custome View + public init(@ViewBuilder customView: @escaping () -> CustomView,style: AlertStyle? = nil){ + self.type = .regular //default, no any meain + self.displayMode = .custom + self.customView = customView + self.style = style + } ///Banner from the bottom of the view public var banner: some View{ VStack{ @@ -393,6 +406,19 @@ public struct AlertToast: View{ .alertBackground(style?.backgroundColor ?? nil) .cornerRadius(10) } + public var custom: some View{ + ZStack{ + if customView != nil { + customView!() + }else{ + EmptyView() + } + } + .padding() + .alertBackground(style?.backgroundColor ?? nil) + .cornerRadius(10) + } + ///Body init determine by `displayMode` public var body: some View{ @@ -403,12 +429,14 @@ public struct AlertToast: View{ hud case .banner: banner + case .custom: + custom } } } @available(iOS 14, macOS 11, *) -public struct AlertToastModifier: ViewModifier{ +public struct AlertToastModifier: ViewModifier{ ///Presentation `Binding` @Binding var isPresenting: Bool @@ -422,7 +450,7 @@ public struct AlertToastModifier: ViewModifier{ var offsetY: CGFloat = 0 ///Init `AlertToast` View - var alert: () -> AlertToast + var alert: () -> AlertToast ///Completion block returns `true` after dismiss var onTap: (() -> ())? = nil @@ -512,6 +540,22 @@ public struct AlertToastModifier: ViewModifier{ completion?() }) .transition(alert().displayMode == .banner(.slide) ? AnyTransition.slide.combined(with: .opacity) : AnyTransition.move(edge: .bottom)) + case .custom: + alert() + .onTapGesture { + onTap?() + if tapToDismiss{ + withAnimation(Animation.spring()){ + self.workItem?.cancel() + isPresenting = false + self.workItem = nil + } + } + } + .onDisappear(perform: { + completion?() + }) + .transition(AnyTransition.scale(scale: 0.8).combined(with: .opacity)) } } @@ -531,6 +575,8 @@ public struct AlertToastModifier: ViewModifier{ .valueChanged(value: isPresenting, onChange: { (presented) in if presented{ onAppearAction() + }else{ + onDisAppearAction() } }) case .hud: @@ -558,6 +604,8 @@ public struct AlertToastModifier: ViewModifier{ .valueChanged(value: isPresenting, onChange: { (presented) in if presented{ onAppearAction() + }else{ + onDisAppearAction() } }) case .alert: @@ -572,6 +620,24 @@ public struct AlertToastModifier: ViewModifier{ .valueChanged(value: isPresenting, onChange: { (presented) in if presented{ onAppearAction() + }else{ + onDisAppearAction() + } + }) + case .custom: + content + .overlay(ZStack{ + main() + .offset(y: offsetY) + } + .frame(maxWidth: screen.width, maxHeight: screen.height, alignment: .center) + .edgesIgnoringSafeArea(.all) + .animation(Animation.spring(), value: isPresenting)) + .valueChanged(value: isPresenting, onChange: { (presented) in + if presented{ + onAppearAction() + }else{ + onDisAppearAction() } }) } @@ -601,6 +667,14 @@ public struct AlertToastModifier: ViewModifier{ DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: task) } } + /// Cancel timer when interface leaves + private func onDisAppearAction(){ + if workItem == nil { + return + } + workItem?.cancel() + workItem = nil + } } ///Fileprivate View Modifier for dynamic frame when alert type is `.regular` / `.loading` @@ -686,7 +760,7 @@ public extension View{ /// - show: Binding /// - alert: () -> AlertToast /// - Returns: `AlertToast` - func toast(isPresenting: Binding, duration: TimeInterval = 2, tapToDismiss: Bool = true, offsetY: CGFloat = 0, alert: @escaping () -> AlertToast, onTap: (() -> ())? = nil, completion: (() -> ())? = nil) -> some View{ + func toast(isPresenting: Binding, duration: Double = 2, tapToDismiss: Bool = true, offsetY: CGFloat = 0, alert: @escaping () -> AlertToast, onTap: (() -> ())? = nil, completion: (() -> ())? = nil) -> some View{ modifier(AlertToastModifier(isPresenting: isPresenting, duration: duration, tapToDismiss: tapToDismiss, offsetY: offsetY, alert: alert, onTap: onTap, completion: completion)) } @@ -695,7 +769,7 @@ public extension View{ /// - item: Binding /// - alert: (Item?) -> AlertToast /// - Returns: `AlertToast` - func toast(item: Binding, duration: Double = 2, tapToDismiss: Bool = true, offsetY: CGFloat = 0, alert: @escaping (Item?) -> AlertToast, onTap: (() -> ())? = nil, completion: (() -> ())? = nil) -> some View where Item : Identifiable { + func toast(item: Binding, duration: Double = 2, tapToDismiss: Bool = true, offsetY: CGFloat = 0, alert: @escaping (Item?) -> AlertToast, onTap: (() -> ())? = nil, completion: (() -> ())? = nil) -> some View where Item : Identifiable { modifier( AlertToastModifier( isPresenting: Binding(