11//
2- // RecommendedRoutineView .swift
2+ // RecommendedRoutineViewController .swift
33// Presentation
44//
55// Created by 최정인 on 7/12/25.
@@ -11,14 +11,15 @@ import Shared
1111import SnapKit
1212import UIKit
1313
14- final class RecommendedRoutineView : BaseViewController < RecommendedRoutineViewModel > {
14+ final class RecommendedRoutineViewController : BaseViewController < RecommendedRoutineViewModel > {
1515 private enum Layout {
1616 static let horizontalMargin : CGFloat = 20
1717 static let bottomSheetHeight : CGFloat = 226
18- static let categoryViewTopSpacing : CGFloat = 16
18+ static let categoryViewTopSpacing : CGFloat = 70
1919 static let categoryViewHeight : CGFloat = 36
2020 static let headerStackViewTrailingSpacing : CGFloat = 8
21- static let headerStackViewTopSpacing : CGFloat = 18
21+ static let headerStackViewTopSpacing : CGFloat = 20
22+ static let headerStackViewTopMaxSpacing : CGFloat = 24
2223 static let headerStackViewHeight : CGFloat = 40
2324 static let recommendedRoutineStackViewSpacing : CGFloat = 12
2425 static let recommendedRoutineScrollViewTopSpacing : CGFloat = 12
@@ -50,10 +51,8 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
5051 private let dimmedView = UIView ( )
5152 private let floatingButton = FloatingButton ( )
5253 private let floatingMenu = FloatingMenuView ( )
53-
54- private let toastMessageView = ToastMessageView ( )
55-
5654 private var cancellables : Set < AnyCancellable >
55+
5756 public override init ( viewModel: RecommendedRoutineViewModel ) {
5857 cancellables = [ ]
5958 super. init ( viewModel: viewModel)
@@ -70,12 +69,11 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
7069 }
7170
7271 override func configureAttribute( ) {
73- title = " 추천 루틴 "
7472 categoryView. delegate = self
7573
76- routineLabel. text = " 루틴 목록 "
77- routineLabel. font = BitnagilFont ( style: . body1 , weight: . semiBold) . font
78- routineLabel. textColor = BitnagilColor . gray10
74+ routineLabel. text = " 추천 루틴리스트 "
75+ routineLabel. font = BitnagilFont ( style: . body2 , weight: . semiBold) . font
76+ routineLabel. textColor = BitnagilColor . gray60
7977
8078 headerStackView. axis = . horizontal
8179
@@ -109,9 +107,12 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
109107
110108 override func configureLayout( ) {
111109 let safeArea = view. safeAreaLayoutGuide
112- view. backgroundColor = . systemBackground
110+ view. backgroundColor = BitnagilColor . gray99
111+ navigationController? . setNavigationBarHidden ( true , animated: false )
112+ configureCustomNavigationBar ( navigationBarStyle: . withTitle( title: " 추천 루틴 " ) , backgroundColor: BitnagilColor . gray99)
113113
114114 view. addSubview ( categoryView)
115+ view. addSubview ( registerEmotionButton)
115116 view. addSubview ( headerStackView)
116117 [ routineLabel, levelButton] . forEach {
117118 headerStackView. addArrangedSubview ( $0)
@@ -123,19 +124,24 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
123124 view. addSubview ( floatingMenu)
124125 view. addSubview ( floatingButton)
125126
126- view. addSubview ( toastMessageView)
127-
128127 categoryView. snp. makeConstraints { make in
129128 make. leading. equalTo ( safeArea)
130129 make. trailing. equalTo ( safeArea)
131130 make. top. equalTo ( safeArea) . offset ( Layout . categoryViewTopSpacing)
132131 make. height. equalTo ( Layout . categoryViewHeight)
133132 }
134133
134+ registerEmotionButton. snp. makeConstraints { make in
135+ make. leading. equalToSuperview ( ) . offset ( 20 )
136+ make. trailing. equalToSuperview ( ) . inset ( 20 )
137+ make. top. equalTo ( categoryView. snp. bottom) . offset ( 20 )
138+ make. height. equalTo ( Layout . registerEmotionButtonHeight)
139+ }
140+
135141 headerStackView. snp. makeConstraints { make in
136142 make. leading. equalTo ( safeArea) . offset ( Layout . horizontalMargin)
137143 make. trailing. equalTo ( safeArea) . inset ( Layout . headerStackViewTrailingSpacing)
138- make. top. equalTo ( categoryView . snp. bottom) . offset ( Layout . headerStackViewTopSpacing)
144+ make. top. equalTo ( registerEmotionButton . snp. bottom) . offset ( Layout . headerStackViewTopSpacing)
139145 make. height. equalTo ( Layout . headerStackViewHeight)
140146 }
141147
@@ -176,19 +182,14 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
176182 dimmedView. snp. makeConstraints { make in
177183 make. edges. equalToSuperview ( )
178184 }
179-
180- toastMessageView. snp. makeConstraints { make in
181- make. centerX. equalToSuperview ( )
182- make. bottom. equalTo ( safeArea. snp. bottom) . offset ( - Layout. toastMessageBottomSpacing)
183- }
184185 }
185186
186187 override func bind( ) {
187188 viewModel. output. selectedCategoryPublisher
188189 . receive ( on: DispatchQueue . main)
189190 . sink { [ weak self] selectedCategory in
190- self ? . showEmotionButton ( isShowEmotionButton: selectedCategory == . recommendation)
191191 self ? . categoryView. updateSelectedCategory ( selectedCategory: selectedCategory)
192+ self ? . showEmotionButton ( isShowEmotionButton: selectedCategory == . recommendation)
192193 }
193194 . store ( in: & cancellables)
194195
@@ -204,7 +205,7 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
204205 . sink { [ weak self] isExistEmotion in
205206 if isExistEmotion {
206207 self ? . isExistEmotion = isExistEmotion
207- self ? . registerEmotionButton . isHidden = true
208+ self ? . showEmotionButton ( isShowEmotionButton : false )
208209 }
209210 }
210211 . store ( in: & cancellables)
@@ -241,24 +242,20 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
241242
242243 // 감정 등록 버튼을 보이거나 숨겨줍니다.
243244 private func showEmotionButton( isShowEmotionButton: Bool ) {
244- guard !isExistEmotion else {
245- registerEmotionButton. isHidden = true
246- return
247- }
248-
249- guard isShowEmotionButton else {
250- registerEmotionButton. isHidden = true
251- return
252- }
253- guard !recommendedRoutineStackView. arrangedSubviews. contains ( registerEmotionButton) else {
254- registerEmotionButton. isHidden = false
255- return
256- }
245+ let safeArea = view. safeAreaLayoutGuide
246+ let showingEmotionButton = isShowEmotionButton && !isExistEmotion
247+ registerEmotionButton. isHidden = !showingEmotionButton
257248
258- recommendedRoutineStackView. addArrangedSubview ( registerEmotionButton)
259- registerEmotionButton. snp. makeConstraints { make in
260- make. leading. trailing. equalToSuperview ( )
261- make. height. equalTo ( Layout . registerEmotionButtonHeight)
249+ headerStackView. snp. remakeConstraints { make in
250+ make. leading. equalTo ( safeArea) . offset ( Layout . horizontalMargin)
251+ make. trailing. equalTo ( safeArea) . inset ( Layout . headerStackViewTrailingSpacing)
252+ make. height. equalTo ( Layout . headerStackViewHeight)
253+
254+ if showingEmotionButton {
255+ make. top. equalTo ( registerEmotionButton. snp. bottom) . offset ( Layout . headerStackViewTopSpacing)
256+ } else {
257+ make. top. equalTo ( categoryView. snp. bottom) . offset ( Layout . headerStackViewTopMaxSpacing)
258+ }
262259 }
263260 }
264261
@@ -282,18 +279,17 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
282279}
283280
284281// MARK: RoutineCategoryViewDelegate
285- extension RecommendedRoutineView : RoutineCategoryViewDelegate {
282+ extension RecommendedRoutineViewController : RoutineCategoryViewDelegate {
286283 func routineCategoryView( _ sender: RoutineCategoryView , didSelectCategory category: RoutineCategoryType ) {
287284 viewModel. action ( input: . selectCategory( selectedCategory: category) )
288285 }
289286}
290287
291288// MARK: RecommendedRoutineCardViewDelegate
292- extension RecommendedRoutineView : RecommendedRoutineCardViewDelegate {
289+ extension RecommendedRoutineViewController : RecommendedRoutineCardViewDelegate {
293290 func recommendedRoutineCardView( _ sender: RecommendedRoutineCardView , didTapRecommendedRoutine routine: RecommendedRoutine ) {
294- guard let routineCreationViewModel = DIContainer . shared. resolve ( type: RoutineCreationViewModel . self) else {
295- fatalError ( " routineCreationViewModel 의존성이 등록되지 않았습니다. " )
296- }
291+ guard let routineCreationViewModel = DIContainer . shared. resolve ( type: RoutineCreationViewModel . self)
292+ else { fatalError ( " routineCreationViewModel 의존성이 등록되지 않았습니다. " ) }
297293
298294 let routineCreationView = RoutineCreationView ( viewModel: routineCreationViewModel, recommendRoutineId: routine. id)
299295 routineCreationView. hidesBottomBarWhenPushed = true
@@ -302,7 +298,7 @@ extension RecommendedRoutineView: RecommendedRoutineCardViewDelegate {
302298}
303299
304300// MARK: SelectableItemTableViewDelegate
305- extension RecommendedRoutineView : SelectableItemTableViewDelegate {
301+ extension RecommendedRoutineViewController : SelectableItemTableViewDelegate {
306302 func selectableItemTableView< T: SelectableItem & CaseIterable & Equatable > ( _ sender: SelectableItemTableView < T > , didSelectItem: T ? ) {
307303 guard let didSelectLevel = didSelectItem as? RoutineLevelType ?
308304 else { return }
@@ -312,19 +308,19 @@ extension RecommendedRoutineView: SelectableItemTableViewDelegate {
312308}
313309
314310// MARK: FloatingMenuViewDelegate
315- extension RecommendedRoutineView : FloatingMenuViewDelegate {
311+ extension RecommendedRoutineViewController : FloatingMenuViewDelegate {
316312 func floatingMenuDidTapRegisterRoutineButton( _ sender: FloatingMenuView ) {
317313 toggleFloatingButton ( )
318- guard let routineCreationViewModel = DIContainer . shared. resolve ( type: RoutineCreationViewModel . self) else {
319- fatalError ( " routineCreationViewModel 의존성이 등록되지 않았습니다. " )
320- }
314+ guard let routineCreationViewModel = DIContainer . shared. resolve ( type: RoutineCreationViewModel . self)
315+ else { fatalError ( " routineCreationViewModel 의존성이 등록되지 않았습니다. " ) }
316+
321317 let routineCreationView = RoutineCreationView ( viewModel: routineCreationViewModel)
322318 routineCreationView. hidesBottomBarWhenPushed = true
323319 self . navigationController? . pushViewController ( routineCreationView, animated: true )
324320 }
325321}
326322
327- extension RecommendedRoutineView : RegisterEmotionButtonViewDelegate {
323+ extension RecommendedRoutineViewController : RegisterEmotionButtonViewDelegate {
328324 func registerEmotionButtonViewDidTapRegisterButton( _ sender: RegisterEmotionButtonView ) {
329325 guard let emotionRegisterViewModel = DIContainer . shared. resolve ( type: EmotionRegisterViewModel . self)
330326 else { fatalError ( " emotionRegisterViewModel 의존성이 등록되지 않았습니다. " ) }
0 commit comments