Skip to content

Commit 11fc2fe

Browse files
committed
Feat: RecommendedRoutineEmptyView 구현 (#T3-167)
1 parent 305e6c0 commit 11fc2fe

5 files changed

Lines changed: 123 additions & 30 deletions

File tree

Projects/Presentation/Sources/Common/Component/RoutineCardView.swift

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,24 @@ import SnapKit
1010
import UIKit
1111

1212
final class RoutineCardView: UIView {
13+
private enum Layout {
14+
static let horizontalMargin: CGFloat = 16
15+
static let cornerRadius: CGFloat = 12
16+
static let headerInfoStackViewSpacing: CGFloat = 10
17+
static let headerInfoStackViewTopSpacing: CGFloat = 14
18+
static let subRoutineStackViewSpacing: CGFloat = 2
19+
static let subRoutineStackViewTopSpacing: CGFloat = 10
20+
static let subRoutineStackViewBottomSpacing: CGFloat = 14
21+
static let subRoutineLabelHeight: CGFloat = 20
22+
static let categoryIconSize: CGFloat = 32
23+
static let plusImageSize: CGFloat = 24
24+
static let plusButtonTopSpacing: CGFloat = 14
25+
static let plusButtonTrailingSpacing: CGFloat = 7
26+
static let plusButtonSize: CGFloat = 32
27+
static let grayLineTopSpacing: CGFloat = 10
28+
static let grayLineHeight: CGFloat = 1
29+
}
30+
1331
private let headerInfoStackView = UIStackView()
1432
private let categoryIconView = RoutineCategoryIcon(routineCategory: .connection)
1533
private let titleLabel = UILabel()
@@ -33,29 +51,30 @@ final class RoutineCardView: UIView {
3351
private func configureAttribute() {
3452
backgroundColor = .white
3553
layer.masksToBounds = true
36-
layer.cornerRadius = 12
54+
layer.cornerRadius = Layout.cornerRadius
3755

3856
headerInfoStackView.axis = .horizontal
39-
headerInfoStackView.spacing = 10
57+
headerInfoStackView.spacing = Layout.headerInfoStackViewSpacing
4058

4159
titleLabel.text = "개운하게 일어나기"
4260
titleLabel.font = BitnagilFont(style: .body1, weight: .semiBold).font
4361
titleLabel.textColor = BitnagilColor.gray10
4462

45-
let plusImage = BitnagilIcon.plusIcon?.resizeAspectFit(to: CGSize(width: 24, height: 24))
63+
let plusImage = BitnagilIcon.plusIcon?
64+
.resizeAspectFit(to: CGSize(width: Layout.plusImageSize, height: Layout.plusImageSize))
4665
plusButton.setImage(plusImage, for: .normal)
4766
plusButton.tintColor = BitnagilColor.gray10
4867

4968
grayLine.backgroundColor = BitnagilColor.gray97
5069

5170
subRoutineStackView.axis = .vertical
52-
subRoutineStackView.spacing = 2
71+
subRoutineStackView.spacing = Layout.subRoutineStackViewSpacing
5372

5473
subRoutineLabel.text = "세부 루틴"
5574
subRoutineLabel.font = BitnagilFont(style: .body2, weight: .medium).font
5675
subRoutineLabel.textColor = BitnagilColor.gray40
5776
subRoutineLabel.snp.makeConstraints { make in
58-
make.height.equalTo(20)
77+
make.height.equalTo(Layout.subRoutineLabelHeight)
5978
}
6079
subRoutineStackView.addArrangedSubview(subRoutineLabel)
6180

@@ -65,7 +84,7 @@ final class RoutineCardView: UIView {
6584
subRoutineTitleLabel.font = BitnagilFont(style: .body2, weight: .medium).font
6685
subRoutineTitleLabel.textColor = BitnagilColor.gray40
6786
subRoutineTitleLabel.snp.makeConstraints { make in
68-
make.height.equalTo(20)
87+
make.height.equalTo(Layout.subRoutineLabelHeight)
6988
}
7089
subRoutineStackView.addArrangedSubview(subRoutineTitleLabel)
7190
}
@@ -81,37 +100,42 @@ final class RoutineCardView: UIView {
81100
addSubview(subRoutineStackView)
82101

83102
categoryIconView.snp.makeConstraints { make in
84-
make.size.equalTo(32)
103+
make.size.equalTo(Layout.categoryIconSize)
85104
}
86105

87106
headerInfoStackView.snp.makeConstraints { make in
88-
make.top.equalToSuperview().offset(14)
89-
make.leading.equalToSuperview().offset(16)
107+
make.top.equalToSuperview().offset(Layout.headerInfoStackViewTopSpacing)
108+
make.leading.equalToSuperview().offset(Layout.horizontalMargin)
90109
}
91110

92111
plusButton.snp.makeConstraints { make in
93-
make.top.equalToSuperview().offset(14)
94-
make.trailing.equalToSuperview().inset(7)
95-
make.size.equalTo(32)
112+
make.top.equalToSuperview().offset(Layout.plusButtonTopSpacing)
113+
make.trailing.equalToSuperview().inset(Layout.plusButtonTrailingSpacing)
114+
make.size.equalTo(Layout.plusButtonSize)
96115
}
97116

98117
grayLine.snp.makeConstraints { make in
99-
make.top.equalTo(headerInfoStackView.snp.bottom).offset(10)
100-
make.leading.equalToSuperview().offset(16)
101-
make.trailing.equalToSuperview().inset(16)
102-
make.height.equalTo(1)
118+
make.top.equalTo(headerInfoStackView.snp.bottom).offset(Layout.grayLineTopSpacing)
119+
make.leading.equalToSuperview().offset(Layout.horizontalMargin)
120+
make.trailing.equalToSuperview().inset(Layout.horizontalMargin)
121+
make.height.equalTo(Layout.grayLineHeight)
103122
}
104123

105124
subRoutineStackView.snp.makeConstraints { make in
106-
make.top.equalTo(grayLine.snp.bottom).offset(10)
107-
make.leading.equalToSuperview().offset(16)
108-
make.trailing.equalToSuperview().inset(16)
109-
make.bottom.equalToSuperview().inset(14)
125+
make.top.equalTo(grayLine.snp.bottom).offset(Layout.subRoutineStackViewTopSpacing)
126+
make.leading.equalToSuperview().offset(Layout.horizontalMargin)
127+
make.trailing.equalToSuperview().inset(Layout.horizontalMargin)
128+
make.bottom.equalToSuperview().inset(Layout.subRoutineStackViewBottomSpacing)
110129
}
111130
}
112131
}
113132

114-
private class RoutineCategoryIcon: UIView {
133+
fileprivate class RoutineCategoryIcon: UIView {
134+
private enum Layout {
135+
static let cornerRadius: CGFloat = 3.76
136+
static let iconSize: CGFloat = 24
137+
}
138+
115139
private let routineCategoryIcon = UIImageView()
116140
private let routineCategory: RoutineCategoryType
117141
init(routineCategory: RoutineCategoryType) {
@@ -120,22 +144,22 @@ private class RoutineCategoryIcon: UIView {
120144
configureAttribute()
121145
configureLayout()
122146
}
123-
147+
124148
required init?(coder: NSCoder) {
125149
fatalError("init(coder:) has not been implemented")
126150
}
127151

128152
private func configureAttribute() {
129153
layer.masksToBounds = true
130-
layer.cornerRadius = 3.76
154+
layer.cornerRadius = Layout.cornerRadius
131155
backgroundColor = routineCategory.iconBackgroundColor ?? BitnagilColor.yellow10
132156
routineCategoryIcon.image = routineCategory.iconImage ?? BitnagilIcon.shineIcon
133157
}
134158

135159
private func configureLayout() {
136160
addSubview(routineCategoryIcon)
137161
routineCategoryIcon.snp.makeConstraints { make in
138-
make.size.equalTo(24)
162+
make.size.equalTo(Layout.iconSize)
139163
make.center.equalToSuperview()
140164
}
141165
}

Projects/Presentation/Sources/RecommendedRoutine/Model/RoutineCategoryType.swift renamed to Projects/Presentation/Sources/RecommendedRoutine/Model/RoutineCategoryType+.swift

File renamed without changes.

Projects/Presentation/Sources/RecommendedRoutine/Model/RoutineLevelType.swift renamed to Projects/Presentation/Sources/RecommendedRoutine/Model/RoutineLevelType+.swift

File renamed without changes.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// RecommendedRoutineEmptyView.swift
3+
// Presentation
4+
//
5+
// Created by 최정인 on 8/17/25.
6+
//
7+
8+
import SnapKit
9+
import UIKit
10+
11+
final class RecommendedRoutineEmptyView: UIView {
12+
private let stackView = UIStackView()
13+
private let mainLabel = UILabel()
14+
private let subLabel = UILabel()
15+
16+
init() {
17+
super.init(frame: .zero)
18+
configureAttribute()
19+
configureLayout()
20+
}
21+
22+
required init?(coder: NSCoder) {
23+
fatalError("init(coder:) has not been implemented")
24+
}
25+
26+
private func configureAttribute() {
27+
stackView.axis = .vertical
28+
stackView.spacing = 2
29+
30+
mainLabel.text = "해당 난이도 루틴이 없어요"
31+
mainLabel.font = BitnagilFont(style: .subtitle1, weight: .semiBold).font
32+
mainLabel.textColor = BitnagilColor.gray30
33+
mainLabel.textAlignment = .center
34+
35+
subLabel.text = "다른 난이도를 살펴보거나 루틴을 추가해 보세요."
36+
subLabel.font = BitnagilFont(style: .body2, weight: .regular).font
37+
subLabel.textColor = BitnagilColor.gray70
38+
subLabel.textAlignment = .center
39+
}
40+
41+
private func configureLayout() {
42+
addSubview(stackView)
43+
[mainLabel, subLabel].forEach {
44+
stackView.addArrangedSubview($0)
45+
}
46+
47+
stackView.snp.makeConstraints { make in
48+
make.edges.equalToSuperview()
49+
}
50+
51+
mainLabel.snp.makeConstraints { make in
52+
make.height.equalTo(28)
53+
}
54+
55+
subLabel.snp.makeConstraints { make in
56+
make.height.equalTo(20)
57+
}
58+
}
59+
}

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
2626
static let recommendedRoutineScrollViewBottomSpacing: CGFloat = 10
2727
static let recommendedRoutineStackViewBottomSpacing: CGFloat = 50
2828
static let routineCardHeight: CGFloat = 80
29+
static let registerEmotionButtonTopSpacing: CGFloat = 20
2930
static let registerEmotionButtonHeight: CGFloat = 66
3031
static let floatingButtonBottomSpacing: CGFloat = 19
3132
static let floatingButtonSize: CGFloat = 52
@@ -41,11 +42,11 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
4142
private let levelButton = RoutineLevelButton()
4243
private let levelView = SelectableItemTableView<RoutineLevelType>(items: RoutineLevelType.allCases.sorted(by: { $0.id < $1.id }))
4344

44-
private let testRoutineView = RoutineCardView()
4545
private let recommendedRoutineScrollView = UIScrollView()
4646
private let recommendedRoutineStackView = UIStackView()
4747
private var recommendedRoutineCards: [Int: RecommendedRoutineCardView] = [:]
4848
private let registerEmotionButton = RegisterEmotionButtonView()
49+
private let recommendedRoutineEmptyView = RecommendedRoutineEmptyView()
4950

5051
private var isExistEmotion: Bool = false
5152
private var isShowingFloatingMenu: Bool = false
@@ -104,6 +105,8 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
104105

105106
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tappedDimmedView))
106107
dimmedView.addGestureRecognizer(tapGesture)
108+
109+
recommendedRoutineEmptyView.isHidden = true
107110
}
108111

109112
override func configureLayout() {
@@ -118,6 +121,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
118121
[routineLabel, levelButton].forEach {
119122
headerStackView.addArrangedSubview($0)
120123
}
124+
view.addSubview(recommendedRoutineEmptyView)
121125
view.addSubview(recommendedRoutineScrollView)
122126
recommendedRoutineScrollView.addSubview(recommendedRoutineStackView)
123127

@@ -133,9 +137,9 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
133137
}
134138

135139
registerEmotionButton.snp.makeConstraints { make in
136-
make.leading.equalToSuperview().offset(20)
137-
make.trailing.equalToSuperview().inset(20)
138-
make.top.equalTo(categoryView.snp.bottom).offset(20)
140+
make.leading.equalToSuperview().offset(Layout.horizontalMargin)
141+
make.trailing.equalToSuperview().inset(Layout.horizontalMargin)
142+
make.top.equalTo(categoryView.snp.bottom).offset(Layout.registerEmotionButtonTopSpacing)
139143
make.height.equalTo(Layout.registerEmotionButtonHeight)
140144
}
141145

@@ -154,6 +158,11 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
154158
make.trailing.equalTo(headerStackView)
155159
}
156160

161+
recommendedRoutineEmptyView.snp.makeConstraints { make in
162+
make.centerX.equalToSuperview()
163+
make.centerY.equalToSuperview().offset(Layout.headerStackViewHeight)
164+
}
165+
157166
recommendedRoutineScrollView.snp.makeConstraints { make in
158167
make.leading.equalTo(safeArea).offset(Layout.horizontalMargin)
159168
make.trailing.equalTo(safeArea).inset(Layout.horizontalMargin)
@@ -222,8 +231,9 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
222231
}
223232
recommendedRoutineCards.removeAll()
224233

225-
let testView = RoutineCardView()
226-
recommendedRoutineStackView.addArrangedSubview(testView)
234+
// let testView = RoutineCardView()
235+
// recommendedRoutineStackView.addArrangedSubview(testView)
236+
recommendedRoutineEmptyView.isHidden = !recommendedRoutines.isEmpty
227237
for routine in recommendedRoutines {
228238
let routineCard = RecommendedRoutineCardView(recommendedRoutine: routine)
229239
recommendedRoutineCards[routine.id] = routineCard

0 commit comments

Comments
 (0)