refactor(toast): 공통 토스트 모디파이어를 적용한다
This commit is contained in:
@@ -36,3 +36,99 @@ struct BaseView_Previews: PreviewProvider {
|
||||
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<Bool>, message: String, autohideIn: Double = 2) -> some View {
|
||||
modifier(
|
||||
SodaToastModifier(
|
||||
isPresented: isPresented,
|
||||
message: message,
|
||||
autohideIn: autohideIn
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user