From 92d33787767689087cbda821ad9e4fdc624d20c9 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 10:58:19 +0900 Subject: [PATCH 1/8] =?UTF-8?q?[IDLE-000]=20IdleSnackBar=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../success_check.imageset/Contents.json | 15 ++++ .../success_check.imageset/success_check.svg | 3 + .../CommonUI/SnackBar/IdleSnackBar.swift | 83 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/Contents.json create mode 100644 project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/success_check.svg create mode 100644 project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift diff --git a/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/Contents.json b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/Contents.json new file mode 100644 index 00000000..5c4ff470 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "success_check.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/success_check.svg b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/success_check.svg new file mode 100644 index 00000000..fb095f26 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Resources/Icons.xcassets/success_check.imageset/success_check.svg @@ -0,0 +1,3 @@ + + + diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift new file mode 100644 index 00000000..b311e2b7 --- /dev/null +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift @@ -0,0 +1,83 @@ +// +// IdleSnackBar.swift +// DSKit +// +// Created by choijunios on 9/12/24. +// + +import UIKit +import RxSwift +import RxCocoa +import Entity + +public class IdleSnackBar: UIView { + + let titleLabel: IdleLabel = { + let label = IdleLabel(typography: .Subtitle4) + label.attrTextColor = DSColor.gray0.color + return label + }() + + let titleIcon: UIImageView = { + let defaultSuccessIcon = DSIcon.successCheck.image + let imageView = UIImageView(image: defaultSuccessIcon) + imageView.tintColor = DSColor.gray0.color + return imageView + }() + + public override init(frame: CGRect) { + super.init(frame: frame) + + setAppearance() + setLayout() + } + + public required init?(coder: NSCoder) { nil } + + func setAppearance() { + self.backgroundColor = DSColor.gray500.color + self.layer.cornerRadius = 8 + } + + func setLayout() { + + self.layoutMargins = .init(top: 12, left: 16, bottom: 12, right: 16) + + let mainStack = HStack([ + titleIcon, titleLabel, Spacer() + ], spacing: 4, alignment: .center) + + [ + mainStack + ].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + + NSLayoutConstraint.activate([ + mainStack.topAnchor.constraint(equalTo: self.layoutMarginsGuide.topAnchor), + mainStack.leftAnchor.constraint(equalTo: self.layoutMarginsGuide.leftAnchor), + mainStack.rightAnchor.constraint(equalTo: self.layoutMarginsGuide.rightAnchor), + mainStack.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), + ]) + } +} + +@available(iOS 17.0, *) +#Preview("Preview", traits: .defaultLayout) { + let view = IdleSnackBar( + frame: .init( + origin: .init(x: 20, y: 300), + size: .init(width: 300, height: 48) + ) + ) + view.titleLabel.textString = "지원이 완료되었어요" + + DispatchQueue.main.asyncAfter(deadline: .now()+3) { + + UIView.animate(withDuration: 0.35) { + view.alpha = 0 + } + } + return view +} From 5008e2bb3508b45c1c8a5377aacf91d4d87ca4be Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 11:01:22 +0900 Subject: [PATCH 2/8] =?UTF-8?q?[IDLE-000]=20=EC=8A=A4=EB=82=B5=EB=B0=94?= =?UTF-8?q?=EB=A5=BC=20=EB=B9=8C=EB=8D=94=ED=8C=A8=ED=84=B4=EC=9D=84=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=B4=20=EC=88=98=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 내부 구현사항을 숨기고, 직관적인 변경가능 --- .../CommonUI/SnackBar/IdleSnackBar.swift | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift index b311e2b7..aecfa5e3 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift @@ -12,13 +12,13 @@ import Entity public class IdleSnackBar: UIView { - let titleLabel: IdleLabel = { + private let titleLabel: IdleLabel = { let label = IdleLabel(typography: .Subtitle4) label.attrTextColor = DSColor.gray0.color return label }() - let titleIcon: UIImageView = { + private let titleIcon: UIImageView = { let defaultSuccessIcon = DSIcon.successCheck.image let imageView = UIImageView(image: defaultSuccessIcon) imageView.tintColor = DSColor.gray0.color @@ -61,6 +61,20 @@ public class IdleSnackBar: UIView { mainStack.bottomAnchor.constraint(equalTo: self.layoutMarginsGuide.bottomAnchor), ]) } + + public func setLabelText(_ text: String) -> Self { + titleLabel.textString = text + return self + } + + public func setImage(_ image: UIImage) -> Self { + titleIcon.image = image + return self + } + + public func setBackgroundColor(_ color: UIColor) { + self.backgroundColor = color + } } @available(iOS 17.0, *) @@ -71,7 +85,9 @@ public class IdleSnackBar: UIView { size: .init(width: 300, height: 48) ) ) - view.titleLabel.textString = "지원이 완료되었어요" + + _ = view + .setLabelText("지원이 완료되었어요") DispatchQueue.main.asyncAfter(deadline: .now()+3) { From 2876f889b5e019a7287c9d2388dd41ba1fc52c42 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 11:06:15 +0900 Subject: [PATCH 3/8] =?UTF-8?q?[IDLE-000]=20IdleSnackBarRO=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=9C=20=EC=8A=A4=ED=83=9D=EB=B0=94=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=84=EB=8B=AC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommonUI/SnackBar/IdleSnackBar.swift | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift index aecfa5e3..169dc732 100644 --- a/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift +++ b/project/Projects/Presentation/DSKit/Sources/CommonUI/SnackBar/IdleSnackBar.swift @@ -10,6 +10,22 @@ import RxSwift import RxCocoa import Entity +public struct IdleSnackBarRO { + let titleText: String + let icon: UIImage? + let backgroundColor: UIColor? + + public init( + titleText: String="완료되었습니다.", + icon: UIImage?=DSIcon.successCheck.image, + backgroundColor: UIColor?=DSColor.gray500.color + ) { + self.titleText = titleText + self.icon = icon + self.backgroundColor = backgroundColor + } +} + public class IdleSnackBar: UIView { private let titleLabel: IdleLabel = { @@ -19,8 +35,7 @@ public class IdleSnackBar: UIView { }() private let titleIcon: UIImageView = { - let defaultSuccessIcon = DSIcon.successCheck.image - let imageView = UIImageView(image: defaultSuccessIcon) + let imageView = UIImageView() imageView.tintColor = DSColor.gray0.color return imageView }() @@ -62,18 +77,10 @@ public class IdleSnackBar: UIView { ]) } - public func setLabelText(_ text: String) -> Self { - titleLabel.textString = text - return self - } - - public func setImage(_ image: UIImage) -> Self { - titleIcon.image = image - return self - } - - public func setBackgroundColor(_ color: UIColor) { - self.backgroundColor = color + public func applyRO(_ ro: IdleSnackBarRO) { + self.backgroundColor = ro.backgroundColor + titleLabel.textString = ro.titleText + titleIcon.image = ro.icon } } @@ -86,9 +93,6 @@ public class IdleSnackBar: UIView { ) ) - _ = view - .setLabelText("지원이 완료되었어요") - DispatchQueue.main.asyncAfter(deadline: .now()+3) { UIView.animate(withDuration: 0.35) { From 7a4b50fd718df1d3d0eaa361c2f7bc42cafab5dc Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 11:20:44 +0900 Subject: [PATCH 4/8] =?UTF-8?q?[IDLE-000]=20snackBar=EB=A5=BC=20BaseVC,=20?= =?UTF-8?q?VM=EC=97=90=20=EC=A0=81=EC=9A=A9(=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=A0=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/BaseViewController.swift | 55 +++++++++++++++++++ .../Sources/ViewModelType/BaseViewModel.swift | 7 +++ 2 files changed, 62 insertions(+) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift index c1ac2290..b537551b 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift @@ -44,6 +44,14 @@ open class BaseViewController: UIViewController { }) .disposed(by: disposeBag) + // Snack bar + viewModel + .snackBarDriver? + .drive(onNext: { [weak self] snackBarRO in + self?.showSnackBar(ro: snackBarRO) + }) + .disposed(by: disposeBag) + // 로딩 viewModel .showLoadingDriver? @@ -106,6 +114,53 @@ public extension BaseViewController { } } +// MARK: Snack bar +extension BaseViewController { + + func showSnackBar(ro: IdleSnackBarRO) { + + let viewSize = self.view.bounds.size + let horizontalPadding: CGFloat = 20 + let bottomPadding: CGFloat = 20 + + let snackBarHeight: CGFloat = 48 + let snackBarWidth: CGFloat = viewSize.width - horizontalPadding*2 + + let snackBarXPos: CGFloat = horizontalPadding + let snackBarYPos: CGFloat = viewSize.height - bottomPadding - snackBarHeight + + let snackBar = IdleSnackBar( + frame: .init( + origin: .init(x: snackBarXPos, y: snackBarYPos), + size: .init(width: snackBarWidth, height: snackBarHeight) + ) + ) + + // ro적용 + snackBar.applyRO(ro) + + // 스낵바를 하단에 감춘다 + snackBar.transform = .init(translationX: 0, y: snackBarHeight+horizontalPadding) + snackBar.alpha = 0.5 + + // 뷰계층에 추가 + view.addSubview(snackBar) + + let snackBarShowingDuration: CGFloat = 0.5 + + UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseIn) { + snackBar.transform = .identity + snackBar.alpha = 1.0 + } completion: { _ in + + UIView.animate(withDuration: 0.2, delay: snackBarShowingDuration, options: .curveEaseIn) { + snackBar.transform = .init(translationX: 0, y: snackBarHeight+horizontalPadding) + snackBar.alpha = 0.5 + } + } + } +} + public extension BaseViewController { func showDefaultLoadingScreen() { diff --git a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift index bbf6d292..8e8fd028 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift @@ -20,6 +20,10 @@ open class BaseViewModel { public let alertObject: PublishSubject = .init() var alertObjectDriver: Driver? + // Snack bar + let snackBar: PublishSubject = .init() + var snackBarDriver: Driver? + // 로딩 public let showLoading: PublishSubject = .init() public let dismissLoading: PublishSubject = .init() @@ -35,6 +39,9 @@ open class BaseViewModel { self.alertObjectDriver = alertObject .asDriver(onErrorDriveWith: .never()) + + self.snackBarDriver = snackBar + .asDriver(onErrorDriveWith: .never()) self.showLoadingDriver = showLoading .asDriver(onErrorDriveWith: .never()) From 9d301c858541f755ca358108f181898ddd31e6f6 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 11:37:48 +0900 Subject: [PATCH 5/8] =?UTF-8?q?[IDLE-000]=20snackBar=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit frame base layout을 사용함으로 뷰컨트롤러의 view의 레이아웃이 결정된 이후 시점에 사용해야 한다. viewDidAppear이후 권장 --- .../Base/ExampleApp/Sources/SceneDelegate.swift | 6 +++++- .../Base/ExampleApp/Sources/ViewController.swift | 12 +++++++++++- .../ViewController/Base/BaseViewController.swift | 2 +- .../Base/Sources/ViewModelType/BaseViewModel.swift | 2 +- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift index 7c3560ed..7e4d470e 100644 --- a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/SceneDelegate.swift @@ -16,8 +16,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = scene as? UIWindowScene else { return } + let vm = BaseViewModel() + let vc = ViewController() + vc.bind(viewModel: vm) + window = UIWindow(windowScene: windowScene) - window?.rootViewController = UIViewController() + window?.rootViewController = vc window?.makeKeyAndVisible() } diff --git a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/ViewController.swift b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/ViewController.swift index e439d432..c04d8514 100644 --- a/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/ViewController.swift +++ b/project/Projects/Presentation/Feature/Base/ExampleApp/Sources/ViewController.swift @@ -6,8 +6,9 @@ // import UIKit +import BaseFeature -class ViewController: UIViewController { +class ViewController: BaseViewController { override func viewDidLoad() { @@ -24,6 +25,15 @@ class ViewController: UIViewController { initialLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor), initialLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor), ]) + + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.viewModel? + .snackBar + .onNext(.init(titleText: "테스트테스트테스트")) } } diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift index b537551b..8d036b20 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift @@ -146,7 +146,7 @@ extension BaseViewController { // 뷰계층에 추가 view.addSubview(snackBar) - let snackBarShowingDuration: CGFloat = 0.5 + let snackBarShowingDuration: CGFloat = 2 UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseIn) { snackBar.transform = .identity diff --git a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift index 8e8fd028..a0a69a99 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift @@ -21,7 +21,7 @@ open class BaseViewModel { var alertObjectDriver: Driver? // Snack bar - let snackBar: PublishSubject = .init() + public let snackBar: PublishSubject = .init() var snackBarDriver: Driver? // 로딩 From 9ba901665b5ce16cf1b8547eb0d23fe57c109ce3 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 12:00:39 +0900 Subject: [PATCH 6/8] =?UTF-8?q?[IDLE-000]=20=EA=B3=B5=EA=B3=A0=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EC=84=B1=EA=B3=A0=EC=8B=9C=20=EC=8A=A4=EB=82=B5?= =?UTF-8?q?=EB=B0=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WorkerRecruitmentPostBoardVM.swift | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift index 146f3e8e..c8c74b5c 100644 --- a/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Worker/Sources/ViewModel/RecruitmentPost/OnGoingPostBoard/WorkerRecruitmentPostBoardVM.swift @@ -123,23 +123,28 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB } } .asDriver(onErrorDriveWith: .never()) - - // 로딩 시작 - loadingStartObservables.append(applyRequest.map { _ in }) - let applyRequestResult = applyRequest + let applyRequestResult = mapEndLoading(mapStartLoading(applyRequest.asObservable()) .flatMap { [recruitmentPostUseCase] postId in // 리스트화면에서는 앱내 지원만 지원합니다. return recruitmentPostUseCase .applyToPost(postId: postId, method: .app) - } + }) .share() - // 로딩 종료 - loadingEndObservables.append(applyRequestResult.map { _ in }) - let applyRequestSuccess = applyRequestResult.compactMap { $0.value } + let applyRequestSuccess = applyRequestResult.compactMap { $0.value }.share() + + // 스낵바 + applyRequestSuccess + .subscribe { [weak self] _ in + + self?.snackBar.onNext( + .init(titleText: "지원이 완료되었어요.") + ) + } + .disposed(by: dispostBag) // 지원하기 성공시 새로고침 applyRequestSuccess @@ -155,7 +160,6 @@ public class WorkerRecruitmentPostBoardVM: BaseViewModel, WorkerRecruitmentPostB message: error.message ) } - // MARK: 공고리스트 처음부터 요청하기 let initialRequest = requestInitialPageRequest From f2d0a99ddde4b5d3df76eb26236cb765fdec1879 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 13:49:19 +0900 Subject: [PATCH 7/8] =?UTF-8?q?[IDLE-000]=20=EC=B1=84=EC=9A=A9=EA=B3=B5?= =?UTF-8?q?=EA=B3=A0=20=EC=A2=85=EB=A3=8C=EC=8B=9C=20=EC=8A=A4=EB=82=B5?= =?UTF-8?q?=EB=B0=94=20=ED=91=9C=EC=B6=9C(VC=EA=B0=80=20=EB=8B=AB=ED=9E=88?= =?UTF-8?q?=EB=A9=B4=EC=84=9C=20=EC=8A=A4=EB=82=B5=EB=B0=94=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A0=84=EB=8B=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/BaseViewController.swift | 6 +++++ .../Sources/ViewModelType/BaseViewModel.swift | 23 +++++++++++++++++++ .../PostDetailForCenterCoordinator.swift | 17 ++++++++++++++ .../CenterRecruitmentPostBoardVM.swift | 6 +++++ .../PostDetailForCenterVM.swift | 15 +++++++++--- 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift index 8d036b20..fa9b67ad 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift @@ -29,6 +29,12 @@ open class BaseViewController: UIViewController { self.viewModel = viewModel + // Life cycle + rx.viewDidAppear + .map({ _ in }) + .bind(to: viewModel.viewDidAppear) + .disposed(by: disposeBag) + // Alert viewModel .alertDriver? diff --git a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift index a0a69a99..b8e42549 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/ViewModelType/BaseViewModel.swift @@ -24,6 +24,11 @@ open class BaseViewModel { public let snackBar: PublishSubject = .init() var snackBarDriver: Driver? + // MARK: SnackBarStack + private var snackBarStack: [IdleSnackBarRO] = [] + + let viewDidAppear: PublishSubject = .init() + // 로딩 public let showLoading: PublishSubject = .init() public let dismissLoading: PublishSubject = .init() @@ -48,6 +53,20 @@ open class BaseViewModel { self.dismissLoadingDriver = dismissLoading .asDriver(onErrorDriveWith: .never()) + + // life cycle + viewDidAppear + .compactMap { [weak self] in + let bars = self?.snackBarStack + self?.snackBarStack = [] + return bars + } + .flatMap { bars in + Observable.from(bars) + } + .debounce(.milliseconds(350), scheduler: MainScheduler.asyncInstance) + .bind(to: snackBar) + .disposed(by: disposeBag) } public func mapStartLoading(_ target: Observable) -> Observable { @@ -71,4 +90,8 @@ open class BaseViewModel { return item } } + + public func addSnackBar(ro: IdleSnackBarRO) { + self.snackBarStack.append(ro) + } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/PostDetailForCenterCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/PostDetailForCenterCoordinator.swift index bf3561d1..a885cf46 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/PostDetailForCenterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/PostDetailForCenterCoordinator.swift @@ -6,9 +6,11 @@ // import UIKit +import BaseFeature import PresentationCore import UseCaseInterface import Entity +import DSKit public class PostDetailForCenterCoordinator: ChildCoordinator { @@ -64,6 +66,21 @@ public class PostDetailForCenterCoordinator: ChildCoordinator { popViewController() parent?.removeChildCoordinator(self) } + + public func coordinatorDidFinishWithSnackBar(ro: IdleSnackBarRO) { + let belowIndex = navigationController.children.count-2 + + if belowIndex >= 0 { + let belowVC = navigationController.children[belowIndex] + + if let baseVC = belowVC as? BaseViewController { + + baseVC.viewModel?.addSnackBar(ro: ro) + } + } + + coordinatorDidFinish() + } } extension PostDetailForCenterCoordinator { diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift index 2c4cd58d..3d3a444c 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/CenterRecruitmentPostBoardVM.swift @@ -124,6 +124,12 @@ public class CenterRecruitmentPostBoardVM: BaseViewModel, CenterRecruitmentPostB // 새로고침 closePostSuccess + .map({ [weak self] value in + // 스낵바 + self?.snackBar.onNext(.init(titleText: "채용을 종료했어요.")) + + return value + }) .bind(to: requestOngoingPost) .disposed(by: disposeBag) diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/PostDetailForCenterVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/PostDetailForCenterVM.swift index 3890add2..f539f3ab 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/PostDetailForCenterVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/PostDetailForCenterVM.swift @@ -12,6 +12,7 @@ import Entity import PresentationCore import UseCaseInterface import BaseFeature +import DSKit public protocol PostDetailViewModelable: AnyObject, @@ -221,10 +222,18 @@ public class PostDetailForCenterVM: BaseViewModel, PostDetailViewModelable { let removePostSuccess = removePostResult.compactMap { $0.value } let removePostFailure = removePostResult.compactMap { $0.error } + + let closePostSuccessSnackBarRO = closePostSuccess + .map { _ in IdleSnackBarRO(titleText: "채용을 종료했어요.") } + + let removePostSuccessSnackBarRO = removePostSuccess + .map { _ in IdleSnackBarRO(titleText: "공고를 삭제했어요.") } + Observable - .merge(closePostSuccess, removePostSuccess) - .subscribe(onNext: { [weak self] _ in - self?.coordinator?.coordinatorDidFinish() + .merge(closePostSuccessSnackBarRO, removePostSuccessSnackBarRO) + .subscribe(onNext: { [weak self] ro in + self?.coordinator? + .coordinatorDidFinishWithSnackBar(ro: ro) }) .disposed(by: disposeBag) From 7b0da3de373c18a1f4433674b81694401bbfe536 Mon Sep 17 00:00:00 2001 From: J0onYEong Date: Thu, 12 Sep 2024 14:10:16 +0900 Subject: [PATCH 8/8] =?UTF-8?q?[IDLE-000]=20=EA=B3=B5=EA=B3=A0=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=98=EA=B8=B0=20=EC=8A=A4=EB=82=B5=EB=B0=94=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base/BaseViewController.swift | 4 +++- .../RecruitmentPost/EditPostCoordinator.swift | 17 +++++++++++++++++ .../ViewModel/RecruitmentPost/EditPostVM.swift | 3 ++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift index fa9b67ad..d4ae4a2f 100644 --- a/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift +++ b/project/Projects/Presentation/Feature/Base/Sources/View/ViewController/Base/BaseViewController.swift @@ -21,6 +21,8 @@ open class BaseViewController: UIViewController { private var loadingDimissionRequested: Bool = false private var loadingVC: UIViewController? + open var snackBarBottomPadding: CGFloat = 0.0 + /// disposeBag public let disposeBag = DisposeBag() @@ -127,7 +129,7 @@ extension BaseViewController { let viewSize = self.view.bounds.size let horizontalPadding: CGFloat = 20 - let bottomPadding: CGFloat = 20 + let bottomPadding: CGFloat = 20 + snackBarBottomPadding let snackBarHeight: CGFloat = 48 let snackBarWidth: CGFloat = viewSize.width - horizontalPadding*2 diff --git a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/EditPostCoordinator.swift b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/EditPostCoordinator.swift index c9ffb3fa..240401a8 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/EditPostCoordinator.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/Coordinator/RecruitmentPost/EditPostCoordinator.swift @@ -7,8 +7,10 @@ import UIKit import PresentationCore +import BaseFeature import UseCaseInterface import Entity +import DSKit public class EditPostCoordinator: ChildCoordinator { @@ -51,5 +53,20 @@ public class EditPostCoordinator: ChildCoordinator { popViewController() parent?.removeChildCoordinator(self) } + + func coordinatorDidFinishWithSnackBar(ro: IdleSnackBarRO) { + let belowIndex = navigationController.children.count-2 + + if belowIndex >= 0 { + let belowVC = navigationController.children[belowIndex] + + if let baseVC = belowVC as? BaseViewController { + + baseVC.viewModel?.addSnackBar(ro: ro) + } + } + + coordinatorDidFinish() + } } diff --git a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift index 2f4ffb66..5e895123 100644 --- a/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift +++ b/project/Projects/Presentation/Feature/Center/Sources/ViewModel/RecruitmentPost/EditPostVM.swift @@ -409,7 +409,8 @@ public class EditPostVM: BaseViewModel, EditPostViewModelable { guard let self else { return } // 성공적으로 수정됨 - self.editPostCoordinator?.coordinatorDidFinish() + self.editPostCoordinator? + .coordinatorDidFinishWithSnackBar(ro: .init(titleText: "공고가 수정되었어요.")) } .disposed(by: disposeBag)