-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Add ToastItem, ToastView, ToastGroup, RootView, and Toast classes
Added ToastItem struct with custom properties and Timing enum for toast item Added ToastView struct for displaying toast item with symbol image and title Added ToastGroup struct for managing multiple toast views in a ZStack Added RootView struct for setting up UIWindow for overlay window to display toast Added Toast class with present method to add toast items to display on RootView
- Loading branch information
Showing
5 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// | ||
// Toast.swift | ||
// | ||
// | ||
// Created by 홍승현 on 3/24/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
@Observable | ||
public final class Toast { | ||
/// Since the class conforms to the Observable protocol, | ||
/// we can use this singleton object as a state object to receive UI updates on the overlay window root controller | ||
public static let shared = Toast() | ||
var toasts: [ToastItem] = [] | ||
|
||
/// 토스트를 띄웁니다. | ||
/// - Parameters: | ||
/// - title: 토스트 제목 | ||
/// - symbol: 토스트에 들어갈 `SF Symbol` 이미지 문자열 값 | ||
/// - tint: 토스트의 틴트 색상 | ||
/// - isUserInteractionEnabled: 사용자의 상호작용 여부, 스스로 지우거나 못하도록 설정할 수 있습니다. | ||
/// - duration: 토스트 유지 시간 | ||
public func present( | ||
title: String, | ||
symbol: String?, | ||
tint: Color = .gray900, | ||
isUserInteractionEnabled: Bool = false, | ||
duration: ToastTime = .medium | ||
) { | ||
toasts.append(.init(title: title, symbol: symbol, tint: tint, isUseInteractionEnabled: isUserInteractionEnabled, duration: duration)) | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
Shared/Sources/DesignSystem/Components/Toast/ToastItem.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// | ||
// ToastItem.swift | ||
// | ||
// | ||
// Created by 홍승현 on 3/24/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
// MARK: - ToastItem | ||
|
||
struct ToastItem: Identifiable { | ||
let id: UUID = .init() | ||
|
||
// MARK: Custom Properties | ||
|
||
/// 토스트 제목 | ||
let title: String | ||
|
||
/// 토스트 심볼 이미지 문자열 | ||
let symbol: String? | ||
|
||
/// 토스트 틴트 색상 | ||
let tint: Color | ||
|
||
/// 사용자 상호작용 여부 | ||
let isUseInteractionEnabled: Bool | ||
|
||
// MARK: Timing | ||
|
||
/// 토스트 유지 시간 | ||
let duration: ToastTime | ||
|
||
init(title: String, symbol: String?, tint: Color, isUseInteractionEnabled: Bool, duration: ToastTime) { | ||
self.title = title | ||
self.symbol = symbol | ||
self.tint = tint | ||
self.isUseInteractionEnabled = isUseInteractionEnabled | ||
self.duration = duration | ||
} | ||
} | ||
|
||
// MARK: - ToastTime | ||
|
||
public enum ToastTime: CGFloat { | ||
case short = 1.0 | ||
case medium = 2.0 | ||
case long = 3.5 | ||
} |
55 changes: 55 additions & 0 deletions
55
Shared/Sources/DesignSystem/Components/Toast/View/RootView.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// | ||
// RootView.swift | ||
// | ||
// | ||
// Created by 홍승현 on 3/24/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
// MARK: - RootView | ||
|
||
public struct RootView<Content: View>: View { | ||
@ViewBuilder public var content: Content | ||
|
||
@inlinable public init(@ViewBuilder content: @escaping () -> Content) { | ||
self.content = content() | ||
} | ||
|
||
// MARK: View Properties | ||
|
||
@State private var overlayWindow: UIWindow? | ||
|
||
public var body: some View { | ||
content | ||
.onAppear { | ||
for scene in UIApplication.shared.connectedScenes where scene.activationState == .foregroundActive && overlayWindow == nil { | ||
if let windowScene = scene as? UIWindowScene { | ||
let window = PassthroughWindow(windowScene: windowScene) | ||
window.backgroundColor = .clear | ||
window.isUserInteractionEnabled = true | ||
window.isHidden = false | ||
|
||
// view controller part | ||
|
||
let rootController = UIHostingController(rootView: ToastGroup()) | ||
rootController.view.frame = windowScene.screen.bounds | ||
rootController.view.backgroundColor = .clear | ||
window.rootViewController = rootController | ||
|
||
overlayWindow = window | ||
break | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// MARK: - PassthroughWindow | ||
|
||
private class PassthroughWindow: UIWindow { | ||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { | ||
guard let view = super.hitTest(point, with: event) else { return nil } | ||
return rootViewController?.view == view ? nil : view | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
Shared/Sources/DesignSystem/Components/Toast/View/ToastGroup.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// | ||
// ToastGroup.swift | ||
// | ||
// | ||
// Created by 홍승현 on 3/24/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct ToastGroup: View { | ||
let model = Toast.shared | ||
var body: some View { | ||
GeometryReader { proxy in | ||
let size = proxy.size | ||
let safeArea = proxy.safeAreaInsets | ||
|
||
ZStack { | ||
ForEach(model.toasts) { | ||
ToastView(size: size, item: $0) | ||
} | ||
} | ||
.padding(.bottom, safeArea.top == .zero ? 15 : 10) | ||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
Shared/Sources/DesignSystem/Components/Toast/View/ToastView.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// | ||
// ToastView.swift | ||
// | ||
// | ||
// Created by 홍승현 on 3/24/24. | ||
// | ||
|
||
import SwiftUI | ||
|
||
struct ToastView: View { | ||
var size: CGSize | ||
var item: ToastItem | ||
|
||
var body: some View { | ||
HStack(spacing: 0) { | ||
if let symbol = item.symbol { | ||
Image(systemName: symbol) | ||
.font(.title3) | ||
.padding(.trailing, 10) | ||
} | ||
|
||
Text(item.title) | ||
} | ||
.foregroundStyle(item.tint) | ||
.padding(.horizontal, 15) | ||
.padding(.vertical, 8) | ||
.background( | ||
.background | ||
.shadow(.drop(color: .primary.opacity(0.06), radius: 5, x: 5, y: 5)) | ||
.shadow(.drop(color: .primary.opacity(0.06), radius: 5, x: -5, y: -5)), | ||
in: .capsule | ||
) | ||
.contentShape(.capsule) | ||
} | ||
} |