Skip to content

Commit d355e52

Browse files
committed
Feat: 추천 루틴 화면에 FloatingButton 추가 및 등록 화면 이동 (#T3-121)
- RecommendedRoutineView에 FloatingButton, FloatingMenu, dimmedView 추가 - 루틴 등록 화면으로 이동해야 하므로 RoutineCreationViewModel 의존성 등록
1 parent 272d7a3 commit d355e52

3 files changed

Lines changed: 85 additions & 1 deletion

File tree

Projects/Presentation/Sources/Common/DesignSystem/BitnagilIcon.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ enum BitnagilIcon {
2323
static let ellipsisIcon = UIImage(named: "ellipsis_icon", in: bundle, with: nil)
2424
static let informationIcon = UIImage(named: "information_icon", in: bundle, with: nil)
2525
static let sortIcon = UIImage(named: "sort_icon", in: bundle, with: nil)
26+
static let addRoutineIcon = UIImage(named: "add_routine_icon", in: bundle, with: nil)
2627

2728
// MARK: - Tab Bar Icons
2829
static let homeFillIcon = UIImage(named: "home_fill_icon", in: bundle, with: nil)

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,9 @@ public struct PresentationDependencyAssembler: DependencyAssemblerProtocol {
4949

5050
return MypageViewModel(userDataRepository: userDataRepository)
5151
}
52+
53+
DIContainer.shared.register(type: RoutineCreationViewModel.self) { _ in
54+
return RoutineCreationViewModel()
55+
}
5256
}
5357
}

Projects/Presentation/Sources/RecommendedRoutine/View/RecommendedRoutineView.swift

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Combine
99
import Domain
10+
import Shared
1011
import SnapKit
1112
import UIKit
1213

@@ -26,19 +27,30 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
2627
static let recommendedRoutineStackViewBottomSpacing: CGFloat = 50
2728
static let routineCardHeight: CGFloat = 80
2829
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
2935
}
3036

3137
private let categoryView = RoutineCategoryView()
3238
private let headerStackView = UIStackView()
3339
private let routineLabel = UILabel()
3440
private let levelButton = RoutineLevelButton()
3541
private let levelView = SelectableItemTableView<RoutineLevelType>(items: RoutineLevelType.allCases.sorted(by: { $0.id < $1.id }))
42+
3643
private let recommendedRoutineScrollView = UIScrollView()
3744
private let recommendedRoutineStackView = UIStackView()
3845
private var recommendedRoutineCards: [Int: RecommendedRoutineCardView] = [:]
3946
private let registerEmotionButton = RegisterEmotionButton()
40-
private var cancellables: Set<AnyCancellable>
4147

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>
4254
public override init(viewModel: RecommendedRoutineViewModel) {
4355
cancellables = []
4456
super.init(viewModel: viewModel)
@@ -73,6 +85,20 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
7385

7486
recommendedRoutineStackView.axis = .vertical
7587
recommendedRoutineStackView.spacing = Layout.recommendedRoutineStackViewSpacing
88+
89+
floatingButton.addAction(UIAction { [weak self] _ in
90+
self?.toggleFloatingButton()
91+
}, for: .touchUpInside)
92+
93+
floatingMenu.isHidden = true
94+
floatingMenu.delegate = self
95+
96+
dimmedView.isHidden = true
97+
dimmedView.backgroundColor = UIColor.black.withAlphaComponent(0.7)
98+
dimmedView.alpha = 0
99+
100+
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tappedDimmedView))
101+
dimmedView.addGestureRecognizer(tapGesture)
76102
}
77103

78104
public override func configureLayout() {
@@ -87,6 +113,10 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
87113
view.addSubview(recommendedRoutineScrollView)
88114
recommendedRoutineScrollView.addSubview(recommendedRoutineStackView)
89115

116+
view.addSubview(dimmedView)
117+
view.addSubview(floatingMenu)
118+
view.addSubview(floatingButton)
119+
90120
categoryView.snp.makeConstraints { make in
91121
make.leading.equalTo(safeArea)
92122
make.trailing.equalTo(safeArea)
@@ -121,6 +151,23 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
121151
make.bottom.equalToSuperview().inset(Layout.recommendedRoutineStackViewBottomSpacing)
122152
make.width.equalTo(recommendedRoutineScrollView.snp.width)
123153
}
154+
155+
floatingButton.snp.makeConstraints { make in
156+
make.trailing.equalTo(safeArea).inset(Layout.horizontalMargin)
157+
make.bottom.equalTo(safeArea).inset(Layout.floatingButtonBottomSpacing)
158+
make.size.equalTo(Layout.floatingButtonSize)
159+
}
160+
161+
floatingMenu.snp.makeConstraints { make in
162+
make.trailing.equalTo(safeArea).inset(Layout.horizontalMargin)
163+
make.bottom.equalTo(floatingButton.snp.top).offset(-Layout.floatingMenuBottomSpacing)
164+
make.height.equalTo(Layout.floatingMenuHeight)
165+
make.width.equalTo(Layout.floatingMenuWidth)
166+
}
167+
168+
dimmedView.snp.makeConstraints { make in
169+
make.edges.equalToSuperview()
170+
}
124171
}
125172

126173
public override func bind() {
@@ -162,6 +209,9 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
162209
}
163210

164211
private func showBottomSheet() {
212+
if isShowingFloatinMenu {
213+
toggleFloatingButton()
214+
}
165215
presentCustomBottomSheet(contentViewController: levelView, maxHeight: Layout.bottomSheetHeight)
166216
}
167217

@@ -173,6 +223,23 @@ final class RecommendedRoutineView: BaseViewController<RecommendedRoutineViewMod
173223
make.height.equalTo(Layout.registerEmotionButtonHeight)
174224
}
175225
}
226+
227+
private func toggleFloatingButton() {
228+
floatingButton.toggle()
229+
isShowingFloatinMenu.toggle()
230+
231+
floatingMenu.isHidden = !isShowingFloatinMenu
232+
dimmedView.isHidden = !isShowingFloatinMenu
233+
234+
UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseOut]) {
235+
self.dimmedView.alpha = self.isShowingFloatinMenu ? 1 : 0
236+
self.floatingMenu.alpha = self.isShowingFloatinMenu ? 1 : 0
237+
}
238+
}
239+
240+
@objc private func tappedDimmedView() {
241+
toggleFloatingButton()
242+
}
176243
}
177244

178245
// MARK: RoutineCategoryViewDelegate
@@ -198,3 +265,15 @@ extension RecommendedRoutineView: SelectableItemTableViewDelegate {
198265
levelButton.updateButton(level: didSelectLevel)
199266
}
200267
}
268+
269+
// MARK: FloatingMenuViewDelegate
270+
extension RecommendedRoutineView: FloatingMenuViewDelegate {
271+
func floatingMenuDidTapRegisterRoutineButton(_ sender: FloatingMenuView) {
272+
toggleFloatingButton()
273+
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self) else {
274+
fatalError("routineCreationViewModel 의존성이 등록되지 않았습니다.")
275+
}
276+
let routineCreationView = RoutineCreationView(viewModel: routineCreationViewModel)
277+
self.navigationController?.pushViewController(routineCreationView, animated: true)
278+
}
279+
}

0 commit comments

Comments
 (0)