Last active
July 10, 2021 07:06
-
-
Save kkebo/43c6100585111ab78d0d96409db01d61 to your computer and use it in GitHub Desktop.
Toast UI implemented in SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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