1. 사용법
import SwiftUI
struct ContentView: View {
@State private var isToastPresented: Bool = false
var body: some View {
VStack {
Button("Show Toast") {
isToastPresented = true
}
}
.toast(
isPresented: $isToastPresented,
duration: 2, // option
message: "저장되었습니다."
)
}
}
2. 구현 방법
1) 표시 조건 (isPresented)
ToastOverlayView.body는 if isPresented { ... } 조건이어서, isPresented == true일 때만 ToastCardView가 뷰 트리에 올라옵니다.
dismissAnimated()에서 애니메이션 후 isPresented = false로 다시 내려서 뷰 트리에서 제거합니다.
2) 배치 기준 (overlay + frame)
toast() 모디파이어가 호출된 뷰에서
.frame(maxWidth: .infinity, maxHeight: .infinity)로 ‘부모(호출한 뷰)의 레이아웃 크기’를 가능한 크게 확장합니다.
.overlay(alignment: .bottom)로 오버레이를 ‘하단 정렬 기준’으로 올립니다.
- 즉,
ToastOverlayView 자체가 전체 화면을 잡는 것이 아니라, overlay의 기준이 되는 부모 뷰가 커지도록 강제하고 그 하단에 붙이는 구조입니다.
3) 등장 애니메이션 (하단에서 위로 올라오는 원리)
@State private var yOffset: CGFloat = 0
@State private var opacityValue: Double = 0
isPresented가 true가 되어 뷰가 나타나면 onAppear가 호출되고,
presentAnimated()에서 withAnimation(.spring(...))로
yOffset = -100
opacityValue = 1
overlay 정렬이 .bottom이므로, 기본 위치(offset == 0)는 하단 고정 위치입니다. 여기서 offset(y: -100)은 화면 위쪽 방향으로 100pt 이동이므로 결과적으로 ‘하단에서 살짝 위로 올라온 위치’로 스프링 애니메이션이 발생합니다.
- 즉, ‘아래에서 올라오는’ 느낌은 실제로는
하단 기준 위치 → 위로 이동 형태의 애니메이션입니다.
4) 자동 종료 (DispatchWorkItem)
scheduleDismiss()가 duration 후 실행할 작업을 DispatchWorkItem으로 만들고 DispatchQueue.main.asyncAfter로 예약합니다.
- 중복 예약 방지를 위해
presentAnimated()/dismissAnimated()에서 기존 dismissWorkItem을 cancel() 후 nil로 정리합니다.
5) 사라짐 애니메이션 (원위치로 내려가며 투명해짐)