@@ -27,19 +27,30 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
2727 static let recommendedRoutineStackViewBottomSpacing : CGFloat = 50
2828 static let routineCardHeight : CGFloat = 80
2929 static let registerEmotionButtonHeight : CGFloat = 52
30+ static let floatingButtonBottomSpacing : CGFloat = 19
31+ static let floatingButtonSize : CGFloat = 52
32+ static let floatingMenuBottomSpacing : CGFloat = 15
33+ static let floatingMenuHeight : CGFloat = 64
34+ static let floatingMenuWidth : CGFloat = 144
3035 }
3136
3237 private let categoryView = RoutineCategoryView ( )
3338 private let headerStackView = UIStackView ( )
3439 private let routineLabel = UILabel ( )
3540 private let levelButton = RoutineLevelButton ( )
3641 private let levelView = SelectableItemTableView < RoutineLevelType > ( items: RoutineLevelType . allCases. sorted ( by: { $0. id < $1. id } ) )
42+
3743 private let recommendedRoutineScrollView = UIScrollView ( )
3844 private let recommendedRoutineStackView = UIStackView ( )
3945 private var recommendedRoutineCards : [ Int : RecommendedRoutineCardView ] = [ : ]
4046 private let registerEmotionButton = RegisterEmotionButton ( )
41- private var cancellables : Set < AnyCancellable >
4247
48+ private var isShowingFloatinMenu : Bool = false
49+ private let dimmedView = UIView ( )
50+ private let floatingButton = FloatingButton ( )
51+ private let floatingMenu = FloatingMenuView ( )
52+
53+ private var cancellables : Set < AnyCancellable >
4354 public override init ( viewModel: RecommendedRoutineViewModel ) {
4455 cancellables = [ ]
4556 super. init ( viewModel: viewModel)
@@ -71,7 +82,7 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
7182 levelView. delegate = self
7283
7384 recommendedRoutineScrollView. showsVerticalScrollIndicator = false
74-
85+
7586 recommendedRoutineStackView. axis = . vertical
7687 recommendedRoutineStackView. spacing = Layout . recommendedRoutineStackViewSpacing
7788
@@ -83,6 +94,20 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
8394 emotionRegisterView. hidesBottomBarWhenPushed = true
8495 self . navigationController? . pushViewController ( emotionRegisterView, animated: true )
8596 } , for: . touchUpInside)
97+
98+ floatingButton. addAction ( UIAction { [ weak self] _ in
99+ self ? . toggleFloatingButton ( )
100+ } , for: . touchUpInside)
101+
102+ floatingMenu. isHidden = true
103+ floatingMenu. delegate = self
104+
105+ dimmedView. isHidden = true
106+ dimmedView. backgroundColor = UIColor . black. withAlphaComponent ( 0.7 )
107+ dimmedView. alpha = 0
108+
109+ let tapGesture = UITapGestureRecognizer ( target: self , action: #selector( tappedDimmedView) )
110+ dimmedView. addGestureRecognizer ( tapGesture)
86111 }
87112
88113 public override func configureLayout( ) {
@@ -97,6 +122,10 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
97122 view. addSubview ( recommendedRoutineScrollView)
98123 recommendedRoutineScrollView. addSubview ( recommendedRoutineStackView)
99124
125+ view. addSubview ( dimmedView)
126+ view. addSubview ( floatingMenu)
127+ view. addSubview ( floatingButton)
128+
100129 categoryView. snp. makeConstraints { make in
101130 make. leading. equalTo ( safeArea)
102131 make. trailing. equalTo ( safeArea)
@@ -131,6 +160,23 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
131160 make. bottom. equalToSuperview ( ) . inset ( Layout . recommendedRoutineStackViewBottomSpacing)
132161 make. width. equalTo ( recommendedRoutineScrollView. snp. width)
133162 }
163+
164+ floatingButton. snp. makeConstraints { make in
165+ make. trailing. equalTo ( safeArea) . inset ( Layout . horizontalMargin)
166+ make. bottom. equalTo ( safeArea) . inset ( Layout . floatingButtonBottomSpacing)
167+ make. size. equalTo ( Layout . floatingButtonSize)
168+ }
169+
170+ floatingMenu. snp. makeConstraints { make in
171+ make. trailing. equalTo ( safeArea) . inset ( Layout . horizontalMargin)
172+ make. bottom. equalTo ( floatingButton. snp. top) . offset ( - Layout. floatingMenuBottomSpacing)
173+ make. height. equalTo ( Layout . floatingMenuHeight)
174+ make. width. equalTo ( Layout . floatingMenuWidth)
175+ }
176+
177+ dimmedView. snp. makeConstraints { make in
178+ make. edges. equalToSuperview ( )
179+ }
134180 }
135181
136182 public override func bind( ) {
@@ -159,7 +205,7 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
159205 if recommendedRoutines. first? . routineCategory == . recommendation {
160206 showEmotionButton ( )
161207 }
162-
208+
163209 for routine in recommendedRoutines {
164210 let routineCard = RecommendedRoutineCardView ( recommendedRoutine: routine)
165211 recommendedRoutineCards [ routine. id] = routineCard
@@ -172,6 +218,9 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
172218 }
173219
174220 private func showBottomSheet( ) {
221+ if isShowingFloatinMenu {
222+ toggleFloatingButton ( )
223+ }
175224 presentCustomBottomSheet ( contentViewController: levelView, maxHeight: Layout . bottomSheetHeight)
176225 }
177226
@@ -183,6 +232,23 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
183232 make. height. equalTo ( Layout . registerEmotionButtonHeight)
184233 }
185234 }
235+
236+ private func toggleFloatingButton( ) {
237+ floatingButton. toggle ( )
238+ isShowingFloatinMenu. toggle ( )
239+
240+ floatingMenu. isHidden = !isShowingFloatinMenu
241+ dimmedView. isHidden = !isShowingFloatinMenu
242+
243+ UIView . animate ( withDuration: 0.2 , delay: 0 , options: [ . curveEaseOut] ) {
244+ self . dimmedView. alpha = self . isShowingFloatinMenu ? 1 : 0
245+ self . floatingMenu. alpha = self . isShowingFloatinMenu ? 1 : 0
246+ }
247+ }
248+
249+ @objc private func tappedDimmedView( ) {
250+ toggleFloatingButton ( )
251+ }
186252}
187253
188254// MARK: RoutineCategoryViewDelegate
@@ -208,3 +274,15 @@ extension RecommendedRoutineView: SelectableItemTableViewDelegate {
208274 levelButton. updateButton ( level: didSelectLevel)
209275 }
210276}
277+
278+ // MARK: FloatingMenuViewDelegate
279+ extension RecommendedRoutineView : FloatingMenuViewDelegate {
280+ func floatingMenuDidTapRegisterRoutineButton( _ sender: FloatingMenuView ) {
281+ toggleFloatingButton ( )
282+ guard let routineCreationViewModel = DIContainer . shared. resolve ( type: RoutineCreationViewModel . self) else {
283+ fatalError ( " routineCreationViewModel 의존성이 등록되지 않았습니다. " )
284+ }
285+ let routineCreationView = RoutineCreationView ( viewModel: routineCreationViewModel)
286+ self . navigationController? . pushViewController ( routineCreationView, animated: true )
287+ }
288+ }
0 commit comments