70 lines
2.1 KiB
Swift
70 lines
2.1 KiB
Swift
//
|
||
// ToastCenter.swift
|
||
// suixinkan
|
||
//
|
||
// Created by Codex on 2026/6/18.
|
||
//
|
||
|
||
import Observation
|
||
import SwiftUI
|
||
|
||
@MainActor
|
||
@Observable
|
||
/// 全局 Toast 状态中心,负责保存和清除当前提示文案。
|
||
final class ToastCenter {
|
||
fileprivate var message: String?
|
||
|
||
/// 显示一条全局 Toast 文案。
|
||
func show(_ message: String) {
|
||
self.message = message
|
||
}
|
||
|
||
/// 清除当前 Toast 文案。
|
||
func dismiss() {
|
||
message = nil
|
||
}
|
||
}
|
||
|
||
/// 全局 Toast 叠加层修饰器,负责把 Toast 展示到页面顶部。
|
||
private struct GlobalToastOverlay: ViewModifier {
|
||
@Environment(ToastCenter.self) private var toastCenter
|
||
|
||
func body(content: Content) -> some View {
|
||
content
|
||
.overlay(alignment: .top) {
|
||
if let message = toastCenter.message {
|
||
HStack(spacing: 10) {
|
||
Image(systemName: "exclamationmark.triangle.fill")
|
||
.font(.system(size: 14, weight: .semibold))
|
||
|
||
Text(message)
|
||
.font(.system(size: 13, weight: .medium))
|
||
.lineLimit(2)
|
||
|
||
Spacer(minLength: 0)
|
||
|
||
Button("关闭") {
|
||
toastCenter.dismiss()
|
||
}
|
||
.font(.system(size: 13, weight: .semibold))
|
||
}
|
||
.foregroundStyle(.white)
|
||
.padding(.horizontal, 14)
|
||
.padding(.vertical, 10)
|
||
.background(Color.black.opacity(0.78), in: RoundedRectangle(cornerRadius: 12))
|
||
.padding(.horizontal, 16)
|
||
.padding(.top, 10)
|
||
.transition(.move(edge: .top).combined(with: .opacity))
|
||
}
|
||
}
|
||
.animation(.easeInOut(duration: 0.18), value: toastCenter.message)
|
||
}
|
||
}
|
||
|
||
extension View {
|
||
/// 给任意根视图追加全局 Toast 展示能力。
|
||
func globalToastOverlay() -> some View {
|
||
modifier(GlobalToastOverlay())
|
||
}
|
||
}
|