77
88import UIKit
99import SwiftUI
10+ import Base
1011import FeedInterface
1112import ReactorKit
1213import DesignSystem
13- import SnapKit
14+ import RxCocoa
1415
1516public final class FeedRecordViewController : UIViewController , ReactorKit . View {
1617 public var disposeBag = DisposeBag ( )
1718
18- private let navigationBarContainerView = UIView ( )
19- private let contentLabel = MUILabel (
20- style : . b3 ,
21- color : . gray10
22- )
19+ private let recordType : FeedRecordType
20+ private let recordView : FeedRecordView
21+
22+ private var finishButtonHostingController : UIHostingController < FeedRecordFinishButtonView > ?
23+ var photoSourceSheetViewController : UIViewController ?
2324
2425 public init ( reactor: FeedRecordReactor ) {
26+ let recordType = reactor. initialState. recordType
27+ self . recordType = recordType
28+ self . recordView = FeedRecordView ( recordType: recordType)
2529 defer { self . reactor = reactor }
2630 super. init ( nibName: nil , bundle: nil )
2731 }
@@ -31,67 +35,171 @@ public final class FeedRecordViewController: UIViewController, ReactorKit.View {
3135 fatalError ( " init(coder:) has not been implemented " )
3236 }
3337
38+ public override func loadView( ) {
39+ view = recordView
40+ }
41+
3442 public override func viewDidLoad( ) {
3543 super. viewDidLoad ( )
3644
37- setupUI ( )
38- setupLayout ( )
3945 configureNavigationBar ( )
46+ configureRecordTypeContent ( )
4047 }
4148
4249 public func bind( reactor: FeedRecordReactor ) {
50+ recordView. uploadView. rx. controlEvent ( . touchUpInside)
51+ . map { FeedRecordReactor . Action. didTapPhotoUpload }
52+ . bind ( to: reactor. action)
53+ . disposed ( by: disposeBag)
54+
55+ recordView. exerciseSelectionField. rx. controlEvent ( . touchUpInside)
56+ . map { FeedRecordReactor . Action. didTapExerciseMenu }
57+ . bind ( to: reactor. action)
58+ . disposed ( by: disposeBag)
59+
60+ recordView. exerciseMenuView. onSelect = { [ weak reactor] type in
61+ reactor? . action. onNext ( . didSelectExerciseType( type) )
62+ }
63+
64+ recordView. exerciseSelectionField. onCustomTextChanged = { [ weak reactor] text in
65+ reactor? . action. onNext ( . didChangeCustomExerciseName( text) )
66+ }
67+
4368 reactor. state
44- . map ( \. recordType . title )
69+ . map ( \. isPhotoSourceSheetPresented )
4570 . distinctUntilChanged ( )
4671 . observe ( on: MainScheduler . instance)
47- . bind ( with: self ) { owner, title in
48- owner. contentLabel . text = " \( title ) 화면 "
72+ . bind ( with: self ) { owner, isPresented in
73+ owner. setPhotoSourceSheetPresented ( isPresented )
4974 }
5075 . disposed ( by: disposeBag)
51- }
52- }
5376
54- private extension FeedRecordViewController {
55- func setupUI( ) {
56- view. backgroundColor = . systemWhite
57- contentLabel. textAlignment = . center
58- }
59-
60- func setupLayout( ) {
61- view. addSubview ( navigationBarContainerView)
62- view. addSubview ( contentLabel)
77+ reactor. state
78+ . map ( \. isFinishButtonEnabled)
79+ . distinctUntilChanged ( )
80+ . observe ( on: MainScheduler . instance)
81+ . bind ( with: self ) { owner, isEnabled in
82+ owner. configureFinishButton ( isEnabled: isEnabled)
83+ }
84+ . disposed ( by: disposeBag)
6385
64- navigationBarContainerView. snp. makeConstraints {
65- $0. top. equalTo ( view. safeAreaLayoutGuide)
66- $0. leading. trailing. equalToSuperview ( )
67- }
86+ guard recordType == . exercise else { return }
6887
69- contentLabel. snp. makeConstraints {
70- $0. center. equalToSuperview ( )
71- }
88+ reactor. state
89+ . map { state in
90+ FeedRecordExerciseInputViewState (
91+ selectedType: state. selectedExerciseType,
92+ customExerciseName: state. customExerciseName,
93+ isExpanded: state. isExerciseMenuExpanded
94+ )
95+ }
96+ . distinctUntilChanged ( )
97+ . observe ( on: MainScheduler . instance)
98+ . bind ( with: self ) { owner, viewState in
99+ owner. recordView. configureExerciseInput ( viewState)
100+ }
101+ . disposed ( by: disposeBag)
72102 }
103+ }
73104
105+ private extension FeedRecordViewController {
74106 func configureNavigationBar( ) {
75- guard let recordType = reactor? . currentState. recordType else { return }
76-
77107 let navigationBar = MNavigationBar (
78108 title: recordType. title,
79109 onBackTap: { [ weak self] in
80110 self ? . reactor? . action. onNext ( . didTapBackButton)
81111 }
82112 )
83- let hostingController = UIHostingController ( rootView: navigationBar)
113+ addHostedView (
114+ navigationBar,
115+ to: recordView. navigationBarContainerView,
116+ sizesToContent: true
117+ )
118+ }
119+
120+ func configureRecordTypeContent( ) {
121+ switch recordType {
122+ case . meal:
123+ configureMealTextField ( )
124+ configureMealTimePicker ( )
125+ case . exercise:
126+ configureExerciseDurationPicker ( )
127+ }
128+
129+ configureFinishButton ( isEnabled: false )
130+ }
131+ }
84132
85- setupNavigationBarLayout ( hostingController)
86- hostingController. didMove ( toParent: self )
133+ private extension FeedRecordViewController {
134+ func configureMealTextField( ) {
135+ let textField = FeedRecordMealTextFieldView (
136+ text: reactor? . currentState. mealMenu ?? " " ,
137+ placeholder: " 메뉴를 간단하게 입력해주세요 " ,
138+ onTextChanged: { [ weak self] text in
139+ self ? . reactor? . action. onNext ( . didChangeMealMenu( text) )
140+ }
141+ )
142+ addHostedView (
143+ textField,
144+ to: recordView. inputFieldContainerView,
145+ sizesToContent: true
146+ )
87147 }
148+
149+ func configureMealTimePicker( ) {
150+ let timePicker = FeedRecordMealTimePickerView (
151+ date: reactor? . currentState. mealTime ?? Date ( ) ,
152+ onDateChanged: { [ weak self] date in
153+ self ? . reactor? . action. onNext ( . didChangeMealTime( date) )
154+ }
155+ )
156+ addHostedView (
157+ timePicker,
158+ to: recordView. timePickerContainerView,
159+ sizesToContent: true
160+ )
161+ }
162+ }
163+
164+ private extension FeedRecordViewController {
165+ func configureExerciseDurationPicker( ) {
166+ let state = reactor? . currentState
167+ let durationPicker = FeedRecordExerciseDurationPickerView (
168+ hours: state? . exerciseHours ?? 0 ,
169+ minutes: state? . exerciseMinutes ?? 0 ,
170+ onDurationChanged: { [ weak self] hours, minutes in
171+ self ? . reactor? . action. onNext (
172+ . didChangeExerciseDuration( hours: hours, minutes: minutes)
173+ )
174+ }
175+ )
176+ addHostedView (
177+ durationPicker,
178+ to: recordView. timePickerContainerView,
179+ sizesToContent: true
180+ )
181+ }
182+ }
88183
89- func setupNavigationBarLayout( _ hostingController: UIHostingController < MNavigationBar > ) {
90- addChild ( hostingController)
91- navigationBarContainerView. addSubview ( hostingController. view)
184+ private extension FeedRecordViewController {
185+ func configureFinishButton( isEnabled: Bool ) {
186+ let rootView = FeedRecordFinishButtonView (
187+ isEnabled: isEnabled,
188+ onTap: { [ weak self] in
189+ self ? . reactor? . action. onNext ( . didTapFinishButton)
190+ }
191+ )
92192
93- hostingController. view. snp. makeConstraints {
94- $0. edges. equalToSuperview ( )
193+ if let finishButtonHostingController {
194+ finishButtonHostingController. rootView = rootView
195+ return
95196 }
197+
198+ let hostingController = addHostedView (
199+ rootView,
200+ to: recordView. finishButtonContainerView,
201+ sizesToContent: true
202+ )
203+ finishButtonHostingController = hostingController
96204 }
97205}
0 commit comments