From 523821851809b6e35d78a93a8b8249a64f24b536 Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:05:25 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[Fix]=20#249=20-=20=EC=A7=81=EB=AC=B4=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EC=95=88=ED=95=B4=EB=8F=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=EC=9D=B4=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=EB=90=98=EC=96=B4=20=EC=9E=88?= =?UTF-8?q?=EC=9D=8C=20->=20=ED=95=AD=EC=83=81=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=8A=94=20=EC=84=A0=ED=83=9D=EB=90=98=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JobFilteringViewController.swift | 14 ++++---------- .../ViewModel/JobFilteringViewModel.swift | 8 ++------ 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewController/JobFilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewController/JobFilteringViewController.swift index e4b3b64..574d1ba 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewController/JobFilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewController/JobFilteringViewController.swift @@ -90,16 +90,10 @@ extension JobFilteringViewController { output.selectedJobType .drive(onNext: { [weak self] selectedJob in - guard let self = self else { return } - if let selectedJob = selectedJob, - let index = JobType.allCases.firstIndex(of: selectedJob) { - let indexPath = IndexPath(item: index, section: 0) - self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically) - } else { - self.collectionView.indexPathsForSelectedItems?.forEach { - self.collectionView.deselectItem(at: $0, animated: false) - } - } + guard let self = self, let selectedJob = selectedJob, + let index = JobType.allCases.firstIndex(of: selectedJob) else { return } + let indexPath = IndexPath(item: index, section: 0) + self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically) }) .disposed(by: disposeBag) } diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewModel/JobFilteringViewModel.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewModel/JobFilteringViewModel.swift index e54668e..21c3294 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewModel/JobFilteringViewModel.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/JobFiltering/ViewModel/JobFilteringViewModel.swift @@ -15,7 +15,7 @@ final class JobFilteringViewModel: ViewModelType { // MARK: - Properties private let jobTypesRelay = BehaviorRelay<[JobType]>(value: JobType.allCases) - private let selectedJobTypeRelay = BehaviorRelay(value: UserFilteringData.shared.jobType) + private let selectedJobTypeRelay = BehaviorRelay(value: UserFilteringData.shared.jobType ?? JobType.allCases.last) // MARK: - Input @@ -38,11 +38,7 @@ final class JobFilteringViewModel: ViewModelType { .subscribe(onNext: { [weak self] indexPath in guard let self = self else { return } let selectedJob = JobType.allCases[indexPath.item] - if self.selectedJobTypeRelay.value == selectedJob { - self.selectedJobTypeRelay.accept(nil) - } else { - self.selectedJobTypeRelay.accept(selectedJob) - } + self.selectedJobTypeRelay.accept(selectedJob) }) .disposed(by: disposeBag) From dc4aa39ad7263ee90a2ad13f0ebd32e7b6a649cf Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:10:43 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[Fix]=20#249=20-=20=EC=83=81=EB=8B=A8?= =?UTF-8?q?=EC=97=90=20=ED=95=84=ED=84=B0=EB=9D=BC=EB=8A=94=20=ED=85=8D?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/FilteringViewController.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift index 84f7da0..4210196 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift @@ -54,6 +54,11 @@ final class FilteringViewController: UIViewController { $0.isUserInteractionEnabled = true } + private var titleLabel = LabelFactory.build( + text: "필터", + font: .title2 + ) + private var segmentControl: CustomSegmentedControl = { let underbarInfo = UnderbarInfo( height: 4, @@ -123,6 +128,7 @@ extension FilteringViewController { private func setHierarchy() { view.addSubviews( notchView, + titleLabel, segmentControl, underLineView, saveButton @@ -137,8 +143,13 @@ extension FilteringViewController { $0.height.equalTo(4.adjustedH) } - segmentControl.snp.makeConstraints { + titleLabel.snp.makeConstraints { $0.top.equalTo(notchView.snp.bottom).offset(24.adjustedH) + $0.leading.equalToSuperview().inset(25.adjusted) + } + + segmentControl.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(20.adjustedH) $0.leading.equalToSuperview().inset(20.adjusted) $0.height.equalTo(40.adjustedH) } From ce942ecfa55116f113541d2d46078cbb3ad81860 Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:13:42 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[Fix]=20#249=20-=20=EA=B3=84=ED=9A=8D?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=EB=B7=B0=20UILabel=20->LabelFactory?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlanFilteringViewController.swift | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift index 1a218d5..7ba1feb 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift @@ -24,21 +24,18 @@ final class PlanFilteringViewController: UIViewController { // MARK: - UI Components - private let gradeTitleLabel = UILabel().then { - $0.text = "재학 상태" - $0.font = .systemFont(ofSize: 16, weight: .bold) - $0.textColor = .darkGray - } - private let periodTitleLabel = UILabel().then { - $0.text = "희망 근무 기간" - $0.font = .systemFont(ofSize: 16, weight: .bold) - $0.textColor = .darkGray - } - private let dateTitleLabel = UILabel().then { - $0.text = "근무 시작 시기" - $0.font = .systemFont(ofSize: 16, weight: .bold) - $0.textColor = .darkGray - } + private var gradeTitleLabel = LabelFactory.build( + text: "재학 상태", + font: .title4 + ) + private var periodTitleLabel = LabelFactory.build( + text: "희망 근무 기간", + font: .title4 + ) + private var dateTitleLabel = LabelFactory.build( + text: "근무 시작 시기", + font: .title4 + ) private lazy var gradeButtons = createButtonGroup(titles: Grade.allCases.map { $0.displayName }, section: 0) private lazy var periodButtons = createButtonGroup(titles: WorkingPeriod.allCases.map { $0.displayName }, section: 1) private var customPickerView = CustomDatePicker() @@ -82,7 +79,7 @@ extension PlanFilteringViewController { } private func setLayout() { gradeTitleLabel.snp.makeConstraints { - $0.top.horizontalEdges.equalToSuperview() + $0.top.leading.equalToSuperview() } gradeButtons.snp.makeConstraints { $0.top.equalTo(gradeTitleLabel.snp.bottom).offset(12.adjustedH) From 25915c0227444f2412d0b19998c5956c475d983b Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 04:24:13 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[Fix]=20#249=20-=20=EA=B3=84=ED=9A=8D=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20=EA=B4=80=EB=A0=A8=20QA=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 처음 체크박스를 누른 후 데이트피커만 움직이면 저장이 활성화 되는 오류 해결 - 건너뛰기 직후 계획 필터링에 진입했을 때 체크박스가 체크되어 있지 않은 오류 해결 --- .../FilteringViewController.swift | 12 +- .../PlanFilteringViewController.swift | 122 +++++++----------- .../ViewModel/PlanFilteringViewModel.swift | 74 ++++++++--- 3 files changed, 107 insertions(+), 101 deletions(-) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift index 4210196..3df8855 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift @@ -18,8 +18,8 @@ final class UserFilteringData { var grade: Grade? var workingPeriod: WorkingPeriod? - var startYear: Int? = Date().getCurrentKrYearAndMonth().year - var startMonth: Int? = Date().getCurrentKrYearAndMonth().month + var startYear: Int? + var startMonth: Int? var jobType: JobType? private init() {} @@ -30,8 +30,8 @@ final class TemporaryFilteringData { var grade: Grade? var workingPeriod: WorkingPeriod? - var startYear: Int? = Date().getCurrentKrYearAndMonth().year - var startMonth: Int? = Date().getCurrentKrYearAndMonth().month + var startYear: Int? + var startMonth: Int? var jobType: JobType? private init() {} @@ -89,8 +89,8 @@ final class FilteringViewController: UIViewController { init(viewModel: FilteringViewModel, data: UserFilteringInfoModel) { UserFilteringData.shared.grade = data.grade.flatMap { Grade(rawValue: $0) ?? Grade.fromEnglishValue($0) } UserFilteringData.shared.workingPeriod = data.workingPeriod.flatMap { WorkingPeriod(rawValue: $0) ?? WorkingPeriod.fromEnglishValue($0) } - UserFilteringData.shared.startYear = data.startYear ?? UserFilteringData.shared.startYear - UserFilteringData.shared.startMonth = data.startMonth ?? UserFilteringData.shared.startMonth + UserFilteringData.shared.startYear = (data.startYear == 0) ? nil : data.startYear + UserFilteringData.shared.startMonth = (data.startMonth == 0) ? nil : data.startMonth UserFilteringData.shared.jobType = data.jobType.flatMap { JobType(rawValue: $0) ?? JobType.fromEnglishValue($0) } self.viewModel = viewModel diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift index 7ba1feb..27ba9fb 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewController/PlanFilteringViewController.swift @@ -20,7 +20,6 @@ final class PlanFilteringViewController: UIViewController { private let viewModel: PlanFilteringViewModel private let disposeBag = DisposeBag() var filteringState = BehaviorRelay(value: false) - private var selectedButtons: [CustomOnboardingButton?] = [nil, nil] // MARK: - UI Components @@ -135,38 +134,37 @@ extension PlanFilteringViewController { private func bindViewModel() { let gradeSelected = Observable.merge( gradeButtons.arrangedSubviews.compactMap { $0 as? CustomOnboardingButton }.map { button in - button.rx.tap.map { [weak self] in - self?.updateSelectedButton(section: 0, sender: button) - return self?.selectedButtons[0] != nil ? Grade.allCases[button.tag % 10] : nil - } + button.rx.tap.map { Grade.allCases[button.tag % 10] } } ) + let periodSelected = Observable.merge( periodButtons.arrangedSubviews.compactMap { $0 as? CustomOnboardingButton }.map { button in - button.rx.tap.map { [weak self] in - self?.updateSelectedButton(section: 1, sender: button) - return self?.selectedButtons[1] != nil ? WorkingPeriod.allCases[button.tag % 10] : nil - } + button.rx.tap.map { WorkingPeriod.allCases[button.tag % 10] } } ) + let dateSelected = Observable<(Int?, Int?)>.create { [weak self] observer -> Disposable in self?.customPickerView.onDateSelected = { year, month in - self?.updateCheckBoxState() observer.onNext((year, month)) } return Disposables.create() } .observe(on: MainScheduler.asyncInstance) .share() - - let checkBoxToggled: Observable = Observable.create { [weak self] observer -> Disposable in + + let checkBoxToggled = Observable.create { [weak self] observer -> Disposable in self?.checkBox.action = { isChecked in - self?.didTapCheckBox(isChecked: isChecked) observer.onNext(isChecked) + if isChecked { + self?.customPickerView.addPlaceholder() + self?.customPickerView.removePlaceholder() + } } return Disposables.create() } - + .observe(on: MainScheduler.asyncInstance) + let input = PlanFilteringViewModel.Input( gradeSelected: gradeSelected, periodSelected: periodSelected, @@ -177,26 +175,44 @@ extension PlanFilteringViewController { let output = viewModel.transform(input: input, disposeBag: disposeBag) + Observable.combineLatest(output.selectedYear.asObservable(), output.selectedMonth.asObservable()) + .take(1) + .subscribe(onNext: { [weak self] year, month in + guard let self = self else { return } + + if let year = year, let month = month, year > 0, month > 0 { + self.customPickerView.setInitialDate(year: year, month: month) + } else { + self.customPickerView.addPlaceholder() + } + }) + .disposed(by: disposeBag) + output.selectedGrade .drive(onNext: { [weak self] grade in - guard let self = self, let grade = grade else { return } - self.updateButtonState(for: self.gradeButtons, selectedValue: grade.displayName, section: 0) + guard let self = self else { return } + if let grade = grade { + self.updateButtonState(for: self.gradeButtons, selectedValue: grade.displayName) + } else { + self.updateButtonState(for: self.gradeButtons, selectedValue: nil) + } }) .disposed(by: disposeBag) - + output.selectedPeriod .drive(onNext: { [weak self] period in - guard let self = self, let period = period else { return } - self.updateButtonState(for: self.periodButtons, selectedValue: period.displayName, section: 1) + guard let self = self else { return } + if let period = period { + self.updateButtonState(for: self.periodButtons, selectedValue: period.displayName) + } else { + self.updateButtonState(for: self.periodButtons, selectedValue: nil) + } }) .disposed(by: disposeBag) - - Observable - .combineLatest(output.selectedYear.asObservable(), output.selectedMonth.asObservable()) - .take(1) - .subscribe(onNext: { [weak self] year, month in - guard let self = self, let year = year, let month = month else { return } - self.customPickerView.setInitialDate(year: year, month: month) + + output.isCheckBoxChecked + .drive(onNext: { [weak self] isChecked in + self?.checkBox.setChecked(isChecked) }) .disposed(by: disposeBag) @@ -205,59 +221,11 @@ extension PlanFilteringViewController { .disposed(by: disposeBag) } - private func updateButtonState(for buttonGroup: UIStackView, selectedValue: String?, section: Int) { + private func updateButtonState(for buttonGroup: UIStackView?, selectedValue: String?) { + guard let buttonGroup = buttonGroup else { return } buttonGroup.arrangedSubviews.compactMap { $0 as? CustomOnboardingButton }.forEach { button in let isSelected = (button.originalTitle == selectedValue) - if isSelected { - button.selectButton() - selectedButtons[section] = button - } else { - button.deselectButton() - } - } - } - - private func updateCheckBoxState() { - let hasSelectedButton = selectedButtons.contains { $0 != nil } - let pickerHasValidSelection = { - let selectedYearRow = customPickerView.selectedRow(inComponent: 0) - let selectedMonthRow = customPickerView.selectedRow(inComponent: 1) - - let yearIsValid = selectedYearRow >= 0 && selectedYearRow < customPickerView.years.count && customPickerView.years[selectedYearRow] != "-" - let monthIsValid = selectedMonthRow >= 0 && selectedMonthRow < customPickerView.months.count && customPickerView.months[selectedMonthRow] != "-" - return yearIsValid || monthIsValid - }() - if hasSelectedButton || pickerHasValidSelection { - checkBox.setChecked(false) - } - } - - private func updateSelectedButton(section: Int, sender: CustomOnboardingButton) { - if selectedButtons[section] == sender { - selectedButtons[section]?.deselectButton() - selectedButtons[section] = nil - } else { - selectedButtons[section]?.deselectButton() - sender.selectButton() - selectedButtons[section] = sender - } - updateCheckBoxState() - } -} - -// MARK: - @objc func - -extension PlanFilteringViewController { - @objc - private func didTapCheckBox(isChecked: Bool) { - if isChecked { - selectedButtons.forEach { button in - button?.deselectButton() - } - selectedButtons = [nil, nil] - - customPickerView.addPlaceholder() - customPickerView.removePlaceholder() + isSelected ? button.selectButton() : button.deselectButton() } } } diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift index ea17d96..54df0d0 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift @@ -13,12 +13,14 @@ final class PlanFilteringViewModel: ViewModelType { // MARK: - Properties - private let isFilterAppliedRelay = BehaviorRelay(value: false) + private let hasNonNilValueRelay = BehaviorRelay(value: false) + private let isCheckBoxRelay = BehaviorRelay(value: nil) + private let gradeRelay = BehaviorRelay(value: UserFilteringData.shared.grade) private let periodRelay = BehaviorRelay(value: UserFilteringData.shared.workingPeriod) private let yearRelay = BehaviorRelay(value: UserFilteringData.shared.startYear) private let monthRelay = BehaviorRelay(value: UserFilteringData.shared.startMonth) - private let checkBoxRelay = BehaviorRelay(value: false) + private let isFilterAppliedRelay = BehaviorRelay(value: false) // MARK: - Input @@ -37,6 +39,7 @@ final class PlanFilteringViewModel: ViewModelType { let selectedPeriod: Driver let selectedYear: Driver let selectedMonth: Driver + let isCheckBoxChecked: Driver let isFilterApplied: Driver } @@ -44,6 +47,9 @@ final class PlanFilteringViewModel: ViewModelType { func transform(input: Input, disposeBag: DisposeBag) -> Output { input.gradeSelected + .withLatestFrom(gradeRelay) { (newGrade, currentGrade) in + return newGrade == currentGrade ? nil : newGrade + } .subscribe(onNext: { grade in TemporaryFilteringData.shared.grade = grade self.gradeRelay.accept(grade) @@ -51,6 +57,9 @@ final class PlanFilteringViewModel: ViewModelType { .disposed(by: disposeBag) input.periodSelected + .withLatestFrom(periodRelay) { (newPeriod, currentPeriod) in + return newPeriod == currentPeriod ? nil : newPeriod + } .subscribe(onNext: { period in TemporaryFilteringData.shared.workingPeriod = period self.periodRelay.accept(period) @@ -63,7 +72,7 @@ final class PlanFilteringViewModel: ViewModelType { self.yearRelay.accept(year) }) .disposed(by: disposeBag) - + input.monthSelected .subscribe(onNext: { month in TemporaryFilteringData.shared.startMonth = month @@ -72,31 +81,60 @@ final class PlanFilteringViewModel: ViewModelType { .disposed(by: disposeBag) input.checkBoxToggled - .bind(to: checkBoxRelay) + .subscribe(onNext: { [weak self] isChecked in + guard let self = self else { return } + self.isCheckBoxRelay.accept(isChecked) + + if isChecked { + self.gradeRelay.accept(nil) + self.periodRelay.accept(nil) + self.yearRelay.accept(nil) + self.monthRelay.accept(nil) + self.hasNonNilValueRelay.accept(false) + } + }) .disposed(by: disposeBag) - Observable - .combineLatest(gradeRelay, periodRelay, yearRelay, monthRelay, checkBoxRelay) - .do(onNext: { grade, period, year, month, isChecked in - print("Grade: \(String(describing: grade))") - print("Period: \(String(describing: period))") - print("Year: \(String(describing: year))") - print("Month: \(String(describing: month))") - print("IsChecked: \(isChecked)") - }) - .map { grade, period, year, month, isChecked in - if isChecked { return true } + let combinedRelays = Observable + .combineLatest(gradeRelay, periodRelay, yearRelay, monthRelay) + + let isCheckBoxChecked = Observable + .combineLatest(combinedRelays, isCheckBoxRelay.asObservable()) + .map { [weak self] relays, isCheckBoxValue -> Bool in + guard let self = self else { return false } + let (grade, period, year, month) = relays + let isAllNil = grade == nil && period == nil && year == nil && month == nil + + if let isCheckBoxValue = isCheckBoxValue { + if !isCheckBoxValue { + return false + } + } + + if !isAllNil { + self.hasNonNilValueRelay.accept(true) + } + return isAllNil && !self.hasNonNilValueRelay.value + } + .distinctUntilChanged() + .share(replay: 1, scope: .forever) + + let isFilterApplied = Observable + .combineLatest(combinedRelays, isCheckBoxChecked) + .map { relays, isCheckBoxChecked in + let (grade, period, year, month) = relays + if isCheckBoxChecked { return true } return grade != nil && period != nil && year != nil && month != nil } - .bind(to: isFilterAppliedRelay) - .disposed(by: disposeBag) + .distinctUntilChanged() return Output( selectedGrade: gradeRelay.asDriver(), selectedPeriod: periodRelay.asDriver(), selectedYear: yearRelay.asDriver(), selectedMonth: monthRelay.asDriver(), - isFilterApplied: isFilterAppliedRelay.asDriver() + isCheckBoxChecked: isCheckBoxChecked.asDriver(onErrorJustReturn: false), + isFilterApplied: isFilterApplied.asDriver(onErrorJustReturn: false) ) } } From aa1eb8606514bf39cd2e5459a93453d1e19ab2b5 Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 06:48:51 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[Fix]=20#249=20-=20[=EC=A7=81=EB=AC=B4=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC]=20=EC=84=B9=EC=85=98?= =?UTF-8?q?=EA=B3=BC=20[=EA=B3=84=ED=9A=8D]=20=EC=84=B9=EC=85=98=20?= =?UTF-8?q?=EA=B0=84=EC=9D=98=20=EA=B0=84=EA=B2=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UIComponents/CustomSegmentedControl.swift | 131 ++++++++++++++---- .../FilteringViewController.swift | 10 +- 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift b/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift index 8639661..fea36e8 100644 --- a/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift +++ b/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift @@ -23,15 +23,21 @@ final class CustomSegmentedControl: UISegmentedControl { private lazy var underbar: UIView = makeUnderbar() private var underbarInfo: UnderbarInfo private var isFirstSettingDone = false + private var itemSpacing: CGFloat = 24 private var underline: Bool + private var items: [String] // MARK: - Init - init(items: [Any]?, underbarInfo info: UnderbarInfo, underline: Bool = true) { + init(items: [String], underbarInfo info: UnderbarInfo, itemSpacing: CGFloat = 24, underline: Bool = true) { + self.items = items self.underbarInfo = info + self.itemSpacing = itemSpacing self.underline = underline + super.init(items: items) setUI() + setAddTarget() } required init?(coder: NSCoder) { @@ -45,13 +51,34 @@ final class CustomSegmentedControl: UISegmentedControl { if !isFirstSettingDone { isFirstSettingDone.toggle() + setupItemBackgrounds() if underline { setUnderbarMovableBackgroundLayer() } layer.masksToBounds = false } + + updateLabelColors() updateUnderbarPosition() } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + var xOffset: CGFloat = 0 + + for index in 0.. UIView { + let view = UIView(frame: .zero) + view.translatesAutoresizingMaskIntoConstraints = false + view.backgroundColor = underbarInfo.highlightColor + addSubview(view) + return view + } + + private func removeBorders() { + let image = UIImage() + setBackgroundImage(image, for: .normal, barMetrics: .default) + setBackgroundImage(image, for: .selected, barMetrics: .default) + setBackgroundImage(image, for: .highlighted, barMetrics: .default) + setDividerImage(image, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default) + } + private func setUnderbarMovableBackgroundLayer() { let backgroundLayer = CALayer() backgroundLayer.frame = .init( @@ -86,14 +134,6 @@ private extension CustomSegmentedControl { layer.addSublayer(backgroundLayer) } - private func makeUnderbar() -> UIView { - let view = UIView(frame: .zero) - view.translatesAutoresizingMaskIntoConstraints = false - view.backgroundColor = underbarInfo.highlightColor - addSubview(view) - return view - } - private func updateUnderbarPosition() { let selectedSegmentFrame = frameForSegment(at: selectedSegmentIndex) let textWidth = calculateTextWidth(for: selectedSegmentIndex) @@ -102,18 +142,19 @@ private extension CustomSegmentedControl { self.underbar.frame = CGRect( x: selectedSegmentFrame.origin.x + (selectedSegmentFrame.width - textWidth) / 2, y: self.bounds.height - self.underbarInfo.height, - width: textWidth, + width: selectedSegmentFrame.width, height: self.underbarInfo.height ) }) } - private func removeBorders() { - let image = UIImage() - setBackgroundImage(image, for: .normal, barMetrics: .default) - setBackgroundImage(image, for: .selected, barMetrics: .default) - setBackgroundImage(image, for: .highlighted, barMetrics: .default) - setDividerImage(image, forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default) + private func updateLabelColors() { + for index in 0.. CGRect { - let segmentWidth = bounds.width / CGFloat(numberOfSegments) - return CGRect(x: CGFloat(index) * segmentWidth, y: 0, width: segmentWidth, height: bounds.height) + let xOffset = (0.. CGFloat { guard let title = titleForSegment(at: index), let font = titleTextAttributes(for: .normal)?[.font] as? UIFont else { return 0 } @@ -133,3 +203,12 @@ extension CustomSegmentedControl { return size.width } } + +// MARK: - @objc func + +extension CustomSegmentedControl { + @objc + private func segmentValueChanged() { + updateLabelColors() + } +} diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift index 3df8855..d2a3ce2 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/FilteringView/ViewController/FilteringViewController.swift @@ -129,8 +129,8 @@ extension FilteringViewController { view.addSubviews( notchView, titleLabel, - segmentControl, underLineView, + segmentControl, saveButton ) } @@ -149,14 +149,14 @@ extension FilteringViewController { } segmentControl.snp.makeConstraints { - $0.top.equalTo(titleLabel.snp.bottom).offset(20.adjustedH) - $0.leading.equalToSuperview().inset(20.adjusted) - $0.height.equalTo(40.adjustedH) + $0.top.equalTo(titleLabel.snp.bottom).offset(11.adjustedH) + $0.leading.equalToSuperview().inset(25.adjusted) + $0.height.equalTo(39) } underLineView.snp.makeConstraints { $0.top.equalTo(segmentControl.snp.bottom).offset(-1) - $0.horizontalEdges.equalToSuperview().inset(29.adjusted) + $0.horizontalEdges.equalToSuperview().inset(25.adjusted) $0.height.equalTo(1) } From 7b1717fe16411fe749769ca7cecfe2118b55ad63 Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 07:01:57 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[Fix]=20#249=20-=20=EC=B4=88=EB=A1=9D?= =?UTF-8?q?=EC=83=89=20=EB=B0=94=EA=B0=80=20=EB=94=B0=EB=9D=BC=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=EC=86=8D=EB=8F=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Resource/UIComponents/CustomSegmentedControl.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift b/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift index fea36e8..f0c70ea 100644 --- a/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift +++ b/Terning-iOS/Terning-iOS/Resource/UIComponents/CustomSegmentedControl.swift @@ -138,13 +138,16 @@ private extension CustomSegmentedControl { let selectedSegmentFrame = frameForSegment(at: selectedSegmentIndex) let textWidth = calculateTextWidth(for: selectedSegmentIndex) - UIView.animate(withDuration: 0.27, delay: 0, options: .curveEaseOut, animations: { + underbar.layer.removeAllAnimations() + + UIView.animate(withDuration: 0.17, delay: 0, options: .curveLinear, animations: { self.underbar.frame = CGRect( x: selectedSegmentFrame.origin.x + (selectedSegmentFrame.width - textWidth) / 2, y: self.bounds.height - self.underbarInfo.height, width: selectedSegmentFrame.width, height: self.underbarInfo.height ) + self.layoutIfNeeded() }) } From ef85b0d128e7d989837a8d599f3d0e23cdc35f0c Mon Sep 17 00:00:00 2001 From: wjdalswl <109158284+wjdalswl@users.noreply.github.com> Date: Sat, 11 Jan 2025 16:58:35 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[Fix]=20#249=20-=201=EC=B0=A8=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PlanFiltering/ViewModel/PlanFilteringViewModel.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift index 54df0d0..3a1a668 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/FilteringSetting/PlanFiltering/ViewModel/PlanFilteringViewModel.swift @@ -91,6 +91,9 @@ final class PlanFilteringViewModel: ViewModelType { self.yearRelay.accept(nil) self.monthRelay.accept(nil) self.hasNonNilValueRelay.accept(false) + + TemporaryFilteringData.shared.grade = nil + TemporaryFilteringData.shared.workingPeriod = nil } }) .disposed(by: disposeBag) @@ -114,6 +117,7 @@ final class PlanFilteringViewModel: ViewModelType { if !isAllNil { self.hasNonNilValueRelay.accept(true) } + return isAllNil && !self.hasNonNilValueRelay.value } .distinctUntilChanged() From ad46ca5272658d753bd25bdbf76ceccb4ecf5779 Mon Sep 17 00:00:00 2001 From: LeeMyeongJin Date: Sat, 11 Jan 2025 17:17:54 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[Chore]=20#249=20-=20filterInfos=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/RefactorHome/NewHomeViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terning-iOS/Terning-iOS/Source/Presentation/RefactorHome/NewHomeViewController.swift b/Terning-iOS/Terning-iOS/Source/Presentation/RefactorHome/NewHomeViewController.swift index 75c782a..5bd796b 100644 --- a/Terning-iOS/Terning-iOS/Source/Presentation/RefactorHome/NewHomeViewController.swift +++ b/Terning-iOS/Terning-iOS/Source/Presentation/RefactorHome/NewHomeViewController.swift @@ -61,8 +61,8 @@ final class NewHomeViewController: UIViewController { var filterInfos: UserFilteringInfoModel = UserFilteringInfoModel( grade: nil, // 기본값 설정 workingPeriod: nil, // 기본값 설정 - startYear: nil, // 기본값 설정 - startMonth: nil, // 기본값 설정 + startYear: 0, // 기본값 설정 + startMonth: 0, // 기본값 설정 jobType: nil )