diff --git a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/Components/WeeklyCalendarHeaderView.swift b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/Components/WeeklyCalendarHeaderView.swift index 049afd1f..ed512f84 100644 --- a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/Components/WeeklyCalendarHeaderView.swift +++ b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/Components/WeeklyCalendarHeaderView.swift @@ -17,6 +17,9 @@ final class WeeklyCalendarHeaderView: UIView { static let headerHeight: CGFloat = 44 static let navigationSpacing: CGFloat = 8 static let buttonSize: CGFloat = 44 + static let todayButtonSpacing: CGFloat = 8 + static let todayButtonCornerRadius: CGFloat = 12 + static let todayButtonInsets = UIEdgeInsets(top: 4, left: 10, bottom: 4, right: 10) } // MARK: - Publishers @@ -29,8 +32,13 @@ final class WeeklyCalendarHeaderView: UIView { nextTapSubject.eraseToAnyPublisher() } + var todayTapPublisher: AnyPublisher { + todayTapSubject.eraseToAnyPublisher() + } + private let previousTapSubject = PassthroughSubject() private let nextTapSubject = PassthroughSubject() + private let todayTapSubject = PassthroughSubject() // MARK: - UI Components @@ -41,6 +49,20 @@ final class WeeklyCalendarHeaderView: UIView { return label }() + private let todayButton: UIButton = { + let button = UIButton() + button.setAttributedTitle( + Typography.p12.styled("오늘", color: DesignSystemAsset.primary.color), + for: .normal + ) + button.contentEdgeInsets = Constants.todayButtonInsets + button.layer.cornerRadius = Constants.todayButtonCornerRadius + button.layer.borderWidth = 1 + button.layer.borderColor = DesignSystemAsset.primary.color.cgColor + button.isHidden = true + return button + }() + private let previousButton: UIButton = { let button = UIButton() button.setImage(DesignSystemAsset.iconNext.image.withHorizontallyFlippedOrientation(), for: .normal) @@ -80,6 +102,7 @@ final class WeeklyCalendarHeaderView: UIView { private func setupUI() { addSubview(monthLabel) + addSubview(todayButton) addSubview(navigationStack) navigationStack.addArrangedSubview(previousButton) navigationStack.addArrangedSubview(nextButton) @@ -95,6 +118,11 @@ final class WeeklyCalendarHeaderView: UIView { $0.centerY.equalToSuperview() } + todayButton.snp.makeConstraints { + $0.leading.equalTo(monthLabel.snp.trailing).offset(Constants.todayButtonSpacing) + $0.centerY.equalToSuperview() + } + navigationStack.snp.makeConstraints { $0.trailing.equalToSuperview() $0.centerY.equalToSuperview() @@ -112,6 +140,7 @@ final class WeeklyCalendarHeaderView: UIView { private func setupActions() { previousButton.addTarget(self, action: #selector(previousTapped), for: .touchUpInside) nextButton.addTarget(self, action: #selector(nextTapped), for: .touchUpInside) + todayButton.addTarget(self, action: #selector(todayTapped), for: .touchUpInside) } // MARK: - Public Methods @@ -125,6 +154,10 @@ final class WeeklyCalendarHeaderView: UIView { nextButton.alpha = isEnabled ? 1.0 : 0.3 } + func setTodayButtonHidden(_ isHidden: Bool) { + todayButton.isHidden = isHidden + } + // MARK: - Actions @objc private func previousTapped() { @@ -134,4 +167,8 @@ final class WeeklyCalendarHeaderView: UIView { @objc private func nextTapped() { nextTapSubject.send() } + + @objc private func todayTapped() { + todayTapSubject.send() + } } diff --git a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/ViewModel/WeeklyCalendarViewModel.swift b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/ViewModel/WeeklyCalendarViewModel.swift index 6fc1e49b..ca15e09f 100644 --- a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/ViewModel/WeeklyCalendarViewModel.swift +++ b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/ViewModel/WeeklyCalendarViewModel.swift @@ -147,6 +147,13 @@ public final class WeeklyCalendarViewModel { await loadWeekData(for: currentWeekBaseDate) await updateDateContent(for: state.selectedDate) + case .goToToday: + let today = calendar.startOfDay(for: Date()) + currentWeekBaseDate = today + state.selectedDate = today + await loadWeekData(for: currentWeekBaseDate) + await updateDateContent(for: today) + case .selectDate(let date): if !calendar.isDate(state.selectedDate, inSameDayAs: date) { state.selectedDate = date @@ -331,6 +338,7 @@ extension WeeklyCalendarViewModel { case requestPhotoAuthorization case goToPreviousWeek case goToNextWeek + case goToToday case selectDate(Date) case saveSelectedPhotos([any ImageAssetable]) case handlePushNotification(AnalysisResultNotification) diff --git a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/WeeklyCalendarViewController.swift b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/WeeklyCalendarViewController.swift index 878baf2f..8da63cf8 100644 --- a/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/WeeklyCalendarViewController.swift +++ b/FoodDiary/Presentation/Sources/Calendar/WeeklyCalendar/WeeklyCalendarViewController.swift @@ -129,6 +129,12 @@ public final class WeeklyCalendarViewController: UIViewController { } .store(in: &cancellables) + headerView.todayTapPublisher + .sink { [weak self] in + self?.viewModel.input.send(.goToToday) + } + .store(in: &cancellables) + weekGridView.dateTapPublisher .sink { [weak self] date in self?.viewModel.input.send(.selectDate(date)) @@ -169,6 +175,15 @@ public final class WeeklyCalendarViewController: UIViewController { } .store(in: &cancellables) + viewModel.statePublisher + .map(\.selectedDate) + .removeDuplicates() + .receive(on: DispatchQueue.main) + .sink { [weak self] date in + self?.headerView.setTodayButtonHidden(Calendar.current.isDateInToday(date)) + } + .store(in: &cancellables) + viewModel.statePublisher .map(\.canGoToNextWeek) .removeDuplicates()