1- import UIKit
2-
31import DesignSystem
42import Infrastructure
5-
63import ReactorKit
74import RxCocoa
85import RxSwift
96import SnapKit
7+ import UIKit
108
119final class AdminBottomSheetViewController : BaseViewController , View {
1210
1311 typealias Reactor = AdminBottomSheetReactor
1412
1513 // MARK: - Properties
1614 private let mainView = AdminBottomSheetView ( )
17- private let dimmedView = UIView ( )
15+ private lazy var dimmedView : UIView = {
16+ let view = UIView ( )
17+ view. backgroundColor = . black. withAlphaComponent ( 0.4 )
18+ view. alpha = 0
19+ return view
20+ } ( )
1821 var disposeBag = DisposeBag ( )
1922 private var containerViewBottomConstraint : Constraint ?
23+ private var containerHeightConstraint : Constraint ?
2024 private var tagSection : TagSection ?
2125
2226 var onSave : ( ( [ String ] ) -> Void ) ?
@@ -43,37 +47,30 @@ final class AdminBottomSheetViewController: BaseViewController, View {
4347 private func setupViews( ) {
4448 view. backgroundColor = . clear
4549
46- Logger . log ( " 초기 뷰 계층: " , category: . debug)
50+ view. addSubview ( dimmedView)
51+ dimmedView. snp. makeConstraints { make in
52+ make. edges. equalToSuperview ( )
53+ }
4754
4855 view. addSubview ( mainView)
49- mainView. isUserInteractionEnabled = true
50- mainView. containerView. isUserInteractionEnabled = true
51- mainView. closeButton. isUserInteractionEnabled = true
52- mainView. segmentedControl. isUserInteractionEnabled = true
53- mainView. headerView. isUserInteractionEnabled = true
54-
5556 mainView. snp. makeConstraints { make in
5657 make. left. right. equalToSuperview ( )
57- make. height. equalTo ( view . bounds . height * 0.45 )
58+ containerHeightConstraint = make. height. greaterThanOrEqualTo ( 400 ) . constraint
5859 containerViewBottomConstraint = make. bottom. equalTo ( view. snp. bottom) . constraint
5960 }
6061
61- Logger . log ( " mainView 추가 후 계층: " , category: . debug)
62-
63- dimmedView. backgroundColor = . black. withAlphaComponent ( 0.4 )
64- dimmedView. alpha = 0
65- dimmedView. isUserInteractionEnabled = false
62+ setupGestures ( )
63+ }
6664
67- let tapGesture = UITapGestureRecognizer ( target: self , action: #selector( dimmedViewTapped) )
65+ private func setupGestures( ) {
66+ let tapGesture = UITapGestureRecognizer ( target: self , action: #selector( handleTapDimmedView) )
67+ tapGesture. delegate = self
6868 dimmedView. addGestureRecognizer ( tapGesture)
69- tapGesture. cancelsTouchesInView = true // 터치 이벤트가 다른 뷰로 전달되도록 설정
70- view. insertSubview ( dimmedView, belowSubview: mainView)
71-
72- dimmedView. snp. makeConstraints { make in
73- make. edges. equalToSuperview ( )
74- }
69+ dimmedView. isUserInteractionEnabled = true
70+ }
7571
76- Logger . log ( " 최종 뷰 계층: " , category: . debug)
72+ @objc private func handleTapDimmedView( ) {
73+ hideBottomSheet ( )
7774 }
7875
7976 private func setupCollectionView( ) {
@@ -86,58 +83,58 @@ final class AdminBottomSheetViewController: BaseViewController, View {
8683 // MARK: - Binding
8784 func bind( reactor: Reactor ) {
8885 mainView. segmentedControl. rx. selectedSegmentIndex
89- . do ( onNext: { _ in
90- } )
91- . map { Reactor . Action. segmentChanged ( $0) }
92- . bind ( to: reactor. action)
93- . disposed ( by: disposeBag)
86+ . map { Reactor . Action. segmentChanged ( $0) }
87+ . bind ( to: reactor. action)
88+ . disposed ( by: disposeBag)
9489
9590 mainView. resetButton. rx. tap
9691 . map { Reactor . Action. resetFilters }
9792 . bind ( to: reactor. action)
9893 . disposed ( by: disposeBag)
9994
10095 mainView. contentCollectionView. rx. itemSelected
101- . withLatestFrom ( reactor. state) { indexPath, state -> Reactor . Action in
102- let title = state. activeSegment == 0 ?
103- state. statusOptions [ indexPath. item] :
104- state. categoryOptions [ indexPath. item]
105-
106- return state. activeSegment == 0 ?
107- . toggleStatusOption( title) :
108- . toggleCategoryOption( title)
109- }
110- . bind ( to: reactor. action)
111- . disposed ( by: disposeBag)
112-
113- reactor. state. map { state in
114- let items = state. activeSegment == 0 ?
115- state. statusOptions :
116- state. categoryOptions
117- let selectedItems = state. activeSegment == 0 ?
118- state. selectedStatusOptions :
119- state. selectedCategoryOptions
120-
121- return items. map {
122- TagSectionCell . Input (
123- title: $0,
124- isSelected: selectedItems. contains ( $0) ,
125- id: nil
126- )
96+ . withLatestFrom ( reactor. state) { indexPath, state -> Reactor . Action in
97+ let title = state. activeSegment == 0 ?
98+ state. statusOptions [ indexPath. item] :
99+ state. categoryOptions [ indexPath. item]
100+
101+ return state. activeSegment == 0 ?
102+ . toggleStatusOption( title) :
103+ . toggleCategoryOption( title)
127104 }
128- }
129- . bind ( to: mainView. contentCollectionView. rx. items (
130- cellIdentifier: TagSectionCell . identifiers,
131- cellType: TagSectionCell . self
132- ) ) { _, item, cell in
133- cell. injection ( with: item)
134- }
135- . disposed ( by: disposeBag)
105+ . bind ( to: reactor. action)
106+ . disposed ( by: disposeBag)
107+
108+ reactor. state
109+ . map { state in
110+ let items = state. activeSegment == 0 ?
111+ state. statusOptions :
112+ state. categoryOptions
113+ let selectedItems = state. activeSegment == 0 ?
114+ state. selectedStatusOptions :
115+ state. selectedCategoryOptions
116+
117+ return items. map {
118+ TagSectionCell . Input (
119+ title: $0,
120+ isSelected: selectedItems. contains ( $0) ,
121+ id: nil
122+ )
123+ }
124+ }
125+ . bind ( to: mainView. contentCollectionView. rx. items (
126+ cellIdentifier: TagSectionCell . identifiers,
127+ cellType: TagSectionCell . self
128+ ) ) { _, item, cell in
129+ cell. injection ( with: item)
130+ }
131+ . disposed ( by: disposeBag)
136132
133+ // 세그먼트 변경 시 전체 시트 높이 업데이트
137134 reactor. state. map { $0. activeSegment }
138135 . distinctUntilChanged ( )
139- . bind { [ weak self] index in
140- self ? . mainView . updateContentVisibility ( isCategorySelected : index == 1 )
136+ . bind { [ weak self] _ in
137+ self ? . updateContainerHeight ( )
141138 }
142139 . disposed ( by: disposeBag)
143140
@@ -167,7 +164,6 @@ final class AdminBottomSheetViewController: BaseViewController, View {
167164 }
168165 . disposed ( by: disposeBag)
169166
170- // View Events
171167 mainView. closeButton. rx. tap
172168 . bind { [ weak self] in
173169 self ? . hideBottomSheet ( )
@@ -189,13 +185,30 @@ final class AdminBottomSheetViewController: BaseViewController, View {
189185 . disposed ( by: disposeBag)
190186 }
191187
192- // MARK: - Actions
193- @objc private func dimmedViewTapped( ) {
194- hideBottomSheet ( )
188+ // MARK: - Height Management
189+ private func updateContainerHeight( ) {
190+ guard let reactor = reactor else { return }
191+
192+ let items = reactor. currentState. activeSegment == 0 ?
193+ reactor. currentState. statusOptions :
194+ reactor. currentState. categoryOptions
195+
196+ let collectionViewHeight = mainView. calculateCollectionViewHeight ( for: items)
197+
198+ let totalHeight = 60 + 50 + collectionViewHeight + 80 + 52 + 100
199+
200+ let finalHeight = min ( max ( totalHeight, 400 ) , UIScreen . main. bounds. height * 0.8 )
201+
202+ containerHeightConstraint? . update ( offset: finalHeight)
203+
204+ self . view. layoutIfNeeded ( )
195205 }
196206
197207 // MARK: - Show/Hide
198208 func showBottomSheet( ) {
209+ // 초기 높이 설정
210+ updateContainerHeight ( )
211+
199212 UIView . animate ( withDuration: 0.25 , delay: 0 , options: . curveEaseOut) {
200213 self . dimmedView. alpha = 1
201214 self . containerViewBottomConstraint? . update ( offset: 0 )
@@ -218,3 +231,13 @@ final class AdminBottomSheetViewController: BaseViewController, View {
218231 Logger . log ( " BottomSheet deinit " , category: . debug)
219232 }
220233}
234+
235+ extension AdminBottomSheetViewController : UIGestureRecognizerDelegate {
236+ func gestureRecognizer( _ gestureRecognizer: UIGestureRecognizer , shouldReceive touch: UITouch ) -> Bool {
237+ if gestureRecognizer. view == dimmedView {
238+ let touchPoint = touch. location ( in: view)
239+ return !mainView. containerView. frame. contains ( touchPoint)
240+ }
241+ return true
242+ }
243+ }
0 commit comments