Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

show snack bar message in SwiftUI

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.

like image 322
JAHelia Avatar asked Feb 06 '20 05:02

JAHelia


Video Answer


1 Answers

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))
    }
}
like image 107
Mac3n Avatar answered Nov 28 '22 08:11

Mac3n