// // BaseView.swift // SodaLive // // Created by klaus on 2023/08/09. // import SwiftUI struct BaseView: View { let content: Content @Binding var isLoading: Bool init(isLoading: Binding = .constant(false), @ViewBuilder content: () -> Content) { self._isLoading = isLoading self.content = content() } var body: some View { ZStack { Color.black.ignoresSafeArea() content if isLoading { LoadingView() } } } } struct BaseView_Previews: PreviewProvider { static var previews: some View { BaseView(isLoading: .constant(false)) {} } } private struct SodaToastModifier: ViewModifier { @Binding var isPresented: Bool let message: String let autohideIn: Double private let toastBackgroundColor = Color( red: 59.0 / 255.0, green: 185.0 / 255.0, blue: 241.0 / 255.0, opacity: 0.92 ) @State private var dismissWorkItem: DispatchWorkItem? func body(content: Content) -> some View { content .overlay(alignment: .top) { GeometryReader { geo in if isPresented, !message.isEmpty { Text(message) .appFont(size: 12, weight: .medium) .foregroundColor(.white) .multilineTextAlignment(.center) .lineLimit(2) .padding(.horizontal, 18) .padding(.vertical, 12) .background( Capsule() .fill(toastBackgroundColor) ) .overlay( Capsule() .stroke(Color.white.opacity(0.15), lineWidth: 1) ) .padding(.top, geo.safeAreaInsets.top + 8) .padding(.horizontal, 24) .frame(maxWidth: .infinity, alignment: .top) .transition(.move(edge: .top).combined(with: .opacity)) } } .allowsHitTesting(false) } .animation(.easeInOut(duration: 0.2), value: isPresented) .onAppear { if isPresented { scheduleDismiss() } } .onChange(of: isPresented) { newValue in if newValue { scheduleDismiss() } else { cancelDismiss() } } .onDisappear { cancelDismiss() } } private func scheduleDismiss() { cancelDismiss() guard autohideIn > 0 else { return } let workItem = DispatchWorkItem { withAnimation { isPresented = false } } dismissWorkItem = workItem DispatchQueue.main.asyncAfter(deadline: .now() + autohideIn, execute: workItem) } private func cancelDismiss() { dismissWorkItem?.cancel() dismissWorkItem = nil } } extension View { func sodaToast(isPresented: Binding, message: String, autohideIn: Double = 2) -> some View { modifier( SodaToastModifier( isPresented: isPresented, message: message, autohideIn: autohideIn ) ) } }