diff --git a/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift b/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift index 090ca93e..dd19df1e 100644 --- a/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift +++ b/Plugins/EnvPlugin/ProjectDescriptionHelpers/Environment.swift @@ -7,7 +7,7 @@ public enum Environment { public extension Project { enum Environment { public static let workspaceName = "MaeumGaGym-iOS" - public static let deploymentTarget = DeploymentTarget.iOS(targetVersion: "15.0", devices: .iphone) + public static let deploymentTarget = DeploymentTarget.iOS(targetVersion: "16.0", devices: .iphone) public static let platform = Platform.iOS public static let bundlePrefix = "com.maeumGaGym-health-iOS" public static let baseSetting: SettingsDictionary = SettingsDictionary() diff --git a/Projects/Features/SelfCareFeature/Demo/Sources/Application/SceneDelegate.swift b/Projects/Features/SelfCareFeature/Demo/Sources/Application/SceneDelegate.swift index 49ca62b0..2751b7b7 100644 --- a/Projects/Features/SelfCareFeature/Demo/Sources/Application/SceneDelegate.swift +++ b/Projects/Features/SelfCareFeature/Demo/Sources/Application/SceneDelegate.swift @@ -20,8 +20,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let useCase = DefaultSelfCareUseCase(repository: SelfCareRepository(networkService: SelfCareService())) let viewModel = SelfCareHomeViewModel() let viewController = SelfCareHomeViewController(viewModel) -// let viewModel = SelfCareMyRoutineViewModel(useCase: useCase) -// let viewController = SelfCareMyRoutineViewController(viewModel) window?.configure(withRootViewController: viewController) window?.makeKeyAndVisible() } diff --git a/Projects/Features/SelfCareFeature/Sources/HomeScene/VC/SelfCareHomeViewController.swift b/Projects/Features/SelfCareFeature/Sources/HomeScene/VC/SelfCareHomeViewController.swift index 8945d985..b081a56a 100644 --- a/Projects/Features/SelfCareFeature/Sources/HomeScene/VC/SelfCareHomeViewController.swift +++ b/Projects/Features/SelfCareFeature/Sources/HomeScene/VC/SelfCareHomeViewController.swift @@ -174,6 +174,22 @@ extension SelfCareHomeViewController: UITableViewDataSource { let viewController = SelfCareProfileViewController(viewModel) self.navigationController?.pushViewController(viewController, animated: true) return + case .selfCare: + let useCase = DefaultSelfCareUseCase(repository: SelfCareRepository(networkService: SelfCareService())) + let viewModel = SelfCareTargetMainViewModel(useCase: useCase) + self.navigationController?.pushViewController(SelfCareTargetMainViewController(viewModel), animated: true) +// switch menus[indexPath.row].menuImage { +// case .goalIcon: +// let useCase = DefaultSelfCareUseCase(repository: SelfCareRepository(networkService: SelfCareService())) +// let viewModel = SelfCareTargetMainViewModel(useCase: useCase) +// self.navigationController?.pushViewController( +// SelfCareTargetMainViewController(viewModel) +// , animated: true +// ) +// return +// default: +// return +// } default: return } diff --git a/Projects/Features/SelfCareFeature/Sources/ProfileScene/View/NavigationBar/SelfCareProfileNavigationBar.swift b/Projects/Features/SelfCareFeature/Sources/ProfileScene/View/NavigationBar/SelfCareProfileNavigationBar.swift index 35822fd3..576638f8 100644 --- a/Projects/Features/SelfCareFeature/Sources/ProfileScene/View/NavigationBar/SelfCareProfileNavigationBar.swift +++ b/Projects/Features/SelfCareFeature/Sources/ProfileScene/View/NavigationBar/SelfCareProfileNavigationBar.swift @@ -26,12 +26,11 @@ final public class SelfCareProfileNavigationBar: UIView { private lazy var leftItemsStackView = UIStackView(arrangedSubviews: [leftButton, leftLabel]).then { $0.axis = .horizontal - $0.spacing = 0 - $0.distribution = .fillEqually + $0.spacing = 24 } public init( - leftText: String + leftText: String? = nil ) { super.init(frame: .zero) self.leftText = leftText @@ -58,7 +57,7 @@ private extension SelfCareProfileNavigationBar { leftItemsStackView.snp.makeConstraints { $0.top.bottom.equalToSuperview() - $0.leading.equalToSuperview().offset(-10.0) + $0.leading.equalToSuperview().inset(10.0) } } } diff --git a/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButton.swift b/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButton.swift index 101cdfd6..05be208b 100644 --- a/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButton.swift +++ b/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButton.swift @@ -73,14 +73,13 @@ private extension SelfCareButton { buttonLabel.snp.makeConstraints { $0.top.bottom.equalToSuperview() + $0.centerX.equalToSuperview() $0.leading.equalTo(buttonImage.snp.trailing).offset(8.0) - $0.trailing.equalToSuperview() $0.height.equalTo(24.0) } buttonLabel.sizeToFit() containerView.snp.remakeConstraints { - $0.width.equalTo(type.width) $0.centerX.equalToSuperview() $0.top.bottom.equalToSuperview().inset(17.0) } diff --git a/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButtonType.swift b/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButtonType.swift index baac19c0..ccc78cae 100644 --- a/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButtonType.swift +++ b/Projects/Features/SelfCareFeature/Sources/Supporter/SelfCareButtonType.swift @@ -23,7 +23,7 @@ public enum SelfCareButtonType { case .posturePlus: return DSKitAsset.Assets.blackPlus.image case .plusTarget: - return DSKitAsset.Assets.blackPlus.image + return DSKitAsset.Assets.whiteAdd.image default: return nil } diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareAddTargetViewController.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareAddTargetViewController.swift new file mode 100644 index 00000000..7fa5376a --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareAddTargetViewController.swift @@ -0,0 +1,231 @@ +import UIKit + +import RxSwift +import RxCocoa +import RxFlow + +import SnapKit +import Then + +import Core +import DSKit +import Domain + +import MGLogger +import Data + +import SelfCareFeatureInterface + +final public class SelfCareAddTargetViewController: BaseViewController { + + private var bottomConstraint: Constraint? + + private lazy var navBar = SelfCareProfileNavigationBar(leftText: "목표 추가") + private let titleTextField = MGSelfCareTextField( + typeText: "제목", + keyboardType: .default, + placeholderText: "제목을 입력해주세요", + placeholderTextColor: .gray400 + ) + private let startDateSelectView = MGDateSelectView(typeText: "시작 날짜") + private let endDateSelectView = MGDateSelectView(typeText: "마감 날짜") + private let contentTextView = MGTextView() + private let endButton = MGButton( + titleText: "확인", + font: UIFont.Pretendard.labelLarge, + textColor: .white, + backColor: .blue500 + ).then { + $0.isHidden = true + } + + private let editFinishButton = MGButton( + titleText: "완료", + font: UIFont.Pretendard.labelLarge, + textColor: .white, + backColor: .blue500 + ).then { + $0.layer.cornerRadius = 8 + } + + public override func configureNavigationBar() { + super.configureNavigationBar() + + navigationController?.isNavigationBarHidden = true + } + public override func attribute() { + super.attribute() + + view.backgroundColor = .white + setupKeyboardObservers() + } + public override func bindActions() { + navBar.leftButtonTap + .bind(onNext: { [weak self] in + self?.navigationController?.popViewController(animated: true) + }).disposed(by: disposeBag) + + endButton.rx.tap + .bind(onNext: { [weak self] in + self?.view.endEditing(true) + }).disposed(by: disposeBag) + + editFinishButton.rx.tap + .bind(onNext: { [weak self] in + self?.navigationController?.popViewController(animated: true) + }).disposed(by: disposeBag) + + startDateSelectView.dateButtonTap + .bind(onNext: { [weak self] in + let vc = MGTargetAlertView(clickDate: { year, month, day in + self?.startDateSelectView.setup(date: [year!, month!, day!]) + }) + vc.modalTransitionStyle = .crossDissolve + vc.modalPresentationStyle = .overFullScreen + self?.present(vc, animated: true) + }).disposed(by: disposeBag) + + endDateSelectView.dateButtonTap + .bind(onNext: { [weak self] in + let vc = MGTargetAlertView(clickDate: { year, month, day in + self?.endDateSelectView.setup(date: [year!, month!, day!]) + }) + vc.modalTransitionStyle = .crossDissolve + vc.modalPresentationStyle = .overFullScreen + self?.present(vc, animated: true) + }).disposed(by: disposeBag) + } + public override func layout() { + super.layout() + + view.addSubviews([ + navBar, + titleTextField, + startDateSelectView, + endDateSelectView, + contentTextView, + endButton, + editFinishButton + ]) + + navBar.snp.makeConstraints { + $0.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) + } + titleTextField.snp.makeConstraints { + $0.top.equalTo(navBar.snp.bottom).offset(52) + $0.leading.trailing.equalToSuperview().inset(20) + $0.height.equalTo(48) + } + startDateSelectView.snp.makeConstraints { + $0.top.equalTo(titleTextField.snp.bottom).offset(32) + $0.leading.trailing.equalToSuperview().inset(20) + } + endDateSelectView.snp.makeConstraints { + $0.top.equalTo(startDateSelectView.snp.bottom).offset(32) + $0.leading.trailing.equalToSuperview().inset(20) + } + contentTextView.snp.makeConstraints { + $0.top.equalTo(endDateSelectView.snp.bottom).offset(32) + $0.leading.trailing.equalToSuperview().inset(20) + $0.height.equalTo(228) + } + endButton.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalToSuperview().inset(54) + $0.height.equalTo(58) + } + editFinishButton.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(20) + $0.bottom.equalToSuperview().inset(54) + $0.height.equalTo(58) + } + } +} + +extension SelfCareAddTargetViewController { + private func setupKeyboardObservers() { + keyboardBind() + + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillShow), + name: UIResponder.keyboardWillShowNotification, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, + object: nil + ) + } + func animateButtonWithKeyboard(notification: NSNotification, show: Bool) { + guard let keyboardSize = (notification.userInfo?[ + UIResponder.keyboardFrameEndUserInfoKey + ] as? NSValue)?.cgRectValue, + let keyboardAnimationDuration = notification.userInfo?[ + UIResponder.keyboardAnimationDurationUserInfoKey + ] as? TimeInterval else { + return + } + + let offset = show ? -keyboardSize.height : -20.0 + bottomConstraint?.update(offset: offset) + + UIView.animate(withDuration: keyboardAnimationDuration) { [weak self] in + self?.view.layoutIfNeeded() + } + } + + @objc func keyboardWillShow(notification: NSNotification) { + animateButtonWithKeyboard(notification: notification, show: true) + } + + @objc func keyboardWillHide(notification: NSNotification) { + animateButtonWithKeyboard(notification: notification, show: false) + } + + private func keyboardBind() { + let keyboardWillShowObservable = NotificationCenter.default.rx.notification( + UIResponder.keyboardWillShowNotification) + .map { ($0.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height ?? 48 } + + let keyboardWillHideObservable = NotificationCenter.default.rx.notification( + UIResponder.keyboardWillHideNotification) + .map { _ in CGFloat(48) } + + Observable.merge(keyboardWillShowObservable) + .withUnretained(self) + .subscribe(onNext: { owner, height in + owner.endButton.isHidden = false + owner.endButton.snp.remakeConstraints { + owner.bottomConstraint = $0.bottom.equalToSuperview().offset(-height).constraint + $0.width.equalToSuperview() + $0.height.equalTo(58.0) + } + UIView.animate(withDuration: 0.3) { + owner.view.layoutIfNeeded() + } + }) + .disposed(by: disposeBag) + + Observable.merge(keyboardWillHideObservable) + .withUnretained(self) + .subscribe(onNext: { owner, height in + owner.endButton.snp.remakeConstraints { + owner.bottomConstraint?.update(offset: -height + 112.0) + $0.leading.equalToSuperview().offset(20.0) + $0.trailing.equalToSuperview().offset(-20.0) + $0.width.equalTo(390.0) + $0.height.equalTo(58.0) + $0.bottom.equalTo((owner.view.safeAreaLayoutGuide)) + } + owner.endButton.isHidden = true + UIView.animate(withDuration: 0.3) { + owner.view.layoutIfNeeded() + } + }) + .disposed(by: disposeBag) + } + +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareDetailTargetViewController.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareDetailTargetViewController.swift new file mode 100644 index 00000000..fbe3eba1 --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareDetailTargetViewController.swift @@ -0,0 +1,86 @@ +import UIKit + +import RxSwift +import RxCocoa +import RxFlow + +import SnapKit +import Then + +import Core +import DSKit +import Domain + +import MGLogger +import Data + +import SelfCareFeatureInterface + +public class SelfCareDetailTargetViewController: BaseViewController { + + private let navBar = SelfCareProfileNavigationBar() + private let dotButton = MGImageButton(image: .blackDosActIcon) + private let targetTitleLabel = MGLabel( + text: "공부하기", + font: UIFont.Pretendard.titleLarge, + textColor: .black + ) + private let startDateBannerView = MGSelfCareTargetDateBannerView(typeText: "시작", dateText: "102200") + private let endDateBannerView = MGSelfCareTargetDateBannerView(typeText: "마감", dateText: "ㄹㅇㄴㅁㄹㅇㄴㅁ") + private let targetContentLabel = MGLabel( + text: "새해가 다가오기 전까지 열심히 공부하자.\n한 해의 끝도 공부와 함께.", + font: UIFont.Pretendard.bodyMedium, + textColor: .black, + isCenter: false, + numberOfLineCount: 0 + ) + + public override func configureNavigationBar() { + super.configureNavigationBar() + + navigationController?.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: dotButton) + navigationController?.isNavigationBarHidden = true + } + public override func attribute() { + super.attribute() + + view.backgroundColor = .white + } + public override func bindActions() { + navBar.leftButtonTap + .bind(onNext: { [weak self] in + self?.navigationController?.popViewController(animated: true) + }).disposed(by: disposeBag) + } + public override func layout() { + view.addSubviews([ + navBar, + targetTitleLabel, + startDateBannerView, + endDateBannerView, + targetContentLabel + ]) + + navBar.snp.makeConstraints { + $0.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) + } + targetTitleLabel.snp.makeConstraints { + $0.top.equalTo(navBar.snp.bottom).offset(28) + $0.leading.equalToSuperview().inset(20) + } + startDateBannerView.snp.makeConstraints { + $0.top.equalTo(targetTitleLabel.snp.bottom).offset(12) + $0.leading.equalToSuperview().inset(20) + } + endDateBannerView.snp.makeConstraints { + $0.top.equalTo(startDateBannerView.snp.bottom).offset(12) + $0.leading.equalToSuperview().inset(20) + } + targetContentLabel.snp.makeConstraints { + $0.top.equalTo(endDateBannerView.snp.bottom).offset(32) + $0.leading.trailing.equalToSuperview().inset(20) + } + + } + +} diff --git a/Projects/Features/SelfCareFeature/Sources/SelfCareScene/View/Target/SelfCareTargetMainViewController.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareTargetMainViewController.swift similarity index 60% rename from Projects/Features/SelfCareFeature/Sources/SelfCareScene/View/Target/SelfCareTargetMainViewController.swift rename to Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareTargetMainViewController.swift index 8aeb5685..d5b2ba88 100644 --- a/Projects/Features/SelfCareFeature/Sources/SelfCareScene/View/Target/SelfCareTargetMainViewController.swift +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/VC/SelfCareTargetMainViewController.swift @@ -23,21 +23,26 @@ public class SelfCareTargetMainViewController: BaseViewController Int { targetMainModel.targetData.count + 1 @@ -148,8 +177,38 @@ extension SelfCareTargetMainViewController: UITableViewDataSource { let model = targetMainModel.targetData[indexPath.row] cell?.setup(with: model) cell?.selectionStyle = .none + cell?.dotButtonTap + .bind { [weak self] in + let modal = MGSelfCareTargetBottomSheetAlertView( + editButtonTap: { + print("editButtonTapped") + }, + deleteButtonTap: { + print("deleteButtonTapped") + } + ) + let customDetents = UISheetPresentationController.Detent.custom( + identifier: .init("sheetHeight") + ) { _ in + return 153 + } + + if let sheet = modal.sheetPresentationController { + sheet.detents = [customDetents] + sheet.prefersGrabberVisible = true + } + self?.present(modal, animated: true) + }.disposed(by: cell?.disposeBag ?? DisposeBag()) return cell ?? UITableViewCell() } } } +extension SelfCareTargetMainViewController { + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let useCase = DefaultSelfCareUseCase(repository: SelfCareRepository(networkService: SelfCareService())) + let viewModel = SelfCareDetailTargetViewModel(useCase: useCase) + let vc = SelfCareDetailTargetViewController(viewModel) + self.navigationController?.pushViewController(vc, animated: true) + } +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGSelfCareTargetBottomSheetAlertView.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGSelfCareTargetBottomSheetAlertView.swift new file mode 100644 index 00000000..f6b4b667 --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGSelfCareTargetBottomSheetAlertView.swift @@ -0,0 +1,85 @@ +import UIKit + +import SnapKit +import Then + +import RxSwift +import RxCocoa + +import Core +import DSKit +import Domain + +public class MGSelfCareTargetBottomSheetAlertView: UIViewController { + + private let disposeBag = DisposeBag() + + public var editButtonTap: () -> Void + public var deleteButtonTap: () -> Void + + private let targetEditButton = MGButton(titleText: "수정", image: .pencilActIcon).then { + $0.contentHorizontalAlignment = .leading + $0.titleEdgeInsets = .init(top: 0, left: 24, bottom: 0, right: 0) + $0.contentEdgeInsets = .init(top: 0, left: 20, bottom: 0, right: 0) + } + private let targetDeleteButton = MGButton(titleText: "삭제", image: .trashActIcon).then { + $0.contentHorizontalAlignment = .leading + $0.titleEdgeInsets = .init(top: 0, left: 24, bottom: 0, right: 0) + $0.contentEdgeInsets = .init(top: 0, left: 20, bottom: 0, right: 0) + } + + public init( + editButtonTap: @escaping () -> Void, + deleteButtonTap: @escaping () -> Void + ) { + self.editButtonTap = editButtonTap + self.deleteButtonTap = deleteButtonTap + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .white + bindActions() + } + public override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + layout() + } + + private func bindActions() { + targetEditButton.rx.tap + .bind { [weak self] in + self?.editButtonTap() + }.disposed(by: disposeBag) + + targetDeleteButton.rx.tap + .bind { [weak self] in + self?.deleteButtonTap() + }.disposed(by: disposeBag) + + } + private func layout() { + view.addSubviews([ + targetEditButton, + targetDeleteButton + ]) + + targetEditButton.snp.makeConstraints { + $0.top.equalToSuperview().inset(15) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(52) + } + targetDeleteButton.snp.makeConstraints { + $0.top.equalTo(targetEditButton.snp.bottom) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(52) + } + } + +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGTargetAlertView.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGTargetAlertView.swift new file mode 100644 index 00000000..295d070e --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Alert/MGTargetAlertView.swift @@ -0,0 +1,70 @@ +import UIKit + +import SnapKit +import Then + +import Core +import DSKit +import Domain + +public class MGTargetAlertView: UIViewController { + + public var clickDate: (Int?, Int?, Int?) -> Void + + private let calendar = UICalendarView().then { + $0.backgroundColor = .white + $0.layer.cornerRadius = 13 + $0.timeZone = .current + $0.locale = .current + $0.wantsDateDecorations = true + $0.layer.shadow(color: .black, alpha: 0.25, x: 0, y: 4, blur: 4, spread: 0) + } + + public init( + clickDate: @escaping (Int?, Int?, Int?) -> Void + ) { + self.clickDate = clickDate + super.init(nibName: nil, bundle: nil) + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func viewDidLoad() { + view.backgroundColor = .clear + attribute() + layout() + } + public override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.dismiss(animated: true) + } + + private func attribute() { + calendar.selectionBehavior = UICalendarSelectionSingleDate(delegate: self) + } + private func layout() { + view.addSubviews([ + calendar + ]) + + calendar.snp.makeConstraints { + $0.center.equalToSuperview() + $0.width.equalTo(390) + $0.height.equalTo(348) + } + } + +} + +extension MGTargetAlertView: UICalendarSelectionSingleDateDelegate { + public func dateSelection( + _ selection: UICalendarSelectionSingleDate, + didSelectDate dateComponents: DateComponents? + ) { + if let date = dateComponents { + self.clickDate(date.year, date.month, date.day) + } else { + print("empty date") + } + } +} diff --git a/Projects/Features/SelfCareFeature/Sources/Supporter/Target/Main/TargetMainTableViewCell.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Cell/TargetMainTableViewCell.swift similarity index 73% rename from Projects/Features/SelfCareFeature/Sources/Supporter/Target/Main/TargetMainTableViewCell.swift rename to Projects/Features/SelfCareFeature/Sources/TargetScene/View/Cell/TargetMainTableViewCell.swift index b1619ebb..b4c06bf2 100644 --- a/Projects/Features/SelfCareFeature/Sources/Supporter/Target/Main/TargetMainTableViewCell.swift +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/Cell/TargetMainTableViewCell.swift @@ -3,30 +3,40 @@ import UIKit import SnapKit import Then +import RxSwift +import RxCocoa + import Core import DSKit import Domain public class TargetMainTableViewCell: BaseTableViewCell { + public var dotButtonTap: ControlEvent { + return dotsButton.rx.tap + } + public static let identifier: String = "TargetMainTableViewCell" private var containerView = UIView().then { - $0.backgroundColor = DSKitAsset.Colors.blue50.color + $0.backgroundColor = .blue50 $0.layer.cornerRadius = 16.0 } - private var targetNameLabel = MGLabel(font: UIFont.Pretendard.labelLarge, - textColor: .black, - numberOfLineCount: 1 + private var targetNameLabel = MGLabel( + font: UIFont.Pretendard.labelLarge, + textColor: .black, + isCenter: false, + numberOfLineCount: 1 ) - private var targetPeriodLabel = MGLabel(font: UIFont.Pretendard.bodySmall, - textColor: DSKitAsset.Colors.gray400.color, - numberOfLineCount: 1 + private var targetPeriodLabel = MGLabel( + font: UIFont.Pretendard.bodySmall, + textColor: .gray400, + numberOfLineCount: 1 ) - private let dotsButton = MGImageButton(image: DSKitAsset.Assets.dotsActIcon.image) + private let dotsButton = MGImageButton(image: .grayDotsActIcon) public func setup(with model: TargetContentModel) { changeTargetName(text: model.targetTitle) @@ -62,7 +72,7 @@ public class TargetMainTableViewCell: BaseTableViewCell { dotsButton.snp.makeConstraints { $0.top.bottom.equalToSuperview().inset(29.0) - $0.trailing.equalToSuperview().offset(-20.0) + $0.trailing.equalToSuperview().inset(20.0) $0.width.height.equalTo(24.0) } } diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGDateSelectView.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGDateSelectView.swift new file mode 100644 index 00000000..9a1d5604 --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGDateSelectView.swift @@ -0,0 +1,76 @@ +import UIKit + +import SnapKit +import Then + +import RxSwift +import RxCocoa + +import Core +import DSKit +import Domain + +public class MGDateSelectView: BaseView { + + public var dateButtonTap: ControlEvent { + return dateSelectButton.rx.tap + } + + private let todayDate = Date() + + private var dateButtonBgColor: UIColor { + dateSelectButton.isSelected ? .blue50 : .gray25 + } + private var dateButtonBdColor: CGColor { + dateSelectButton.isSelected ? UIColor.blue100.cgColor : UIColor.gray50.cgColor + } + + private let typeLabel = UILabel().then { + $0.textColor = .black + $0.font = UIFont.Pretendard.bodyMedium + } + private var dateSelectButton = UIButton(type: .custom).then { + $0.setTitleColor(.black, for: .normal) + $0.titleLabel?.font = UIFont.Pretendard.bodyLarge + $0.contentHorizontalAlignment = .leading + $0.titleEdgeInsets = .init(top: 0, left: 12, bottom: 0, right: 0) + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + } + public init( + typeText: String + ) { + super.init(frame: .zero) + self.typeLabel.text = typeText + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + public func setup( + date: [Int] + ) { + self.dateSelectButton.setTitle("\(date[0])년 \(date[1])월 \(date[2])일", for: .normal) + } + public override func attribute() { + dateSelectButton.backgroundColor = dateButtonBgColor + dateSelectButton.layer.borderColor = dateButtonBdColor + } + public override func layout() { + self.addSubviews([ + typeLabel, + dateSelectButton + ]) + + self.snp.makeConstraints { + $0.height.equalTo(76) + } + typeLabel.snp.makeConstraints { + $0.top.leading.equalToSuperview() + } + dateSelectButton.snp.makeConstraints { + $0.top.equalTo(typeLabel.snp.bottom).offset(8) + $0.leading.trailing.bottom.equalToSuperview() + } + } + +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGSelfCareTargetDateBannerView.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGSelfCareTargetDateBannerView.swift new file mode 100644 index 00000000..cc1854fc --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/View/MGSelfCareTargetDateBannerView.swift @@ -0,0 +1,57 @@ +import UIKit + +import SnapKit +import Then + +import Core +import DSKit +import Domain + +public class MGSelfCareTargetDateBannerView: BaseView { + + private let lineView = MGLine(lineColor: .blue500, lineWidth: 2).then { + $0.layer.cornerRadius = 1 + } + private let typeLabel = MGLabel(font: UIFont.Pretendard.bodyMedium, textColor: .gray600) + private let dateLabel = MGLabel(font: UIFont.Pretendard.bodyMedium, textColor: .gray600) + +// public override func attribute() { +// <#code#> +// } + + public init( + typeText: String, + dateText: String + ) { + super.init(frame: .zero) + self.typeLabel.text = typeText + self.dateLabel.text = dateText + } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public override func layout() { + self.addSubviews([ + lineView, + typeLabel, + dateLabel + ]) + + self.snp.makeConstraints { + $0.height.equalTo(20) + } + lineView.snp.makeConstraints { + $0.top.leading.bottom.equalToSuperview() + } + typeLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.leading.equalTo(lineView.snp.trailing).offset(10) + } + dateLabel.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.leading.equalTo(typeLabel.snp.leading).offset(48) + } + } + +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareAddTargetViewModel.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareAddTargetViewModel.swift new file mode 100644 index 00000000..c6aa1944 --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareAddTargetViewModel.swift @@ -0,0 +1,32 @@ +import Foundation + +import RxFlow +import RxCocoa +import RxSwift + +import Core +import Domain +import MGLogger + +public class SelfCareAddTargetViewModel: BaseViewModel { + + public typealias ViewModel = SelfCareAddTargetViewModel + + private let disposeBag = DisposeBag() + + private let useCase: SelfCareUseCase + + public init(useCase: SelfCareUseCase) { + self.useCase = useCase + } + + public struct Input { + } + + public struct Output { + } + + public func transform(_ input: Input, action: (Output) -> Void) -> Output { + return Output() + } +} diff --git a/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareDetailTargetViewModel.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareDetailTargetViewModel.swift new file mode 100644 index 00000000..c3c63ec7 --- /dev/null +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareDetailTargetViewModel.swift @@ -0,0 +1,32 @@ +import Foundation + +import RxFlow +import RxCocoa +import RxSwift + +import Core +import Domain +import MGLogger + +public class SelfCareDetailTargetViewModel: BaseViewModel { + + public typealias ViewModel = SelfCareDetailTargetViewModel + + private let disposeBag = DisposeBag() + + private let useCase: SelfCareUseCase + + public init(useCase: SelfCareUseCase) { + self.useCase = useCase + } + + public struct Input { + } + + public struct Output { + } + + public func transform(_ input: Input, action: (Output) -> Void) -> Output { + return Output() + } +} diff --git a/Projects/Features/SelfCareFeature/Sources/SelfCareScene/ViewModel/Target/SelfCareTargetMainViewModel.swift b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareTargetMainViewModel.swift similarity index 99% rename from Projects/Features/SelfCareFeature/Sources/SelfCareScene/ViewModel/Target/SelfCareTargetMainViewModel.swift rename to Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareTargetMainViewModel.swift index 2a7d1b42..16c2ede8 100644 --- a/Projects/Features/SelfCareFeature/Sources/SelfCareScene/ViewModel/Target/SelfCareTargetMainViewModel.swift +++ b/Projects/Features/SelfCareFeature/Sources/TargetScene/ViewModel/SelfCareTargetMainViewModel.swift @@ -9,7 +9,7 @@ import Domain import MGLogger public class SelfCareTargetMainViewModel: BaseViewModel { - + public typealias ViewModel = SelfCareTargetMainViewModel private let disposeBag = DisposeBag() diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MGSelfCareTextField.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MGSelfCareTextField.swift index e9d43ba0..beed04d3 100644 --- a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MGSelfCareTextField.swift +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextField/MGSelfCareTextField.swift @@ -12,8 +12,6 @@ open class MGSelfCareTextField: UITextField { private let disposeBag = DisposeBag() - public var errorMessage = PublishRelay() - private lazy var leftPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: self.frame.height)) private lazy var rightPaddingView = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: self.frame.height)) @@ -29,7 +27,9 @@ open class MGSelfCareTextField: UITextField { public init( typeText: String, keyboardType: UIKeyboardType, - unitText: String? = nil + unitText: String? = nil, + placeholderText : String? = nil, + placeholderTextColor: UIColor? = nil ) { super.init(frame: .zero) @@ -37,6 +37,8 @@ open class MGSelfCareTextField: UITextField { self.placeholder = typeText self.keyboardType = keyboardType self.unitLabel.text = unitText + self.placeholder = placeholderText + self.setPlaceholder(textColor: placeholderTextColor ?? .gray200) setup() bind() @@ -48,7 +50,6 @@ open class MGSelfCareTextField: UITextField { public override func layoutSubviews() { super.layoutSubviews() layout() - setPlaceholder() } private func setup() { @@ -99,14 +100,14 @@ extension MGSelfCareTextField { .disposed(by: disposeBag) } - private func setPlaceholder() { + private func setPlaceholder(textColor: UIColor) { guard let string = self.placeholder else { return } attributedPlaceholder = NSAttributedString( string: string, attributes: [ - .foregroundColor: DSKitAsset.Colors.gray200.color, + .foregroundColor: textColor, .font: UIFont.Pretendard.bodyLarge ] ) diff --git a/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextView/MgTextView.swift b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextView/MgTextView.swift new file mode 100644 index 00000000..0e0a2c77 --- /dev/null +++ b/Projects/Modules/DSKit/Sources/Components/MaeumGaGymTextView/MgTextView.swift @@ -0,0 +1,108 @@ +import UIKit + +import Then +import SnapKit + +import RxSwift +import RxCocoa + +import Core + +open class MGTextView: UITextView { + + private let disposeBag = DisposeBag() + + private var isEditing: Bool = false { + didSet { + self.attribute() + } + } + + private var bgColor: UIColor { + isEditing ? .blue50 : .gray25 + } + private var bdColor: CGColor { + isEditing ? UIColor.blue100.cgColor : UIColor.gray50.cgColor + } + + private let typeLabel = UILabel().then { + $0.text = "내용" + $0.textColor = .black + $0.font = UIFont.Pretendard.bodyMedium + } + private let placeholderLabel = UILabel().then { + $0.text = "내용을 입력해주세요" + $0.textColor = .gray400 + $0.font = UIFont.Pretendard.bodyLarge + $0.isHidden = false + } + + public override init(frame: CGRect, textContainer: NSTextContainer?) { + super.init(frame: frame, textContainer: textContainer) + attribute() + setup() + bind() + } + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + open override func layoutSubviews() { + super.layoutSubviews() + layout() + } + + private func attribute() { + self.backgroundColor = bgColor + self.layer.borderColor = bdColor + } + private func setup() { + self.textColor = .black + self.font = UIFont.Pretendard.bodyLarge + self.textContainer.maximumNumberOfLines = 0 + self.layer.cornerRadius = 8 + self.layer.borderWidth = 1 + self.autocorrectionType = .no + self.autocapitalizationType = .none + self.textContainerInset = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12) + } + private func layout() { + self.addSubviews([ + typeLabel, + placeholderLabel + ]) + + typeLabel.snp.makeConstraints { + $0.bottom.equalTo(self.snp.top).inset(-8) + $0.leading.equalToSuperview() + } + placeholderLabel.snp.makeConstraints { + $0.top.equalToSuperview().inset(12) + $0.leading.equalToSuperview().inset(14) + } + } + +} + +extension MGTextView { + private func bind() { + self.rx.didBeginEditing + .bind { [weak self] in + self?.isEditing = true + }.disposed(by: disposeBag) + + self.rx.didChange + .bind { [weak self] in + self?.placeholderLabel.isHidden = true + }.disposed(by: disposeBag) + + self.rx.didEndEditing + .bind { [weak self] in + self?.isEditing = false + + if self?.text.isEmpty == true { + self?.placeholderLabel.isHidden = false + } + }.disposed(by: disposeBag) + } + +} diff --git a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift index 88589899..33a44ebd 100644 --- a/Tuist/ProjectDescriptionHelpers/Project+Templates.swift +++ b/Tuist/ProjectDescriptionHelpers/Project+Templates.swift @@ -18,7 +18,7 @@ public extension Project { let configurationName: ConfigurationName = "Development" let hasDynamicFramework = targets.contains(.dynamicFramework) - let deploymentTarget = DeploymentTarget.iOS(targetVersion: "15.0", devices: .iphone) + let deploymentTarget = DeploymentTarget.iOS(targetVersion: "16.0", devices: .iphone) let platform = Environment.platform let baseSettings: SettingsDictionary = .baseSettings.setCodeSignManual()