Skip to content

Commit f2486b3

Browse files
authored
Feat: 제보 상세 화면 UI 구현 (#T3-197)
* Feat: FloatingMenu에 제보하기 버튼 추가 (#T3-197) * Feat: ReportDetailView UI 구현 (#T3-197) * Fix: ScrollView 추가 및 ReportType으로 타입 변경 * Feat: Xcode 업데이트로 인한 Tuist 설정 일부 수정 * Fix: Tuist 설정 롤백
1 parent 87035d6 commit f2486b3

13 files changed

Lines changed: 410 additions & 36 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "report_icon.png",
5+
"idiom" : "universal",
6+
"scale" : "1x"
7+
},
8+
{
9+
"filename" : "report_icon@2x.png",
10+
"idiom" : "universal",
11+
"scale" : "2x"
12+
},
13+
{
14+
"filename" : "report_icon@3x.png",
15+
"idiom" : "universal",
16+
"scale" : "3x"
17+
}
18+
],
19+
"info" : {
20+
"author" : "xcode",
21+
"version" : 1
22+
}
23+
}
607 Bytes
Loading
1.03 KB
Loading
1.51 KB
Loading

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

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,47 @@ import SnapKit
99
import UIKit
1010

1111
protocol FloatingMenuViewDelegate: AnyObject {
12+
func floatingMenuDidTapReportButton(_ sender: FloatingMenuView)
1213
func floatingMenuDidTapRegisterRoutineButton(_ sender: FloatingMenuView)
1314
}
1415

