Skip to content

Instantly share code, notes, and snippets.

@kkebo
Last active July 10, 2021 07:06
Show Gist options
  • Save kkebo/43c6100585111ab78d0d96409db01d61 to your computer and use it in GitHub Desktop.
Save kkebo/43c6100585111ab78d0d96409db01d61 to your computer and use it in GitHub Desktop.
Toast UI implemented in SwiftUI
// Swift Playgrounds での利用例
import PlaygroundSupport
import SwiftUI
struct ContentView {
@State var toastDuration: DispatchTimeInterval?
@State var toastMessage: LocalizedStringKey = ""
}
extension ContentView: View {
var body: some View {
VStack {
Button("Show") {
self.toastMessage = "\(Date())"
self.toastDuration = .seconds(3)
}
.toast(duration: self.$toastDuration) {
Text(self.toastMessage)
}
}
.frame(
maxWidth: .infinity,
maxHeight: .infinity
)
.background(Color.red.ignoresSafeArea())
}
}
PlaygroundPage.current.setLiveView(ContentView())
import SwiftUI
fileprivate extension AnyTransition {
static var toast: Self {
Self.move(edge: .bottom)
.combined(with: .opacity)
.animation(.easeInOut(duration: 0.5))
}
}
public struct Toast<Label> where Label: View {
@Binding var duration: DispatchTimeInterval?
let label: Label
@State private var isPresented = false
@State private var task: DispatchWorkItem?
public init(
duration: Binding<DispatchTimeInterval?>,
label: @escaping () -> Label
) {
self._duration = duration
self.label = label()
}
}
extension Toast: View {
public var body: some View {
VStack {
if self.isPresented {
self.label
.foregroundColor(.white)
.padding()
.background(Color.black.opacity(0.5))
.cornerRadius(10)
.transition(.toast)
}
}
.onChange(of: self.duration) { duration in
if let duration = duration {
self.task?.cancel()
self.duration = nil
withAnimation {
self.isPresented = true
}
let task = DispatchWorkItem {
withAnimation {
self.isPresented = false
}
}
self.task = task
DispatchQueue.main.asyncAfter(
deadline: .now() + duration,
execute: task
)
}
}
}
}
public struct ToastModifier<Label> where Label: View {
@Binding var duration: DispatchTimeInterval?
let label: () -> Label
}
extension ToastModifier: ViewModifier {
public func body(content: Content) -> some View {
ZStack {
content
VStack {
Spacer()
Toast(
duration: self.$duration,
label: self.label
)
.padding()
}
}
}
}
extension View {
public func toast<Label>(
duration: Binding<DispatchTimeInterval?>,
label: @escaping () -> Label
) -> some View where Label: View {
self.modifier(
ToastModifier(duration: duration, label: label)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment