Is there a way to show an HStack
like a snack bar message which dismisses after (n) seconds using SwiftUI ?
I have the following struct which's a container to my message:
struct MessageBuilder<Content>: View where Content: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
content()
}
}
I call the MessageBuilder
struct like following:
MessageBuilder {
HStack {
Image("MyImage")
Text("Some Message")
}
}
I got two questions:
(1) how can I show it on top of screen and then auto-dismiss it (like SwiftMessages) ?
(2) how can I write a wrapper function that displays the message above any SwiftUI View
, for ex. in a Storyboard
based projects we would pass something like topViewController or rootViewController
in order to show a UIView
on top of it.
You can use ViewModifier
to create the banner view and then call it as modifier in your SwiftUI.
Using ZStack in your modifier to present the banner on top of your content.
Here is the simple example for create a banner and show on top of the view
in your view you can use .banner(data: $bannerData, show: $showBanner)
to present the banner
struct BannerData {
var title: String
var detail: String
var type: BannerType
}
enum BannerType {
case info
case warning
case success
case error
var tintColor: Color {
switch self {
case .info:
return Color(red: 67/255, green: 154/255, blue: 215/255)
case .success:
return Color.green
case .warning:
return Color.yellow
case .error:
return Color.red
}
}
}
struct BannerModifier: ViewModifier {
@Binding var data: BannerData
@Binding var show: Bool
@State var task: DispatchWorkItem?
func body(content: Content) -> some View {
ZStack {
if show {
VStack {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(data.title)
.bold()
Text(data.detail)
.font(Font.system(size: 15, weight: Font.Weight.light, design: Font.Design.default))
}
Spacer()
}
.foregroundColor(Color.white)
.padding(12)
.background(data.type.tintColor)
.cornerRadius(8)
.shadow(radius: 20)
Spacer()
}
.padding()
.animation(.easeInOut(duration: 1.2))
.transition(AnyTransition.move(edge: .top).combined(with: .opacity))
.onTapGesture {
withAnimation {
self.show = false
}
}.onAppear {
self.task = DispatchWorkItem {
withAnimation {
self.show = false
}
}
// Auto dismiss after 5 seconds, and cancel the task if view disappear before the auto dismiss
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: self.task!)
}
.onDisappear {
self.task?.cancel()
}
}
content
}
}
}
extension View {
func banner(data: Binding<BannerData>, show: Binding<Bool>) -> some View {
self.modifier(BannerModifier(data: data, show: show))
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With