1516
final class FloatingMenuView: UIView {
1617
private enum Layout {
17-
static let registerRoutineButtonHeight: CGFloat = 24
18-
static let registerRoutineIconSize: CGFloat = 24
19-
static let registerRoutineLabelLeadingSpacing: CGFloat = 14
20-
static let registerRoutineButtonWidth: CGFloat = 112
21-
static let registerRoutineLabelHeight: CGFloat = 20
18+
static let menuButtonStackViewSpacing: CGFloat = 24
19+
static let menuButtonHeight: CGFloat = 24
20+
static let menuIconSize: CGFloat = 24
21+
static let menuLabelLeadingSpacing: CGFloat = 14
22+
static let menuButtonWidth: CGFloat = 112
23+
static let menuLabelHeight: CGFloat = 20
24+
}
25+
26+
private enum FloatingMenu {
27+
case addRoutine
28+
case report
29+
30+
var menuIcon: UIImage? {
31+
switch self {
32+
case .addRoutine:
33+
return BitnagilIcon.addRoutineIcon
34+
case .report:
35+
return BitnagilIcon.reportIcon
36+
}
37+
}
38+
39+
var menuLabel: String {
40+
switch self {
41+
case .addRoutine:
42+
return "루틴 등록"
43+
case .report:
44+
return "제보하기"
45+
}
46+
}
2247
}
2348

2449
private let containerView = UIView()
25-
private let registerRoutineButton = UIButton()
26-
private let registerRoutineIconView = UIImageView()
27-
private let registerRoutineLabel = UILabel()
50+
private let menuButtonStackView = UIStackView()
51+
private var reportButton = UIButton()
52+
private var registerRoutineButton = UIButton()
2853
weak var delegate: FloatingMenuViewDelegate?
2954

3055
init() {
@@ -42,12 +67,18 @@ final class FloatingMenuView: UIView {
4267
containerView.layer.masksToBounds = true
4368
containerView.layer.cornerRadius = 12
4469

45-
registerRoutineIconView.image = BitnagilIcon.addRoutineIcon
70+
menuButtonStackView.axis = .vertical
71+
menuButtonStackView.spacing = Layout.menuButtonStackViewSpacing
4672

47-
registerRoutineLabel.text = "루틴 등록"
48-
registerRoutineLabel.font = BitnagilFont(style: .body2, weight: .medium).font
49-
registerRoutineLabel.textColor = BitnagilColor.gray30
73+
reportButton = makeMenuButton(menu: .report)
74+
reportButton.addAction(
75+
UIAction { [weak self] _ in
76+
guard let self else { return }
77+
self.delegate?.floatingMenuDidTapReportButton(self)
78+
},
79+
for: .touchUpInside)
5080

81+
registerRoutineButton = makeMenuButton(menu: .addRoutine)
5182
registerRoutineButton.addAction(
5283
UIAction { [weak self] _ in
5384
guard let self else { return }
@@ -58,31 +89,56 @@ final class FloatingMenuView: UIView {
5889

5990
private func configureLayout() {
6091
addSubview(containerView)
61-
containerView.addSubview(registerRoutineButton)
62-
[registerRoutineIconView, registerRoutineLabel].forEach {
63-
registerRoutineButton.addSubview($0)
92+
[reportButton, registerRoutineButton].forEach { menuButton in
93+
menuButtonStackView.addArrangedSubview(menuButton)
6494
}
95+
containerView.addSubview(menuButtonStackView)
6596

6697
containerView.snp.makeConstraints { make in
6798
make.edges.equalToSuperview()
6899
}
69100

101+
menuButtonStackView.snp.makeConstraints { make in
102+
make.edges.equalToSuperview().inset(16)
103+
}
104+
105+
reportButton.snp.makeConstraints { make in
106+
make.height.equalTo(Layout.menuButtonHeight)
107+
make.width.equalTo(Layout.menuButtonWidth)
108+
}
109+
70110
registerRoutineButton.snp.makeConstraints { make in
71-
make.center.equalToSuperview()
72-
make.height.equalTo(Layout.registerRoutineButtonHeight)
73-
make.width.equalTo(Layout.registerRoutineButtonWidth)
111+
make.height.equalTo(Layout.menuButtonHeight)
112+
make.width.equalTo(Layout.menuButtonWidth)
113+
}
114+
}
115+
116+
private func makeMenuButton(menu: FloatingMenu) -> UIButton {
117+
let menuButton = UIButton()
118+
let menuIconView = UIImageView()
119+
let menuLabel = UILabel()
120+
121+
menuIconView.image = menu.menuIcon
122+
menuLabel.text = menu.menuLabel
123+
menuLabel.font = BitnagilFont(style: .body2, weight: .medium).font
124+
menuLabel.textColor = BitnagilColor.gray30
125+
126+
[menuIconView, menuLabel].forEach {
127+
menuButton.addSubview($0)
74128
}
75129

76-
registerRoutineIconView.snp.makeConstraints { make in
130+
menuIconView.snp.makeConstraints { make in
77131
make.centerY.equalToSuperview()
78132
make.leading.equalToSuperview()
79-
make.size.equalTo(Layout.registerRoutineIconSize)
133+
make.size.equalTo(Layout.menuIconSize)
80134
}
81135

82-
registerRoutineLabel.snp.makeConstraints { make in
83-
make.leading.equalTo(registerRoutineIconView.snp.trailing).offset(Layout.registerRoutineLabelLeadingSpacing)
136+
menuLabel.snp.makeConstraints { make in
137+
make.leading.equalTo(menuIconView.snp.trailing).offset(Layout.menuLabelLeadingSpacing)
84138
make.centerY.equalToSuperview()
85-
make.height.equalTo(Layout.registerRoutineLabelHeight)
139+
make.height.equalTo(Layout.menuLabelHeight)
86140
}
141+
142+
return menuButton
87143
}
88144
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ enum BitnagilIcon {
1515
// MARK: - Common Icons
1616
static let backButtonIcon = UIImage(named: "back_button_icon", in: bundle, with: nil)
1717
static let plusIcon = UIImage(named: "plus_icon", in: bundle, with: nil)?.withRenderingMode(.alwaysTemplate)
18+
static let reportIcon = UIImage(named: "report_icon", in: bundle, with: nil)
1819
static let bitnagilChevronIcon = UIImage(named: "bitnagil_chevron_icon", in: bundle, with: nil)
1920
static func bitnagilChevronIcon(direction: Direction) -> UIImage? {
2021
return BitnagilIcon.bitnagilChevronIcon?.rotate(degrees: direction.rotation)?.withRenderingMode(.alwaysTemplate)

Projects/Presentation/Sources/Common/PresentationDependencyAssembler.swift

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

128128
return ReportViewModel(reportUseCase: reportUseCase)
129129
}
130+
131+
DIContainer.shared.register(type: ReportDetailViewModel.self) { container in
132+
return ReportDetailViewModel()
133+
}
130134
}
131135
}

Projects/Presentation/Sources/Home/View/HomeViewController.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ final class HomeViewController: BaseViewController<HomeViewModel> {
4545
static let floatingButtonBottomSpacing: CGFloat = 19
4646
static let floatingButtonSize: CGFloat = 52
4747
static let floatingMenuBottomSpacing: CGFloat = 16
48-
static let floatingMenuHeight: CGFloat = 56
48+
static let floatingMenuHeight: CGFloat = 104
4949
static let floatingMenuWidth: CGFloat = 144
5050
}
5151

@@ -664,6 +664,18 @@ extension HomeViewController: RoutineViewDelegate {
664664

665665
// MARK: FloatingMenuViewDelegate
666666
extension HomeViewController: FloatingMenuViewDelegate {
667+
func floatingMenuDidTapReportButton(_ sender: FloatingMenuView) {
668+
toggleFloatingButton()
669+
// TODO: 제보하기 뷰로 이동 (현재는 제보 detailView)
670+
guard let reportDetailViewModel = DIContainer.shared.resolve(type: ReportDetailViewModel.self)
671+
else { fatalError("reportDetailViewModel 의존성이 등록되지 않았습니다.") }
672+
673+
let reportDetailViewController = ReportDetailViewController(viewModel: reportDetailViewModel)
674+
reportDetailViewController.hidesBottomBarWhenPushed = true
675+
676+
self.navigationController?.pushViewController(reportDetailViewController, animated: true)
677+
}
678+
667679
func floatingMenuDidTapRegisterRoutineButton(_ sender: FloatingMenuView) {
668680
toggleFloatingButton()
669681
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self) else {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ final class RecommendedRoutineViewController: BaseViewController<RecommendedRout
3030
static let floatingButtonBottomSpacing: CGFloat = 19
3131
static let floatingButtonSize: CGFloat = 52
3232
static let floatingMenuBottomSpacing: CGFloat = 15
33-
static let floatingMenuHeight: CGFloat = 64
33+
static let floatingMenuHeight: CGFloat = 104
3434
static let floatingMenuWidth: CGFloat = 144
3535
static let toastMessageViewHeight: CGFloat = 52
3636
static let toastMessageViewBottomSpacing: CGFloat = 38
@@ -335,6 +335,11 @@ extension RecommendedRoutineViewController: SelectableItemTableViewDelegate {
335335

336336
// MARK: FloatingMenuViewDelegate
337337
extension RecommendedRoutineViewController: FloatingMenuViewDelegate {
338+
func floatingMenuDidTapReportButton(_ sender: FloatingMenuView) {
339+
toggleFloatingButton()
340+
// TODO: 제보하기 뷰로 이동
341+
}
342+
338343
func floatingMenuDidTapRegisterRoutineButton(_ sender: FloatingMenuView) {
339344
toggleFloatingButton()
340345
guard let routineCreationViewModel = DIContainer.shared.resolve(type: RoutineCreationViewModel.self)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// ReportDetail.swift
3+
// Presentation
4+
//
5+
// Created by 최정인 on 11/18/25.
6+
//
7+
8+
import Domain
9+
10+
struct ReportDetail {
11+
let date: String
12+
let title: String
13+
let category: ReportType
14+
let description: String
15+
let location: String
16+
}

0 commit comments

Comments
 (0)