From e6c9c9a5c0a2c4a1a8004706e35e842be6065d68 Mon Sep 17 00:00:00 2001 From: heejoo Date: Thu, 11 Jan 2024 22:17:07 +0900 Subject: [PATCH 01/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=A0=84=20=EB=B7=B0?= =?UTF-8?q?=EB=93=A4=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?delegate=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JoinAgreementViewController.swift | 1 + .../JoinProfileViewController.swift | 2 ++ .../OnboardingEndingViewController.swift | 14 +++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift index f42235e8..2b3475c2 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift @@ -163,6 +163,7 @@ extension JoinAgreementViewController { output.pushViewController .sink { _ in let viewController = JoinProfileViewController(viewModel: JoinProfileViewModel()) + self.navigationBackButton.removeFromSuperview() self.navigationController?.pushViewController(viewController, animated: true) } .store(in: self.cancelBag) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift index ce6f067d..b80a41d5 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift @@ -105,6 +105,7 @@ extension JoinProfileViewController { UserDefaults.standard.set(userNickname.userNickname, forKey: "nickname") let viewContoller = OnboardingViewController() + self.navigationBackButton.removeFromSuperview() self.navigationController?.pushViewController(viewContoller, animated: true) } } @@ -112,6 +113,7 @@ extension JoinProfileViewController { output.isEnable .sink { isTrue in + self.originView.nickNameTextField.resignFirstResponder() self.originView.finishActiveButton.isHidden = !isTrue if isTrue { self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index 35292ada..526cae0e 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -68,6 +68,7 @@ final class OnboardingEndingViewController: UIViewController { setUI() setHierarchy() setLayout() + setDelegate() bindViewModel() } @@ -140,6 +141,10 @@ extension OnboardingEndingViewController { } } + private func setDelegate() { + self.introductionView.introduction.delegate = self + } + private func bindViewModel() { let input = OnboardingEndingViewModel.Input(startButtonTapped: startButtonTapped, backButtonTapped: backButtonTapped) @@ -154,8 +159,15 @@ extension OnboardingEndingViewController { } else { self.navigationController?.popViewController(animated: true) } - } .store(in: self.cancelBag) } } + +extension OnboardingEndingViewController: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } +} From f526d1c1166dc2d18e2a87cb8167674df7c234ec Mon Sep 17 00:00:00 2001 From: heejoo Date: Thu, 11 Jan 2024 22:34:36 +0900 Subject: [PATCH 02/25] =?UTF-8?q?[Feat]=20#33=20-=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=ED=95=98=EB=8A=94=20=EC=9C=A0=EC=A0=80,=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=20=EA=B0=80=EC=9E=85=ED=95=9C=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EB=B6=84=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OnboardingEndingViewController.swift | 24 ++++++++++++------- .../OnboardingViewController.swift | 24 ++++++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index 526cae0e..a4eb7eda 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -18,6 +18,7 @@ final class OnboardingEndingViewController: UIViewController { private let viewModel: OnboardingEndingViewModel private lazy var startButtonTapped = self.startButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() private lazy var backButtonTapped = self.backButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() + private let isExistUser: Bool = true // MARK: - UI Components @@ -130,14 +131,21 @@ extension OnboardingEndingViewController { $0.top.equalTo(profileImage).offset(50.adjusted) } - startButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - } - - skipButton.snp.makeConstraints { - $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() + if isExistUser { + startButton.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + } + + skipButton.snp.makeConstraints { + $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() + } + } else { + startButton.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift index 8e555bf9..f9cf2267 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift @@ -15,6 +15,7 @@ final class OnboardingViewController: UIViewController { static var pushCount: Int = 0 private let dummy = OnboardingDummy.dummy() + private let isExistUser: Bool = true // MARK: - UI Components @@ -139,14 +140,21 @@ extension OnboardingViewController { } } - nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - } - - skipButton.snp.makeConstraints { - $0.top.equalTo(nextButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() + if isExistUser { + nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + } + + skipButton.snp.makeConstraints { + $0.top.equalTo(nextButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() + } + } else { + nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } } } From 1babbf16c3a36d054ecef50de973f11bb7e17456 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 02:01:49 +0900 Subject: [PATCH 03/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=86=8C=EC=85=9C?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EC=98=A8=EB=B3=B4=EB=94=A9=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Application/SceneDelegate.swift | 16 +++++++++++-- .../Presentation/Helpers/UserInfo.swift | 24 ++++++++++++++++++- .../JoinProfileViewController.swift | 8 +++---- .../ViewControllers/LoginViewController.swift | 10 +++++++- .../OnboardingEndingViewController.swift | 12 ++++++---- .../OnboardingViewController.swift | 3 +-- .../Onboarding/Views/IntroductionView.swift | 2 +- 7 files changed, 60 insertions(+), 15 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Application/SceneDelegate.swift b/DontBe-iOS/DontBe-iOS/Application/SceneDelegate.swift index 3c1c3043..dcf3ca7d 100644 --- a/DontBe-iOS/DontBe-iOS/Application/SceneDelegate.swift +++ b/DontBe-iOS/DontBe-iOS/Application/SceneDelegate.swift @@ -20,9 +20,21 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.window?.makeKeyAndVisible() DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) { - let navigationController = UINavigationController(rootViewController: LoginViewController(viewModel: LoginViewModel())) +// let navigationController = UINavigationController(rootViewController: LoginViewController(viewModel: LoginViewModel())) // let navigationController = UINavigationController(rootViewController: DontBeTabBarController()) - self.window?.rootViewController = navigationController + if loadUserData()?.isSocialLogined == true && loadUserData()?.isJoinedApp == true && loadUserData()?.isOnboardingFinished == true { + let navigationController = UINavigationController(rootViewController: DontBeTabBarController()) + self.window?.rootViewController = navigationController + } else if loadUserData()?.isJoinedApp == false { + let navigationController = UINavigationController(rootViewController: LoginViewController(viewModel: LoginViewModel())) + self.window?.rootViewController = navigationController + } else if loadUserData()?.isOnboardingFinished == false { + let navigationController = UINavigationController(rootViewController: OnboardingViewController()) + self.window?.rootViewController = navigationController + } else { + let navigationController = UINavigationController(rootViewController: LoginViewController(viewModel: LoginViewModel())) + self.window?.rootViewController = navigationController + } self.window?.makeKeyAndVisible() } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift b/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift index f0088b67..5e4c5124 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift @@ -7,6 +7,28 @@ import Foundation -struct UserInfo { +struct UserInfo: Codable { + let isSocialLogined: Bool + let isJoinedApp: Bool + let isOnboardingFinished: Bool let userNickname: String } + +// 구조체를 UserDefault에 저장하는 함수 +func saveUserData(_ userData: UserInfo) { + let encoder = JSONEncoder() + if let encodedData = try? encoder.encode(userData) { + UserDefaults.standard.set(encodedData, forKey: "saveUserInfo") + } +} + +// UserDefault에서 구조체를 가져오는 함수 +func loadUserData() -> UserInfo? { + if let encodedData = UserDefaults.standard.data(forKey: "saveUserInfo") { + let decoder = JSONDecoder() + if let userData = try? decoder.decode(UserInfo.self, from: encodedData) { + return userData + } + } + return nil +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift index b80a41d5..ce1d4724 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift @@ -99,10 +99,10 @@ extension JoinProfileViewController { if value == 0 { self.navigationController?.popViewController(animated: true) } else { - // UserInfo 인스턴스 생성 - let userNickname = UserInfo(userNickname: self.originView.nickNameTextField.text ?? "") - // Local DB에 저장 - UserDefaults.standard.set(userNickname.userNickname, forKey: "nickname") + saveUserData(UserInfo(isSocialLogined: true, + isJoinedApp: true, + isOnboardingFinished: false, + userNickname: self.originView.nickNameTextField.text ?? "")) let viewContoller = OnboardingViewController() self.navigationBackButton.removeFromSuperview() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift index c7f04c69..3dc6b039 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift @@ -116,7 +116,15 @@ extension LoginViewController { // .receive(on: RunLoop.main) .sink { userInfo in print(userInfo) - let viewController = JoinAgreementViewController(viewModel: JoinAgreeViewModel()) + // 서버통신 -> 첫 로그인 유저면 여기 + let viewController = JoinAgreementViewController(viewModel: JoinAgreeViewModel()) + saveUserData(UserInfo(isSocialLogined: true, + isJoinedApp: false, + isOnboardingFinished: false, + userNickname: "")) + // 서버통신 -> 이미 로그인 유저면 +// let viewController = OnboardingViewController() +// saveUserData(UserInfo(isExist: true, userNickname: "벼니주")) // 서버통신 후에 실제 닉네임을 넣음 (뷰모델에서) self.navigationController?.pushViewController(viewController, animated: true) } .store(in: self.cancelBag) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index a4eb7eda..800c5b35 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -16,10 +16,14 @@ final class OnboardingEndingViewController: UIViewController { private var cancelBag = CancelBag() private let viewModel: OnboardingEndingViewModel - private lazy var startButtonTapped = self.startButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() + private lazy var startButtonTapped = self.startButton.publisher(for: .touchUpInside).map { _ + in saveUserData(UserInfo(isSocialLogined: loadUserData()?.isSocialLogined ?? true, + isJoinedApp: true, + isOnboardingFinished: true, + userNickname: loadUserData()?.userNickname ?? "")) + }.eraseToAnyPublisher() private lazy var backButtonTapped = self.backButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() - private let isExistUser: Bool = true - + // MARK: - UI Components private let progressImage: UIImageView = { @@ -131,7 +135,7 @@ extension OnboardingEndingViewController { $0.top.equalTo(profileImage).offset(50.adjusted) } - if isExistUser { + if loadUserData()?.isSocialLogined == true { startButton.snp.makeConstraints { $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) $0.centerX.equalToSuperview() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift index f9cf2267..3489f540 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift @@ -15,7 +15,6 @@ final class OnboardingViewController: UIViewController { static var pushCount: Int = 0 private let dummy = OnboardingDummy.dummy() - private let isExistUser: Bool = true // MARK: - UI Components @@ -140,7 +139,7 @@ extension OnboardingViewController { } } - if isExistUser { + if loadUserData()?.isSocialLogined == true { nextButton.snp.makeConstraints { $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) $0.centerX.equalToSuperview() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift index 7a4f2db7..b961b246 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift @@ -17,7 +17,7 @@ final class IntroductionView: UIView { private let nickName: UILabel = { let nickName = UILabel() - nickName.text = UserDefaults.standard.string(forKey: "nickname") + nickName.text = loadUserData()?.userNickname nickName.textColor = .donBlack nickName.font = .font(.head3) return nickName From 693946b86947bda731477bbe9970835472a597d1 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 03:38:37 +0900 Subject: [PATCH 04/25] =?UTF-8?q?[Feat]=20#33=20-=20=EA=B1=B4=EB=84=88?= =?UTF-8?q?=EB=9B=B0=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=98=A8=EB=B3=B4=EB=94=A9=20?= =?UTF-8?q?=EB=A7=88=EB=AC=B4=EB=A6=AC=20=EB=B7=B0,=20=EB=B7=B0=EC=BB=A8?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 4 + .../JoinAgreementViewController.swift | 2 +- .../JoinProfileViewController.swift | 8 +- .../OnboardingEndingViewController.swift | 160 +++++++----------- .../OnboardingViewController.swift | 7 + .../OnboardingEndingViewModel.swift | 18 +- .../Views/OnboardingEndingView.swift | 142 ++++++++++++++++ 7 files changed, 236 insertions(+), 105 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index b8e66c34..b75a2d16 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ 2A84465A2B4EE8B400F98F2A /* JoinProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8446592B4EE8B400F98F2A /* JoinProfileViewController.swift */; }; 2A84465C2B4EEC6C00F98F2A /* JoinProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A84465B2B4EEC6C00F98F2A /* JoinProfileViewModel.swift */; }; 2A84465E2B4EF41900F98F2A /* JoinProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A84465D2B4EF41900F98F2A /* JoinProfileView.swift */; }; + 2A8868D62B5069790017513A /* OnboardingEndingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8868D52B5069790017513A /* OnboardingEndingView.swift */; }; 2A8D70B12B4C4D8F009F4C6C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 2A8D70B02B4C4D8F009F4C6C /* KakaoSDK */; }; 2A8D70B42B4C999F009F4C6C /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */; }; 2A8D70B82B4C9A59009F4C6C /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8D70B72B4C9A59009F4C6C /* OnboardingViewController.swift */; }; @@ -94,6 +95,7 @@ 2A8446592B4EE8B400F98F2A /* JoinProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinProfileViewController.swift; sourceTree = ""; }; 2A84465B2B4EEC6C00F98F2A /* JoinProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinProfileViewModel.swift; sourceTree = ""; }; 2A84465D2B4EF41900F98F2A /* JoinProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinProfileView.swift; sourceTree = ""; }; + 2A8868D52B5069790017513A /* OnboardingEndingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingEndingView.swift; sourceTree = ""; }; 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = ""; }; 2A8D70B72B4C9A59009F4C6C /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; 2A8D70BC2B4D61A1009F4C6C /* OnboardingDummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingDummy.swift; sourceTree = ""; }; @@ -241,6 +243,7 @@ isa = PBXGroup; children = ( 2A8D70C92B4D9787009F4C6C /* IntroductionView.swift */, + 2A8868D52B5069790017513A /* OnboardingEndingView.swift */, ); path = Views; sourceTree = ""; @@ -630,6 +633,7 @@ 2A8D70CA2B4D9787009F4C6C /* IntroductionView.swift in Sources */, 3C6193172B3A7A7B00220CEB /* UIStackView+.swift in Sources */, 3CE9C12E2B4C08AE0086E4A3 /* WriteTextView.swift in Sources */, + 2A8868D62B5069790017513A /* OnboardingEndingView.swift in Sources */, 2A8D70BD2B4D61A1009F4C6C /* OnboardingDummy.swift in Sources */, 3C6193152B3A7A6400220CEB /* UIView+.swift in Sources */, 2AC9FB1F2B4E634A00D31071 /* JoinAgreeView.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift index 2b3475c2..bc405068 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinAgreementViewController.swift @@ -69,7 +69,7 @@ final class JoinAgreementViewController: UIViewController { extension JoinAgreementViewController { private func setUI() { - self.view.backgroundColor = .donWhite + self.view.backgroundColor = .donGray1 self.navigationItem.title = StringLiterals.Join.joinNavigationTitle } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift index ce1d4724..c239b24c 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift @@ -70,7 +70,7 @@ final class JoinProfileViewController: UIViewController { extension JoinProfileViewController { private func setUI() { - self.view.backgroundColor = .donWhite + self.view.backgroundColor = .donGray1 self.navigationItem.title = StringLiterals.Join.joinNavigationTitle } @@ -130,6 +130,12 @@ extension JoinProfileViewController { // MARK: - UITextFieldDelegate extension JoinProfileViewController: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let maxLength = 12 // 글자수 제한 let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index 800c5b35..3f47ebb5 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -16,42 +16,21 @@ final class OnboardingEndingViewController: UIViewController { private var cancelBag = CancelBag() private let viewModel: OnboardingEndingViewModel - private lazy var startButtonTapped = self.startButton.publisher(for: .touchUpInside).map { _ - in saveUserData(UserInfo(isSocialLogined: loadUserData()?.isSocialLogined ?? true, - isJoinedApp: true, + + private lazy var startButtonTapped = self.originView.startButton.publisher(for: .touchUpInside).map { _ + in saveUserData(UserInfo(isSocialLogined: + loadUserData()?.isSocialLogined ?? true, + isJoinedApp: true, isOnboardingFinished: true, userNickname: loadUserData()?.userNickname ?? "")) }.eraseToAnyPublisher() - private lazy var backButtonTapped = self.backButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() + private lazy var skipButtonTapped = self.originView.skipButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() + private lazy var backButtonTapped = self.originView.backButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() // MARK: - UI Components - private let progressImage: UIImageView = { - let progress = UIImageView() - progress.image = ImageLiterals.Onboarding.progressbar4 - return progress - }() - - private let titleImage: UIImageView = { - let title = UIImageView() - title.image = ImageLiterals.Onboarding.imgFourthTitle - title.contentMode = .scaleAspectFit - return title - }() - - private let profileImage: UIImageView = { - let profile = UIImageView() - profile.image = ImageLiterals.Common.imgProfile - return profile - }() + private let originView = OnboardingEndingView() - private let introductionView = IntroductionView() - - private let backButton = BackButton() - private let startButton = CustomButton(title: StringLiterals.Button.start, backColor: .donPrimary, titleColor: .donBlack) - - private let skipButton = CustomButton(title: StringLiterals.Button.skip, backColor: .clear, titleColor: .donGray7) - override func touchesBegan(_ touches: Set, with event: UIEvent?) { view.endEditing(true) } @@ -67,12 +46,16 @@ final class OnboardingEndingViewController: UIViewController { fatalError("init(coder:) has not been implemented") } + override func loadView() { + super.loadView() + + view = originView + } + override func viewDidLoad() { super.viewDidLoad() setUI() - setHierarchy() - setLayout() setDelegate() bindViewModel() } @@ -92,84 +75,26 @@ extension OnboardingEndingViewController { self.view.backgroundColor = .donGray1 } - private func setHierarchy() { - self.view.addSubviews(backButton, - progressImage, - titleImage, - profileImage, - introductionView, - startButton, - skipButton) - self.view.bringSubviewToFront(profileImage) - } - - private func setLayout() { - backButton.snp.makeConstraints { - $0.top.equalToSuperview().inset(statusBarHeight + 38.adjusted) - $0.leading.equalToSuperview().inset(23.adjusted) - } - - progressImage.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.top.equalToSuperview().inset(statusBarHeight + 46.adjusted) - $0.width.equalTo(48.adjusted) - $0.height.equalTo(6.adjusted) - } - - titleImage.snp.makeConstraints { - $0.leading.trailing.equalToSuperview().inset(91.adjusted) - $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) - $0.height.equalTo(72.adjusted) - } - - profileImage.snp.makeConstraints { - $0.size.equalTo(100.adjusted) - $0.centerX.equalToSuperview() - $0.top.equalToSuperview().inset(statusBarHeight + 201.adjustedH) - } - - introductionView.snp.makeConstraints { - $0.width.equalTo(320.adjusted) - $0.height.equalTo(211.adjusted) - $0.centerX.equalToSuperview() - $0.top.equalTo(profileImage).offset(50.adjusted) - } - - if loadUserData()?.isSocialLogined == true { - startButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - } - - skipButton.snp.makeConstraints { - $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() - } - } else { - startButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() - } - } - } - private func setDelegate() { - self.introductionView.introduction.delegate = self + self.originView.introductionView.introduction.delegate = self } private func bindViewModel() { - let input = OnboardingEndingViewModel.Input(startButtonTapped: startButtonTapped, backButtonTapped: backButtonTapped) + let input = OnboardingEndingViewModel.Input( + backButtonTapped: backButtonTapped, + startButtonTapped: startButtonTapped, + skipButtonTapped: skipButtonTapped) let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag) output.voidPublisher .sink { value in - if value == "start" { + if value == "back" { + self.navigationController?.popViewController(animated: true) + } else { let viewController = DontBeTabBarController() - print(self.introductionView.introduction.text ?? "") // 텍스트 필드 텍스트 잘 넘어오는지 확인 + print(self.originView.introductionView.introduction.text ?? "") // 텍스트 필드 텍스트 잘 넘어오는지 확인 self.navigationController?.pushViewController(viewController, animated: true) - } else { - self.navigationController?.popViewController(animated: true) } } .store(in: self.cancelBag) @@ -182,4 +107,43 @@ extension OnboardingEndingViewController: UITextFieldDelegate { textField.resignFirstResponder() return true } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text + let addedText = string // 입력한 text + let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 + let newTextLength = newText.count // 합쳐진 text의 길이 + + // 글자수 제한 + if newTextLength > 0 { + return true + } + + let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 + let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 + let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 + + if separatedCharactersCount == 1 && !addedText.isConsonant { + return true + } else if separatedCharactersCount == 2 && addedText.isConsonant { + return true + } else if separatedCharactersCount == 3 && addedText.isConsonant { + return true + } + return false + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" // textField에 수정이 반영된 후의 text + if text.count > 0 { + self.originView.startButton.isEnabled = true + self.originView.startButton.setTitleColor(.donBlack, for: .normal) + self.originView.startButton.backgroundColor = .donPrimary + } else { + self.originView.startButton.isEnabled = false + self.originView.startButton.setTitleColor(.donGray9, for: .normal) + self.originView.startButton.backgroundColor = .donGray4 + } + } } + diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift index 3489f540..e83dbe25 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift @@ -159,6 +159,7 @@ extension OnboardingViewController { private func setAddTarget() { backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside) + skipButton.addTarget(self, action: #selector(skipButtonTapped), for: .touchUpInside) nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside) } @@ -187,4 +188,10 @@ extension OnboardingViewController { OnboardingViewController.pushCount -= 1 self.navigationController?.popViewController(animated: true) } + + @objc + private func skipButtonTapped() { + let viewController = OnboardingEndingViewController(viewModel: OnboardingEndingViewModel()) + self.navigationController?.pushViewController(viewController, animated: true) + } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewModels/OnboardingEndingViewModel.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewModels/OnboardingEndingViewModel.swift index 3e150c0d..eb879d7e 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewModels/OnboardingEndingViewModel.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewModels/OnboardingEndingViewModel.swift @@ -12,8 +12,9 @@ final class OnboardingEndingViewModel: ViewModelType { private let cancelBag = CancelBag() struct Input { - let startButtonTapped: AnyPublisher let backButtonTapped: AnyPublisher + let startButtonTapped: AnyPublisher + let skipButtonTapped: AnyPublisher } struct Output { @@ -23,6 +24,13 @@ final class OnboardingEndingViewModel: ViewModelType { func transform(from input: Input, cancelBag: CancelBag) -> Output { let publisher = PassthroughSubject() + input.backButtonTapped + .sink { _ in + // back 버튼 누르면 바로 신호보냄 + publisher.send("back") + } + .store(in: self.cancelBag) + input.startButtonTapped .sink { _ in // 온보딩 완료 서버통신 @@ -31,10 +39,10 @@ final class OnboardingEndingViewModel: ViewModelType { } .store(in: self.cancelBag) - input.backButtonTapped - .sink { _ in - // back 버튼 누르면 바로 신호보냄 - publisher.send("back") + input.skipButtonTapped + .sink { _ in + // 이때는 서버통신 X + publisher.send("skip") } .store(in: self.cancelBag) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift new file mode 100644 index 00000000..86b772fd --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift @@ -0,0 +1,142 @@ +// +// OnboardingEndingView.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import UIKit + +import SnapKit + +final class OnboardingEndingView: UIView { + + // MARK: - Properties + + // MARK: - UI Components + private let progressImage: UIImageView = { + let progress = UIImageView() + progress.image = ImageLiterals.Onboarding.progressbar4 + return progress + }() + + private let titleImage: UIImageView = { + let title = UIImageView() + title.image = ImageLiterals.Onboarding.imgFourthTitle + title.contentMode = .scaleAspectFit + return title + }() + + private let profileImage: UIImageView = { + let profile = UIImageView() + profile.image = ImageLiterals.Common.imgProfile + return profile + }() + + let introductionView = IntroductionView() + + let backButton = BackButton() + + let startButton: UIButton = { + let startButton = UIButton() + startButton.setTitle(StringLiterals.Button.start, for: .normal) + startButton.titleLabel?.font = .font(.body3) + startButton.backgroundColor = .donGray4 + startButton.setTitleColor(.donGray9, for: .normal) + startButton.layer.cornerRadius = 6.adjusted + startButton.isEnabled = false + return startButton + }() + + let skipButton = CustomButton(title: StringLiterals.Button.skip, backColor: .clear, titleColor: .donGray7) + + // MARK: - Life Cycles + + override init(frame: CGRect) { + super.init(frame: frame) + + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extensions + +extension OnboardingEndingView { + private func setHierarchy() { + self.addSubviews(backButton, + progressImage, + titleImage, + profileImage, + introductionView, + startButton, + skipButton) + self.bringSubviewToFront(profileImage) + } + + private func setLayout() { + let statusBarHeight = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first? + .statusBarManager? + .statusBarFrame.height ?? 20 + + backButton.snp.makeConstraints { + $0.top.equalToSuperview().inset(statusBarHeight + 38.adjusted) + $0.leading.equalToSuperview().inset(23.adjusted) + } + + progressImage.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalToSuperview().inset(statusBarHeight + 46.adjusted) + $0.width.equalTo(48.adjusted) + $0.height.equalTo(6.adjusted) + } + + titleImage.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(91.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) + $0.height.equalTo(72.adjusted) + } + + profileImage.snp.makeConstraints { + $0.size.equalTo(100.adjusted) + $0.centerX.equalToSuperview() + $0.top.equalToSuperview().inset(statusBarHeight + 201.adjustedH) + } + + introductionView.snp.makeConstraints { + $0.width.equalTo(320.adjusted) + $0.height.equalTo(211.adjusted) + $0.centerX.equalToSuperview() + $0.top.equalTo(profileImage).offset(50.adjusted) + } + + if loadUserData()?.isSocialLogined == true { + startButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + $0.width.equalTo(342.adjusted) + $0.height.equalTo(50.adjusted) + } + + skipButton.snp.makeConstraints { + $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() + } + } else { + startButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + $0.width.equalTo(342.adjusted) + $0.height.equalTo(50.adjusted) + } + } + } +} + From ec00fa109ab0a8a870911d77799aac81decc9e90 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 03:52:46 +0900 Subject: [PATCH 05/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B7=B0=EC=BB=A8=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 20 +++++++++ .../Join/Views/JoinProfileView.swift | 5 --- .../NotificationViewController.swift | 42 +++++++++++++++++++ .../TabBar/DontBeTabBarItem.swift | 4 +- 4 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index b75a2d16..c475be0b 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ 2A84465C2B4EEC6C00F98F2A /* JoinProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A84465B2B4EEC6C00F98F2A /* JoinProfileViewModel.swift */; }; 2A84465E2B4EF41900F98F2A /* JoinProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A84465D2B4EF41900F98F2A /* JoinProfileView.swift */; }; 2A8868D62B5069790017513A /* OnboardingEndingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8868D52B5069790017513A /* OnboardingEndingView.swift */; }; + 2A8868DA2B506D720017513A /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8868D92B506D720017513A /* NotificationViewController.swift */; }; 2A8D70B12B4C4D8F009F4C6C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 2A8D70B02B4C4D8F009F4C6C /* KakaoSDK */; }; 2A8D70B42B4C999F009F4C6C /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */; }; 2A8D70B82B4C9A59009F4C6C /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A8D70B72B4C9A59009F4C6C /* OnboardingViewController.swift */; }; @@ -96,6 +97,7 @@ 2A84465B2B4EEC6C00F98F2A /* JoinProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinProfileViewModel.swift; sourceTree = ""; }; 2A84465D2B4EF41900F98F2A /* JoinProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinProfileView.swift; sourceTree = ""; }; 2A8868D52B5069790017513A /* OnboardingEndingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingEndingView.swift; sourceTree = ""; }; + 2A8868D92B506D720017513A /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = ""; }; 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = ""; }; 2A8D70B72B4C9A59009F4C6C /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; 2A8D70BC2B4D61A1009F4C6C /* OnboardingDummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingDummy.swift; sourceTree = ""; }; @@ -203,6 +205,22 @@ path = Splash; sourceTree = ""; }; + 2A8868D72B506D3B0017513A /* Notification */ = { + isa = PBXGroup; + children = ( + 2A8868D82B506D620017513A /* ViewControllers */, + ); + path = Notification; + sourceTree = ""; + }; + 2A8868D82B506D620017513A /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 2A8868D92B506D720017513A /* NotificationViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 2A8D70B22B4C990A009F4C6C /* Onboarding */ = { isa = PBXGroup; children = ( @@ -372,6 +390,7 @@ 2A8D70B22B4C990A009F4C6C /* Onboarding */, 3C61930F2B3A7A0700220CEB /* Home */, 3CE9C1252B4BE5970086E4A3 /* Write */, + 2A8868D72B506D3B0017513A /* Notification */, 3CF184C72B4EEBD200816D5F /* MyPage */, 3C2855022B3AAEE400369C99 /* Example */, ); @@ -684,6 +703,7 @@ 3C6192EF2B3A719A00220CEB /* SceneDelegate.swift in Sources */, 2A2672052B4C3C00009D214F /* CancelBag.swift in Sources */, 2A84465A2B4EE8B400F98F2A /* JoinProfileViewController.swift in Sources */, + 2A8868DA2B506D720017513A /* NotificationViewController.swift in Sources */, 3C4993652B4F2471002A99CF /* MyPageSegmentedControl.swift in Sources */, 3CF184CD2B4EEC1900816D5F /* MyPageView.swift in Sources */, 3C2854FF2B3AA01700369C99 /* ExampleView.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift index baa8892b..2560083b 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift @@ -95,7 +95,6 @@ final class JoinProfileView: UIView { override init(frame: CGRect) { super.init(frame: frame) - setUI() setHierarchy() setLayout() } @@ -109,10 +108,6 @@ final class JoinProfileView: UIView { // MARK: - Extensions extension JoinProfileView { - func setUI() { - self.backgroundColor = .donWhite - } - func setHierarchy() { self.addSubviews(topDivisionLine, profileImage, diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift new file mode 100644 index 00000000..14becbed --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -0,0 +1,42 @@ +// +// NotificationViewController.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import UIKit + +final class NotificationViewController: UIViewController { + + // MARK: - Properties + + // MARK: - UI Components + + // MARK: - Life Cycles + + + override func viewDidLoad() { + super.viewDidLoad() + + setUI() + setHierarchy() + setLayout() + } +} + +// MARK: - Extensions + +extension NotificationViewController { + private func setUI() { + self.view.backgroundColor = .donGray1 + } + + private func setHierarchy() { + + } + + private func setLayout() { + + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift index bbe96531..3c62e81e 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift @@ -43,8 +43,8 @@ enum DontBeTabBarItem: CaseIterable { var targetViewController: UIViewController? { switch self { case .home: return HomeViewController() - case .writing: return nil - case .notification: return nil + case .writing: return nil // 애니메이션 -> 다른 곳에서 뷰컨 연결 + case .notification: return NotificationViewController() case .myPage: return MyPageViewController() } } From a5d730b32f05e9008d9470f81afb9e513a5b1f9a Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 06:46:18 +0900 Subject: [PATCH 06/25] =?UTF-8?q?[Feat]=20#33=20-=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=B7=B0=20=EC=A0=84=EC=B2=B4=EC=A0=81=EC=9D=B8=20?= =?UTF-8?q?=ED=8B=80=20=EC=9E=A1=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 54 +++++++++- .../Global/Literals/ImageLiterals.swift | 4 + .../Global/Literals/StringLiterals.swift | 12 +++ .../etc/img_dog.imageset/Contents.json | 23 +++++ .../etc/img_dog.imageset/image_profile.png | Bin 0 -> 2918 bytes .../etc/img_dog.imageset/image_profile@2x.png | Bin 0 -> 8357 bytes .../etc/img_dog.imageset/image_profile@3x.png | Bin 0 -> 16360 bytes .../UICollectionViewRegisterable.swift | 0 .../Helpers/UITableViewCellRegisterable.swift | 29 ++++++ .../Cells/NotificationTableViewCell.swift | 95 ++++++++++++++++++ .../Helpers/NotificationDummy.swift | 52 ++++++++++ .../NotificationViewController.swift | 57 ++++++++++- .../ViewModels/NotificationViewModel.swift | 8 ++ .../Views/NotificationEmptyView.swift | 8 ++ 14 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/Contents.json create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png rename DontBe-iOS/DontBe-iOS/Presentation/{Home/Views => Helpers}/UICollectionViewRegisterable.swift (100%) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Helpers/UITableViewCellRegisterable.swift create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewModels/NotificationViewModel.swift create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index c475be0b..8776fbe5 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -16,6 +16,11 @@ 2A31FF572B4F1E0400FEEED9 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A31FF562B4F1E0400FEEED9 /* String+.swift */; }; 2A31FF592B4F3A8B00FEEED9 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A31FF582B4F3A8B00FEEED9 /* UserInfo.swift */; }; 2A51AE852B4B05AA00FF770A /* SplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A51AE842B4B05AA00FF770A /* SplashViewController.swift */; }; + 2A5220EC2B507F2A001510B7 /* UITableViewCellRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */; }; + 2A5220EF2B507F97001510B7 /* NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220EE2B507F97001510B7 /* NotificationViewModel.swift */; }; + 2A5220F22B507FAE001510B7 /* NotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */; }; + 2A5220F52B508142001510B7 /* NotificationEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */; }; + 2A5220F82B508177001510B7 /* NotificationDummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F72B508177001510B7 /* NotificationDummy.swift */; }; 2A6D54C12B479B4300F9891E /* adjusted+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D54C02B479B4300F9891E /* adjusted+.swift */; }; 2A6D54C32B493A8400F9891E /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D54C22B493A8400F9891E /* UIFont+.swift */; }; 2A6D54C62B493E3F00F9891E /* Pretendard-SemiBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2A6D54C42B493E3F00F9891E /* Pretendard-SemiBold.otf */; }; @@ -89,6 +94,11 @@ 2A31FF562B4F1E0400FEEED9 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; 2A31FF582B4F3A8B00FEEED9 /* UserInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfo.swift; sourceTree = ""; }; 2A51AE842B4B05AA00FF770A /* SplashViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = ""; }; + 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCellRegisterable.swift; sourceTree = ""; }; + 2A5220EE2B507F97001510B7 /* NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewModel.swift; sourceTree = ""; }; + 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTableViewCell.swift; sourceTree = ""; }; + 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyView.swift; sourceTree = ""; }; + 2A5220F72B508177001510B7 /* NotificationDummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDummy.swift; sourceTree = ""; }; 2A6D54C02B479B4300F9891E /* adjusted+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "adjusted+.swift"; sourceTree = ""; }; 2A6D54C22B493A8400F9891E /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; 2A6D54C42B493E3F00F9891E /* Pretendard-SemiBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-SemiBold.otf"; sourceTree = ""; }; @@ -205,10 +215,46 @@ path = Splash; sourceTree = ""; }; + 2A5220ED2B507F82001510B7 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 2A5220EE2B507F97001510B7 /* NotificationViewModel.swift */, + ); + path = ViewModels; + sourceTree = ""; + }; + 2A5220F02B507F9C001510B7 /* Cells */ = { + isa = PBXGroup; + children = ( + 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */, + ); + path = Cells; + sourceTree = ""; + }; + 2A5220F32B508135001510B7 /* Views */ = { + isa = PBXGroup; + children = ( + 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 2A5220F62B508169001510B7 /* Helpers */ = { + isa = PBXGroup; + children = ( + 2A5220F72B508177001510B7 /* NotificationDummy.swift */, + ); + path = Helpers; + sourceTree = ""; + }; 2A8868D72B506D3B0017513A /* Notification */ = { isa = PBXGroup; children = ( + 2A5220F32B508135001510B7 /* Views */, 2A8868D82B506D620017513A /* ViewControllers */, + 2A5220ED2B507F82001510B7 /* ViewModels */, + 2A5220F02B507F9C001510B7 /* Cells */, + 2A5220F62B508169001510B7 /* Helpers */, ); path = Notification; sourceTree = ""; @@ -470,7 +516,6 @@ 2F87353F2B4BE65300E55552 /* HomeView.swift */, 2F8735452B4C34A500E55552 /* HomeCollectionView.swift */, 2F87354B2B4D28D700E55552 /* HomeCollectionFooterView.swift */, - 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */, ); path = Views; sourceTree = ""; @@ -535,6 +580,8 @@ 2A8D70C22B4D7FF5009F4C6C /* BackButton.swift */, 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */, 2A31FF582B4F3A8B00FEEED9 /* UserInfo.swift */, + 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */, + 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */, ); path = Helpers; sourceTree = ""; @@ -681,10 +728,15 @@ 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */, 2F8735442B4BE67300E55552 /* HomeViewModel.swift in Sources */, 2F87354C2B4D28D700E55552 /* HomeCollectionFooterView.swift in Sources */, + 2A5220EC2B507F2A001510B7 /* UITableViewCellRegisterable.swift in Sources */, 2F8735482B4C355100E55552 /* HomeCollectionViewCell.swift in Sources */, 3CE9C12A2B4BE6780086E4A3 /* WriteViewController.swift in Sources */, + 2A5220EF2B507F97001510B7 /* NotificationViewModel.swift in Sources */, 2A84465C2B4EEC6C00F98F2A /* JoinProfileViewModel.swift in Sources */, + 2A5220F82B508177001510B7 /* NotificationDummy.swift in Sources */, + 2A5220F22B507FAE001510B7 /* NotificationTableViewCell.swift in Sources */, 3C2854FD2B3A9FD800369C99 /* ExampleViewController.swift in Sources */, + 2A5220F52B508142001510B7 /* NotificationEmptyView.swift in Sources */, 2AAEFC992B4A9E3B00C2D323 /* DontBeTabBarController.swift in Sources */, 2A31FF592B4F3A8B00FEEED9 /* UserInfo.swift in Sources */, 2AAEFC972B4A9C3700C2D323 /* DontBeTabBarItem.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift index 1661bc2d..9758e492 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift @@ -74,6 +74,10 @@ enum ImageLiterals { static var icnCheck: UIImage { .load(name: "icn_check") } } + enum Notification { + static var imgDog: UIImage { .load(name: "img_dog") } + } + enum MyPage { static var icnMenu: UIImage { .load(name: "icn_menu") } static var icnEditProfile: UIImage { .load(name: "icn_editprofile") } diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 1292bf7c..6baaa3c0 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -63,6 +63,18 @@ enum StringLiterals { static let uploaded = "게시 완료!" } + enum Notification { + static let alarm = "알림" + static let likeContent = "님이 회원님의 글을 좋아합니다." + static let writeComment = "님이 답글을 작성했습니다." + static let likeComment = "님이 회원님의 답글을 좋아합니다." + static let welcome = "님, 이제 다시 글을 작성할 수 있어요! 오랜만에 인사를 남겨주세요!" + static let transparency = "님, 투명해져서 당분간 글을 작성할 수 없어요." + static let violation = "님 커뮤니티 활동 정책 위반으로 더이상 돈비를 이용할 수 없어요. 자세한 내용은 문의사항으로 남겨주세요." + static let contentTransparency = "님, 작성하신 게시글로 인해 점점 투명해지고 있어요." + static let commentTransparency = "님, 작성하신 답글로 인해 점점 투명해지고 있어요." + } + enum MyPage { static let MyPageNavigationTitle = "마이" } diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/Contents.json b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/Contents.json new file mode 100644 index 00000000..892f834d --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "image_profile.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "image_profile@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "image_profile@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1f6f1b8ed81dad581adfe6d18bb167db24fb97 GIT binary patch literal 2918 zcmV-s3z_tZP)cdJ_+voKxrQz1P3~^{=(}*~tpOd6bltL^p2S$ZOWD z*}SV)ug3cH>67>E+qcMmN4&TD)UaX0k~uj!skd+6o;Y>tRN~mNV~OyaM^;{@O9PcJ12g#@KDr(1y#GFOLr&KD_Mf*RLf8BHxVV8e5@BlP0hixc~Cy zONhl{p>^xl;nSy2;ql|g;oZA;;o-xFp-r1M;rsXRx$WAu%d@_#3~=0@ok%8=sfO79 zS4QLUIH4@g&CM;EF=Ix!ckf;}ckWy;gJJUI$qs7Jph2Ny$BqGZ0r&CaM+a(7h0B*O zhl>|4hW72-hi=`vIqMxdbO=3r_6&(cfRXTkFFULJsYdhh!G>^uUN6-#KD6HYwN?4UEf3Nm}kPT zG~Gq$kHo+jIwX9?vi~NW7zGeX#B*cCWiepD0M{3|OXB_e_uh_Z@ZJ(LSV9F$tZ?Yip>W~Cg)n;b=*Xl}X=9C@GG$8g9~rd) z0as~2W3WJ3IdkSrSiE>~=-s=wGr|cQgdGh6m;{2skt0WjzJ2@pSO8;;ojZ4iRjXEc z$oWmVBAEcm7(~PX1`B<#l81wlkyW{W|NiJ7ff|TnV(*c%h5Y<{*PiJ#OrJj8 z*<3Q$g>#I?4z zHtgB6#}f;C9Mn|+BR8r6%r08AsQ8D_8=K4)aS{XLcnzxz#FdVOf5Lwwz8t6>V3U_byc2zVtCNuYTgP#7OOcC0f@dul6eSuOPT0A&mSFn<63{oO(I4P&$c zB+{X@QCk@cC($}`DlRUbp9yq61M&jx%$++o^ytyU!2pybpfd*^lkKoU+k%1ue~yEb zCQb6PLj?c;;24((aD@OW2&|(+`VcJ=31{eI6HVB?d$;EX<N=SmcFoS3FJg!N43}b&Cjs`{r~_50g4Wl(G1QNVCiD)0B+s7LGh&`1MtU<~67hBpAfbG+mkx&r6MFTh4X2VyIoH5prrO_W;Rsn5%$t<)A;m<%GuL9)OIIIg`;&TF*ArDX~BY0EtnpfH;+0%o=3l z*|TTs7AEQ|xnycmlLd#O#DR=BB@+OtugFnS+BKD94O7-20u&z6n3n($2r)&7$$sW? zo>R*B`NEqy~s9_R;QJ#y{%>FiR5}4GX_>a$%HmAMKVYRIyC#>jLI0&VbRS*A(BWa+6gFR;(iSeFvNhSg^S=Z-vzkdCEx|U6`t|+7wF_NDC>U6GYSy_%FuqbdmV+`zMJWtt*E8$sh z?3MEZHduU=25YJhM5+Yboc>#z*uUhzXDaxEe1yq{n8FM{R;U0iQP;JmU5%L-rE#f6viB7e0LaTh0A`jJEPR3yH!-o&MY@~aIUdg4s0c>zjBymg& z5!yi%&X^P1>wqpNePRjZ>H6)M(ndTZk;N%LedIKj0iZ{tgvH*MFm$aBv>@FmehuW zbf*=VzQ6R*rx4SJ5~UfAtM%z;8yiV4{sL;@O4`c4y)lx{AlklxEsXG+6T^VyIpIa4 z0MO1JjA+fO=}@a6MML#gngu+4xa#x8-fg$B{x5t#?3CC?T8WjZdDb|Fhjb=LO)M|%hP+e6ftBz`sq0%Jg)JKzswM$yRl1yxW+jg{BB4&f0XZM)I!E4xn zO$MTj=?ju~vuDrtzECsQ7n*R=6G75o6j8P<-XvQOH5<{$*m`yk+S%e&bR0ig3HoVASR`2?9Ws{8ltQ$1F!lr(w z*8nMVrohIlcgy^jJI-6SY>Ao2l?E`T?M3ZB0fNt980I<8U=|H)^@S5?oi=PfFjX?4 zDB)C9SRL6}pbT#`P!8LRip+S4F|b-wD}c}+p!IE~xjJ1|94y;GNW%Uj;*p@QWNrpQY*3jfnRQoH~7CIuxlYIGbZ8)A8^!7!pA*^(HioARcVp(rJZR-B)y}Tu;eRU zvt~`AA@&>n?;B${&zLB(PwRQ6;Yi!ufaS`8hGCj4mkt4L?4}G{xv^hi8@!1|m~A}J z%wyNCU2y{#H$Z5Sx3$og@T4N4X*ja7=NNdo)Ykofd0)mR{q3Iq{ QBLDyZ07*qoM6N<$g6W!BYybcN literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cbee364bf02df8d41a05229b1e585af9caa36c8c GIT binary patch literal 8357 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 literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..700cade6e60a1bba267e035e0a72126380bcd33b GIT binary patch literal 16360 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 Self + static var reuseIdentifier: String { get } +} + +extension UITableViewCellRegisterable { + public static func register(tableView: UITableView) { + tableView.register(self, forCellReuseIdentifier: self.reuseIdentifier) + } + + public static func dequeueReusableCell(tableView: UITableView, indexPath: IndexPath) -> Self { + guard let cell = tableView.dequeueReusableCell(withIdentifier: self.reuseIdentifier, for: indexPath) as? Self else { fatalError()} + return cell + } + + public static var reuseIdentifier: String { + return String(describing: self) + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift new file mode 100644 index 00000000..6021a1f3 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift @@ -0,0 +1,95 @@ +// +// NotificationTableViewCell.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import UIKit + +import SnapKit + +final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegisterable { + + // MARK: - Properties + + // MARK: - UI Components + + private let profileImage = UIImageView() + private var notificationLabel: UILabel = { + let description = UILabel() + description.textColor = .donGray12 + description.font = .font(.body4) + return description + }() + private let minutes: UILabel = { + let minutes = UILabel() + minutes.textColor = .donGray8 + minutes.font = .font(.caption4) + return minutes + }() + + override func layoutSubviews() { + super.layoutSubviews() + + self.contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 13.adjusted, bottom: 4.adjusted, right: 13.adjusted)) + } + + // MARK: - Life Cycles + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setUI() + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + + func configureCell(item: NotificationDummy) { + profileImage.setCircularImage(image: item.profile) + notificationLabel.text = item.userName + " " + item.description + minutes.text = item.minutes + } +} + + +// MARK: - Extensions + +extension NotificationTableViewCell { + private func setUI() { + self.backgroundColor = .donGray1 + self.contentView.backgroundColor = .donWhite + self.contentView.layer.cornerRadius = 8.adjusted + self.contentView.layer.masksToBounds = true + } + + private func setHierarchy() { + self.contentView.addSubviews(profileImage, + notificationLabel, + minutes) + } + + private func setLayout() { + profileImage.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.top.leading.equalToSuperview().inset(14.adjusted) + $0.size.equalTo(42.adjusted) + } + + notificationLabel.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.leading.equalTo(profileImage.snp.trailing).offset(14.adjusted) + $0.trailing.equalTo(minutes.snp.leading).offset(-14.adjusted) + } + + minutes.snp.makeConstraints { + $0.top.trailing.equalToSuperview().inset(14.adjusted) + } + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift new file mode 100644 index 00000000..ab88c1f2 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift @@ -0,0 +1,52 @@ +// +// NotificationDummy.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import UIKit + +struct NotificationDummy { + let profile: UIImage + let userName: String + let description: String + let minutes: String +} + +extension NotificationDummy { + static func dummy() -> [NotificationDummy] { + return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.likeContent, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.writeComment, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.likeComment, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.welcome, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.transparency, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.violation, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.contentTransparency, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.commentTransparency, + minutes: "12분전")] + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift index 14becbed..9f3cab88 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -7,12 +7,26 @@ import UIKit +import SnapKit + final class NotificationViewController: UIViewController { // MARK: - Properties + private let dummy = NotificationDummy.dummy() + // MARK: - UI Components + private let notificationTableView: UITableView = { + let tableView = UITableView() + tableView.backgroundColor = .donGray1 + tableView.separatorStyle = .none + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 70.adjusted + tableView.contentInsetAdjustmentBehavior = .never + return tableView + }() + // MARK: - Life Cycles @@ -22,6 +36,15 @@ final class NotificationViewController: UIViewController { setUI() setHierarchy() setLayout() + setDelegate() + setRegisterCell() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.navigationController?.navigationBar.isHidden = false + self.navigationItem.hidesBackButton = false } } @@ -30,13 +53,43 @@ final class NotificationViewController: UIViewController { extension NotificationViewController { private func setUI() { self.view.backgroundColor = .donGray1 + self.navigationItem.title = StringLiterals.Notification.alarm + self.navigationController?.navigationBar.barTintColor = .donWhite + self.navigationController?.navigationBar.backgroundColor = .donWhite } private func setHierarchy() { - + self.view.addSubview(notificationTableView) } private func setLayout() { - + notificationTableView.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide).inset(6.adjusted) + $0.leading.trailing.equalToSuperview() + $0.height.equalTo(1000.adjusted) + } + } + + private func setDelegate() { + self.notificationTableView.delegate = self + self.notificationTableView.dataSource = self + } + + private func setRegisterCell() { + NotificationTableViewCell.register(tableView: notificationTableView) + } +} + +extension NotificationViewController: UITableViewDelegate { } +extension NotificationViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return dummy.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: NotificationTableViewCell.reuseIdentifier, for: indexPath) as? NotificationTableViewCell else { return UITableViewCell() } + cell.configureCell(item: dummy[indexPath.row]) + cell.selectionStyle = .none + return cell } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewModels/NotificationViewModel.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewModels/NotificationViewModel.swift new file mode 100644 index 00000000..8bfd2ee1 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewModels/NotificationViewModel.swift @@ -0,0 +1,8 @@ +// +// NotificationViewModel.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import Foundation diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift new file mode 100644 index 00000000..1cb87a0d --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift @@ -0,0 +1,8 @@ +// +// NotificationEmptyView.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import Foundation From 0acff13ad5616511d32f80b006dcea351e58767b Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 08:00:24 +0900 Subject: [PATCH 07/25] =?UTF-8?q?[Feat]=20#33=20-=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=B7=B0=20=EC=84=AC=EC=84=B8=20=EB=B6=80=EB=B6=84=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 --- .../Global/Extension/UILabel+.swift | 9 +++++ .../Global/Literals/StringLiterals.swift | 1 + .../Cells/NotificationTableViewCell.swift | 19 +++++++---- .../NotificationViewController.swift | 33 +++++++++++++++---- 4 files changed, 50 insertions(+), 12 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift index 8e3b1cc6..36347cee 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift @@ -23,4 +23,13 @@ extension UILabel { self.attributedText = attrString } } + + /// 특정 텍스트만 폰트를 다르게 주는 함수 + func asFont(targetString: String, font: UIFont) { + let fullText = text ?? "" + let attributedString = NSMutableAttributedString(string: fullText) + let range = (fullText as NSString).range(of: targetString) + attributedString.addAttribute(.font, value: font, range: range) + attributedText = attributedString + } } diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 6baaa3c0..28f6108a 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -71,6 +71,7 @@ enum StringLiterals { static let welcome = "님, 이제 다시 글을 작성할 수 있어요! 오랜만에 인사를 남겨주세요!" static let transparency = "님, 투명해져서 당분간 글을 작성할 수 없어요." static let violation = "님 커뮤니티 활동 정책 위반으로 더이상 돈비를 이용할 수 없어요. 자세한 내용은 문의사항으로 남겨주세요." + static let emphasizeViolation = "님 커뮤니티 활동 정책 위반으로 더이상 돈비를 이용할 수 없어요." static let contentTransparency = "님, 작성하신 게시글로 인해 점점 투명해지고 있어요." static let commentTransparency = "님, 작성하신 답글로 인해 점점 투명해지고 있어요." } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift index 6021a1f3..8e460b77 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift @@ -17,10 +17,12 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera private let profileImage = UIImageView() private var notificationLabel: UILabel = { - let description = UILabel() - description.textColor = .donGray12 - description.font = .font(.body4) - return description + let notificationLabel = UILabel() + notificationLabel.textColor = .donGray12 + notificationLabel.font = .font(.body4) + notificationLabel.numberOfLines = 0 // 여러 줄 지원 + notificationLabel.lineBreakMode = .byCharWrapping + return notificationLabel }() private let minutes: UILabel = { let minutes = UILabel() @@ -54,6 +56,11 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera func configureCell(item: NotificationDummy) { profileImage.setCircularImage(image: item.profile) notificationLabel.text = item.userName + " " + item.description + if item.description == StringLiterals.Notification.violation { + notificationLabel.asFont(targetString: item.userName + " " + StringLiterals.Notification.emphasizeViolation, font: .font(.body3)) + } else { + notificationLabel.asFont(targetString: item.userName, font: .font(.body3)) + } minutes.text = item.minutes } } @@ -84,8 +91,8 @@ extension NotificationTableViewCell { notificationLabel.snp.makeConstraints { $0.centerY.equalToSuperview() - $0.leading.equalTo(profileImage.snp.trailing).offset(14.adjusted) - $0.trailing.equalTo(minutes.snp.leading).offset(-14.adjusted) + $0.leading.equalToSuperview().inset(70.adjusted) + $0.trailing.equalToSuperview().inset(63.adjusted) } minutes.snp.makeConstraints { diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift index 9f3cab88..f1936376 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -16,9 +16,9 @@ final class NotificationViewController: UIViewController { private let dummy = NotificationDummy.dummy() // MARK: - UI Components - + private let notificationTableView: UITableView = { - let tableView = UITableView() + let tableView = UITableView(frame: .zero, style: .grouped) tableView.backgroundColor = .donGray1 tableView.separatorStyle = .none tableView.rowHeight = UITableView.automaticDimension @@ -27,6 +27,13 @@ final class NotificationViewController: UIViewController { return tableView }() + private let notificationTableFooterView: UIView = { + let notificationTableFooterView = UIView() + notificationTableFooterView.backgroundColor = .donGray1 + return notificationTableFooterView + }() + + // MARK: - Life Cycles @@ -52,9 +59,10 @@ final class NotificationViewController: UIViewController { extension NotificationViewController { private func setUI() { - self.view.backgroundColor = .donGray1 + self.view.backgroundColor = .donWhite self.navigationItem.title = StringLiterals.Notification.alarm self.navigationController?.navigationBar.barTintColor = .donWhite + self.navigationController?.navigationBar.tintColor = .donWhite self.navigationController?.navigationBar.backgroundColor = .donWhite } @@ -64,9 +72,8 @@ extension NotificationViewController { private func setLayout() { notificationTableView.snp.makeConstraints { - $0.top.equalTo(self.view.safeAreaLayoutGuide).inset(6.adjusted) - $0.leading.trailing.equalToSuperview() - $0.height.equalTo(1000.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 14.adjusted) + $0.leading.trailing.bottom.equalToSuperview() } } @@ -92,4 +99,18 @@ extension NotificationViewController: UITableViewDataSource { cell.selectionStyle = .none return cell } + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + + return UITableView.automaticDimension + + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return notificationTableFooterView + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return 59.adjusted + } } From 5f81f7b3a36a22020fb5993f145635409250e539 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 17:55:53 +0900 Subject: [PATCH 08/25] =?UTF-8?q?[Feat]=20#33=20-=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EB=B7=B0=20=EB=A0=88=EC=9D=B4=EB=B8=94=20=EC=A4=84=20?= =?UTF-8?q?=EC=88=98=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=86=92=EC=9D=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/Extension/UILabel+.swift | 30 ++++++++++++- .../Cells/NotificationTableViewCell.swift | 22 +++++++--- .../NotificationViewController.swift | 44 +++++++++++-------- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift index 36347cee..3dd08405 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift @@ -8,12 +8,12 @@ import UIKit extension UILabel { - func setTextWithLineHeight(text: String?, lineHeight: CGFloat){ + func setTextWithLineHeight(text: String?, lineHeight: CGFloat){ if let text = text { let style = NSMutableParagraphStyle() style.maximumLineHeight = lineHeight style.minimumLineHeight = lineHeight - + let attributes: [NSAttributedString.Key: Any] = [ .paragraphStyle: style, ] @@ -32,4 +32,30 @@ extension UILabel { attributedString.addAttribute(.font, value: font, range: range) attributedText = attributedString } + + func setTextWithLineHeightAndFont(text: String?, lineHeight: CGFloat, targetString: String, font: UIFont) { + if let text = text { + let style = NSMutableParagraphStyle() + style.maximumLineHeight = lineHeight + style.minimumLineHeight = lineHeight + + let attributes: [NSAttributedString.Key: Any] = [ + .paragraphStyle: style, + .font: UIFont.font(.body4) + ] + + let attrString = NSMutableAttributedString(string: text, attributes: attributes) + + // 특정 문자열에 대해서만 폰트 변경 + let range = (text as NSString).range(of: targetString) + attrString.addAttribute(.font, value: font, range: range) + + self.attributedText = attrString + } + } + + class func lineNumber(label: UILabel, labelWidth: CGFloat) -> Int { + let boundingRect = label.text?.boundingRect(with: .zero, options: [.usesFontLeading], attributes: [.font: label.font!], context: nil) + return Int((boundingRect?.width ?? 0) / labelWidth + 1) + } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift index 8e460b77..064dc9a1 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift @@ -16,7 +16,7 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera // MARK: - UI Components private let profileImage = UIImageView() - private var notificationLabel: UILabel = { + let notificationLabel: UILabel = { let notificationLabel = UILabel() notificationLabel.textColor = .donGray12 notificationLabel.font = .font(.body4) @@ -34,7 +34,7 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera override func layoutSubviews() { super.layoutSubviews() - self.contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 13.adjusted, bottom: 4.adjusted, right: 13.adjusted)) + self.contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 13.adjusted, bottom: 4.adjustedH, right: 13.adjusted)) } // MARK: - Life Cycles @@ -44,7 +44,6 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera setUI() setHierarchy() - setLayout() } @available(*, unavailable) @@ -57,11 +56,20 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera profileImage.setCircularImage(image: item.profile) notificationLabel.text = item.userName + " " + item.description if item.description == StringLiterals.Notification.violation { - notificationLabel.asFont(targetString: item.userName + " " + StringLiterals.Notification.emphasizeViolation, font: .font(.body3)) + notificationLabel.setTextWithLineHeightAndFont( + text: notificationLabel.text, + lineHeight: 21.adjusted, + targetString: item.userName + " " + StringLiterals.Notification.emphasizeViolation, + font: .font(.body3)) } else { - notificationLabel.asFont(targetString: item.userName, font: .font(.body3)) + notificationLabel.setTextWithLineHeightAndFont( + text: notificationLabel.text, + lineHeight: 21.adjusted, + targetString: item.userName, + font: .font(.body3)) } minutes.text = item.minutes + setLayout() } } @@ -96,7 +104,9 @@ extension NotificationTableViewCell { } minutes.snp.makeConstraints { - $0.top.trailing.equalToSuperview().inset(14.adjusted) + $0.top.equalTo(notificationLabel).offset(2.adjustedH) + $0.trailing.equalToSuperview().inset(14.adjusted) + $0.height.equalTo(18.adjusted) } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift index f1936376..3084ff4a 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -14,6 +14,7 @@ final class NotificationViewController: UIViewController { // MARK: - Properties private let dummy = NotificationDummy.dummy() + private var numsOfLinesOfCellLabel: Int = 0 // MARK: - UI Components @@ -21,8 +22,6 @@ final class NotificationViewController: UIViewController { let tableView = UITableView(frame: .zero, style: .grouped) tableView.backgroundColor = .donGray1 tableView.separatorStyle = .none - tableView.rowHeight = UITableView.automaticDimension - tableView.estimatedRowHeight = 70.adjusted tableView.contentInsetAdjustmentBehavior = .never return tableView }() @@ -32,10 +31,10 @@ final class NotificationViewController: UIViewController { notificationTableFooterView.backgroundColor = .donGray1 return notificationTableFooterView }() - + // MARK: - Life Cycles - + override func viewDidLoad() { super.viewDidLoad() @@ -87,7 +86,26 @@ extension NotificationViewController { } } -extension NotificationViewController: UITableViewDelegate { } +extension NotificationViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if numsOfLinesOfCellLabel == 3 { + return 95.adjustedH + } else if numsOfLinesOfCellLabel == 4 { + return 116.adjustedH + } else { + return 74.adjustedH + } + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + return notificationTableFooterView + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return 59.adjusted + } +} + extension NotificationViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return dummy.count @@ -97,20 +115,8 @@ extension NotificationViewController: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: NotificationTableViewCell.reuseIdentifier, for: indexPath) as? NotificationTableViewCell else { return UITableViewCell() } cell.configureCell(item: dummy[indexPath.row]) cell.selectionStyle = .none + let numsOflines = UILabel.lineNumber(label: cell.notificationLabel, labelWidth: 216.adjusted) + numsOfLinesOfCellLabel = numsOflines return cell } - - func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { - - return UITableView.automaticDimension - - } - - func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return notificationTableFooterView - } - - func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return 59.adjusted - } } From 4a2e5dbadc412a47ef2088433351951e6cb30c40 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 19:55:06 +0900 Subject: [PATCH 09/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EB=B7=B0=20(=EC=97=A0=ED=8B=B0=EB=B7=B0,=20=EC=9D=B8=EB=94=94?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=ED=84=B0)=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 8 +- .../Global/Literals/ImageLiterals.swift | 1 + .../Global/Literals/StringLiterals.swift | 2 + .../etc/img_empty.imageset/Contents.json | 23 +++++ .../etc/img_empty.imageset/img_empty.png | Bin 0 -> 5683 bytes .../etc/img_empty.imageset/img_empty@2x.png | Bin 0 -> 17912 bytes .../etc/img_empty.imageset/img_empty@3x.png | Bin 0 -> 36880 bytes .../Cells/NotificationEmptyViewCell.swift | 89 ++++++++++++++++++ .../Cells/NotificationTableViewCell.swift | 41 ++++---- .../Helpers/NotificationDummy.swift | 65 ++++++------- .../NotificationViewController.swift | 50 +++++++--- .../Views/NotificationEmptyView.swift | 8 -- 12 files changed, 210 insertions(+), 77 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/Contents.json create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@2x.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@3x.png create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift delete mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index 8776fbe5..3f917b7b 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ 2A5220EC2B507F2A001510B7 /* UITableViewCellRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */; }; 2A5220EF2B507F97001510B7 /* NotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220EE2B507F97001510B7 /* NotificationViewModel.swift */; }; 2A5220F22B507FAE001510B7 /* NotificationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */; }; - 2A5220F52B508142001510B7 /* NotificationEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */; }; 2A5220F82B508177001510B7 /* NotificationDummy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5220F72B508177001510B7 /* NotificationDummy.swift */; }; 2A6D54C12B479B4300F9891E /* adjusted+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D54C02B479B4300F9891E /* adjusted+.swift */; }; 2A6D54C32B493A8400F9891E /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D54C22B493A8400F9891E /* UIFont+.swift */; }; @@ -47,6 +46,7 @@ 2AAEFC992B4A9E3B00C2D323 /* DontBeTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AAEFC982B4A9E3B00C2D323 /* DontBeTabBarController.swift */; }; 2AC9FB1B2B4DE77400D31071 /* AgreementListCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1A2B4DE77400D31071 /* AgreementListCustomView.swift */; }; 2AC9FB1F2B4E634A00D31071 /* JoinAgreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */; }; + 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; 2F8735422B4BE66500E55552 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735412B4BE66500E55552 /* HomeViewController.swift */; }; 2F8735442B4BE67300E55552 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735432B4BE67300E55552 /* HomeViewModel.swift */; }; @@ -97,7 +97,6 @@ 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCellRegisterable.swift; sourceTree = ""; }; 2A5220EE2B507F97001510B7 /* NotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewModel.swift; sourceTree = ""; }; 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationTableViewCell.swift; sourceTree = ""; }; - 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyView.swift; sourceTree = ""; }; 2A5220F72B508177001510B7 /* NotificationDummy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDummy.swift; sourceTree = ""; }; 2A6D54C02B479B4300F9891E /* adjusted+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "adjusted+.swift"; sourceTree = ""; }; 2A6D54C22B493A8400F9891E /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; @@ -123,6 +122,7 @@ 2AAEFC982B4A9E3B00C2D323 /* DontBeTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DontBeTabBarController.swift; sourceTree = ""; }; 2AC9FB1A2B4DE77400D31071 /* AgreementListCustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgreementListCustomView.swift; sourceTree = ""; }; 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinAgreeView.swift; sourceTree = ""; }; + 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyViewCell.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; 2F8735412B4BE66500E55552 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 2F8735432B4BE67300E55552 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; @@ -227,6 +227,7 @@ isa = PBXGroup; children = ( 2A5220F12B507FAE001510B7 /* NotificationTableViewCell.swift */, + 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */, ); path = Cells; sourceTree = ""; @@ -234,7 +235,6 @@ 2A5220F32B508135001510B7 /* Views */ = { isa = PBXGroup; children = ( - 2A5220F42B508142001510B7 /* NotificationEmptyView.swift */, ); path = Views; sourceTree = ""; @@ -736,7 +736,6 @@ 2A5220F82B508177001510B7 /* NotificationDummy.swift in Sources */, 2A5220F22B507FAE001510B7 /* NotificationTableViewCell.swift in Sources */, 3C2854FD2B3A9FD800369C99 /* ExampleViewController.swift in Sources */, - 2A5220F52B508142001510B7 /* NotificationEmptyView.swift in Sources */, 2AAEFC992B4A9E3B00C2D323 /* DontBeTabBarController.swift in Sources */, 2A31FF592B4F3A8B00FEEED9 /* UserInfo.swift in Sources */, 2AAEFC972B4A9C3700C2D323 /* DontBeTabBarItem.swift in Sources */, @@ -761,6 +760,7 @@ 3C2854FF2B3AA01700369C99 /* ExampleView.swift in Sources */, 3CF184CB2B4EEC0B00816D5F /* MyPageViewController.swift in Sources */, 3C35565B2B494F0A0016BA49 /* UIColor+.swift in Sources */, + 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */, 2AC9FB1B2B4DE77400D31071 /* AgreementListCustomView.swift in Sources */, 3C01692A2B4DC82D0075334B /* DontBePopupView.swift in Sources */, 2A2671FF2B4C3AF0009D214F /* Publisher+UIControl.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift index 9758e492..c2da2612 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift @@ -76,6 +76,7 @@ enum ImageLiterals { enum Notification { static var imgDog: UIImage { .load(name: "img_dog") } + static var imgEmpty: UIImage { .load(name: "img_empty") } } enum MyPage { diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 28f6108a..7cfab6f8 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -74,6 +74,8 @@ enum StringLiterals { static let emphasizeViolation = "님 커뮤니티 활동 정책 위반으로 더이상 돈비를 이용할 수 없어요." static let contentTransparency = "님, 작성하신 게시글로 인해 점점 투명해지고 있어요." static let commentTransparency = "님, 작성하신 답글로 인해 점점 투명해지고 있어요." + static let emptyTitle = "아직 받은 알림이 없어요." + static let emptyDescription = "새로운 소식이 도착하면 알려드릴게요." } enum MyPage { diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/Contents.json b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/Contents.json new file mode 100644 index 00000000..d4ab2336 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "img_empty.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "img_empty@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "img_empty@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..915f858b20f564d21e2b4ab26a31cb11c6f4c371 GIT binary patch literal 5683 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 Zp8_JujZUIyWO4ui002ovPDHLkV1hb9>{$Q+ literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@2x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bbb18956ca31d28a1fa42ed589e5772070abc25d GIT binary patch literal 17912 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 literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@3x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_empty.imageset/img_empty@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..afc89cd05ab4be219ded7445eeb62ea1bee9716f GIT binary patch literal 36880 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

DCMIX4CwH>v~g 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 literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift new file mode 100644 index 00000000..a420ffd6 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift @@ -0,0 +1,89 @@ +// +// NotificationEmptyViewCell.swift +// DontBe-iOS +// +// Created by 변희주 on 1/12/24. +// + +import UIKit + +import SnapKit + +final class NotificationEmptyViewCell: UITableViewCell, UITableViewCellRegisterable { + + // MARK: - Properties + + // MARK: - UI Components + + private let emptyImage: UIImageView = { + let emptyImage = UIImageView() + emptyImage.image = ImageLiterals.Notification.imgEmpty + return emptyImage + }() + + private let emptyTitle: UILabel = { + let emptyTitle = UILabel() + emptyTitle.text = StringLiterals.Notification.emptyTitle + emptyTitle.textColor = .donGray12 + emptyTitle.font = .font(.body3) + return emptyTitle + }() + + private let emptyDescirption: UILabel = { + let emptyDescirption = UILabel() + emptyDescirption.text = StringLiterals.Notification.emptyDescription + emptyDescirption.textColor = .donGray9 + emptyDescirption.font = .font(.caption2) + return emptyDescirption + }() + + + // MARK: - Life Cycles + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setUI() + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + + +// MARK: - Extensions + +extension NotificationEmptyViewCell { + private func setUI() { + self.backgroundColor = .donGray1 + } + + private func setHierarchy() { + self.addSubviews(emptyImage, + emptyTitle, + emptyDescirption) + } + + private func setLayout() { + emptyImage.snp.makeConstraints { + $0.top.equalTo(self.safeAreaLayoutGuide).inset(177.adjustedH) + $0.centerX.equalToSuperview() + $0.width.equalTo(77.adjusted) + $0.height.equalTo(90.adjusted) + } + + emptyTitle.snp.makeConstraints { + $0.top.equalTo(emptyImage.snp.bottom).offset(24.adjustedH) + $0.centerX.equalToSuperview() + } + + emptyDescirption.snp.makeConstraints { + $0.top.equalTo(emptyTitle.snp.bottom).offset(4.adjustedH) + $0.centerX.equalToSuperview() + } + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift index 064dc9a1..09fefc16 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift @@ -44,33 +44,13 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera setUI() setHierarchy() + setLayout() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - - func configureCell(item: NotificationDummy) { - profileImage.setCircularImage(image: item.profile) - notificationLabel.text = item.userName + " " + item.description - if item.description == StringLiterals.Notification.violation { - notificationLabel.setTextWithLineHeightAndFont( - text: notificationLabel.text, - lineHeight: 21.adjusted, - targetString: item.userName + " " + StringLiterals.Notification.emphasizeViolation, - font: .font(.body3)) - } else { - notificationLabel.setTextWithLineHeightAndFont( - text: notificationLabel.text, - lineHeight: 21.adjusted, - targetString: item.userName, - font: .font(.body3)) - } - minutes.text = item.minutes - setLayout() - } } @@ -109,4 +89,23 @@ extension NotificationTableViewCell { $0.height.equalTo(18.adjusted) } } + + func configureCell(item: NotificationDummy) { + profileImage.setCircularImage(image: item.profile) + notificationLabel.text = item.userName + " " + item.description + if item.description == StringLiterals.Notification.violation { + notificationLabel.setTextWithLineHeightAndFont( + text: notificationLabel.text, + lineHeight: 21.adjusted, + targetString: item.userName + " " + StringLiterals.Notification.emphasizeViolation, + font: .font(.body3)) + } else { + notificationLabel.setTextWithLineHeightAndFont( + text: notificationLabel.text, + lineHeight: 21.adjusted, + targetString: item.userName, + font: .font(.body3)) + } + minutes.text = item.minutes + } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift index ab88c1f2..9dd4b20b 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift @@ -16,37 +16,38 @@ struct NotificationDummy { extension NotificationDummy { static func dummy() -> [NotificationDummy] { - return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.likeContent, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.writeComment, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.likeComment, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.welcome, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.transparency, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.violation, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.contentTransparency, - minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, - userName: "벼니주", - description: StringLiterals.Notification.commentTransparency, - minutes: "12분전")] +// return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.likeContent, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.writeComment, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.likeComment, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.welcome, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.transparency, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.violation, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.contentTransparency, +// minutes: "12분전"), +// NotificationDummy(profile: ImageLiterals.Notification.imgDog, +// userName: "벼니주", +// description: StringLiterals.Notification.commentTransparency, +// minutes: "12분전")] + return [] } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift index 3084ff4a..4c4f8bb9 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -17,7 +17,8 @@ final class NotificationViewController: UIViewController { private var numsOfLinesOfCellLabel: Int = 0 // MARK: - UI Components - + + private let refreshControl = UIRefreshControl() private let notificationTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .grouped) tableView.backgroundColor = .donGray1 @@ -25,7 +26,6 @@ final class NotificationViewController: UIViewController { tableView.contentInsetAdjustmentBehavior = .never return tableView }() - private let notificationTableFooterView: UIView = { let notificationTableFooterView = UIView() notificationTableFooterView.backgroundColor = .donGray1 @@ -44,6 +44,7 @@ final class NotificationViewController: UIViewController { setLayout() setDelegate() setRegisterCell() + setRefreshControll() } override func viewWillAppear(_ animated: Bool) { @@ -66,12 +67,12 @@ extension NotificationViewController { } private func setHierarchy() { - self.view.addSubview(notificationTableView) + self.view.addSubviews(notificationTableView) } private func setLayout() { notificationTableView.snp.makeConstraints { - $0.top.equalToSuperview().inset(statusBarHeight + 14.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 15.adjusted) $0.leading.trailing.bottom.equalToSuperview() } } @@ -82,11 +83,26 @@ extension NotificationViewController { } private func setRegisterCell() { + NotificationEmptyViewCell.register(tableView: notificationTableView) NotificationTableViewCell.register(tableView: notificationTableView) } + + private func setRefreshControll() { + refreshControl.addTarget(self, action: #selector(refreshTableView), for: .valueChanged) + notificationTableView.refreshControl = refreshControl + refreshControl.backgroundColor = .donGray1 + } + + @objc + func refreshTableView() { + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.notificationTableView.reloadData() + self.refreshControl.endRefreshing() + } + } } -extension NotificationViewController: UITableViewDelegate { +extension NotificationViewController: UITableViewDelegate { func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if numsOfLinesOfCellLabel == 3 { return 95.adjustedH @@ -108,15 +124,25 @@ extension NotificationViewController: UITableViewDelegate { extension NotificationViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return dummy.count + let count = dummy.count + if count == 0 { + return 1 + } else { + return count + } } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: NotificationTableViewCell.reuseIdentifier, for: indexPath) as? NotificationTableViewCell else { return UITableViewCell() } - cell.configureCell(item: dummy[indexPath.row]) - cell.selectionStyle = .none - let numsOflines = UILabel.lineNumber(label: cell.notificationLabel, labelWidth: 216.adjusted) - numsOfLinesOfCellLabel = numsOflines - return cell + if dummy.isEmpty { + guard let cell = tableView.dequeueReusableCell(withIdentifier: NotificationEmptyViewCell.reuseIdentifier, for: indexPath) as? NotificationEmptyViewCell else { return UITableViewCell() } + return cell + } else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: NotificationTableViewCell.reuseIdentifier, for: indexPath) as? NotificationTableViewCell else { return UITableViewCell() } + cell.configureCell(item: dummy[indexPath.row]) + cell.selectionStyle = .none + let numsOflines = UILabel.lineNumber(label: cell.notificationLabel, labelWidth: 216.adjusted) + numsOfLinesOfCellLabel = numsOflines + return cell + } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift deleted file mode 100644 index 1cb87a0d..00000000 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Views/NotificationEmptyView.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// NotificationEmptyView.swift -// DontBe-iOS -// -// Created by 변희주 on 1/12/24. -// - -import Foundation From 54839d8e273f1dc259a7275b1d362fd5f65c51db Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 20:31:06 +0900 Subject: [PATCH 10/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=98=A8=EB=B3=B4?= =?UTF-8?q?=EB=94=A9=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift | 1 + DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift | 2 +- .../Presentation/Onboarding/Views/IntroductionView.swift | 7 +++++-- .../Onboarding/Views/OnboardingEndingView.swift | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift index 3dd08405..926e67a4 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift @@ -21,6 +21,7 @@ extension UILabel { let attrString = NSAttributedString(string: text, attributes: attributes) self.attributedText = attrString + self.textAlignment = .center } } diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 7cfab6f8..b67ad2a0 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -48,7 +48,7 @@ enum StringLiterals { enum Onboarding { static let placeHolder = "한문장으로 소개를 남겨주세요!" - static let information = "설정한 사진, 닉네임, 한줄소개는 설정에서 변경 가능해요!" + static let information = "설정한 사진, 닉네임, 한줄소개는 설정에서 변경 가능해요!\n작성한 한 줄 소개는 작성한 게시글로 업로드 돼요." } enum Button { diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift index b961b246..cb58bb5b 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift @@ -40,8 +40,10 @@ final class IntroductionView: UIView { private let information: UILabel = { let label = UILabel() label.text = StringLiterals.Onboarding.information + label.numberOfLines = 2 label.textColor = .donGray8 label.font = .font(.caption4) + label.setTextWithLineHeight(text: label.text, lineHeight: 17.adjusted) return label }() @@ -85,14 +87,15 @@ extension IntroductionView { } introduction.snp.makeConstraints { - $0.bottom.equalToSuperview().inset(51.adjusted) + $0.bottom.equalToSuperview().inset(70.adjusted) $0.leading.trailing.equalToSuperview().inset(16.adjusted) $0.height.equalTo(55.adjusted) } information.snp.makeConstraints { - $0.bottom.equalToSuperview().inset(20.adjusted) $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().inset(22.adjusted) + $0.height.equalTo(36.adjusted) } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift index 86b772fd..837b2dd2 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift @@ -107,12 +107,12 @@ extension OnboardingEndingView { profileImage.snp.makeConstraints { $0.size.equalTo(100.adjusted) $0.centerX.equalToSuperview() - $0.top.equalToSuperview().inset(statusBarHeight + 201.adjustedH) + $0.top.equalToSuperview().inset(statusBarHeight + 186.adjustedH) } introductionView.snp.makeConstraints { $0.width.equalTo(320.adjusted) - $0.height.equalTo(211.adjusted) + $0.height.equalTo(229.adjusted) $0.centerX.equalToSuperview() $0.top.equalTo(profileImage).offset(50.adjusted) } From 35a9a79156e296f88d952bec40901d75c27d22fc Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 22:29:48 +0900 Subject: [PATCH 11/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B7=B0=20UI=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=B0=8F=20=ED=83=AD=EB=B0=94=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS/Application/AppDelegate.swift | 2 +- .../Global/Literals/ImageLiterals.swift | 3 +- .../Contents.json | 23 ++++++ .../Property 1=badge.png | Bin 0 -> 422 bytes .../Property 1=badge@2x.png | Bin 0 -> 702 bytes .../Property 1=badge@3x.png | Bin 0 -> 1000 bytes .../Cells/NotificationTableViewCell.swift | 2 +- .../Helpers/NotificationDummy.swift | 66 +++++++++--------- .../TabBar/DontBeTabBarController.swift | 19 +++-- .../TabBar/DontBeTabBarItem.swift | 2 +- 10 files changed, 74 insertions(+), 43 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Contents.json create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge@2x.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge@3x.png diff --git a/DontBe-iOS/DontBe-iOS/Application/AppDelegate.swift b/DontBe-iOS/DontBe-iOS/Application/AppDelegate.swift index 1f41fcaf..ace6c3b7 100644 --- a/DontBe-iOS/DontBe-iOS/Application/AppDelegate.swift +++ b/DontBe-iOS/DontBe-iOS/Application/AppDelegate.swift @@ -18,7 +18,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UINavigationBar.appearance().titleTextAttributes = [ NSAttributedString.Key.font: UIFont.font(.body1), NSAttributedString.Key.foregroundColor: UIColor.donBlack - ] + ] KakaoSDK.initSDK(appKey: Bundle.main.object(forInfoDictionaryKey: Config.Keys.Plist.nativeAppKey) as? String ?? "") return true diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift index c2da2612..29cf4c80 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift @@ -19,7 +19,8 @@ enum ImageLiterals { static var icnHomeSelected: UIImage { .load(name: "icn_home_selected") } static var icnWriting: UIImage { .load(name: "icn_writing") } static var icnWritingSelected: UIImage { .load(name: "icn_writing_selected") } - static var icnNotification: UIImage { .load(name: "icn_notification") } + static var icnNotificationRead: UIImage { .load(name: "icn_notification").withRenderingMode(.alwaysOriginal) } + static var icnNotificationUnread: UIImage { .load(name: "icn_notification_unread").withRenderingMode(.alwaysOriginal) } static var icnNotificationSelected: UIImage { .load(name: "icn_notification_selected") } static var icnMyPage: UIImage { .load(name: "icn_mypage") } static var icnMyPageSelected: UIImage { .load(name: "icn_mypage_selected") } diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Contents.json b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Contents.json new file mode 100644 index 00000000..82f34c0b --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Property 1=badge.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Property 1=badge@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Property 1=badge@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge.png new file mode 100644 index 0000000000000000000000000000000000000000..b13c1b24dce86d8b777225b0a99524cc43fe08b6 GIT binary patch literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL zyA#8@b22Z19F}xPUq=Rpjs4tz5?O(A)}AhoAr*{or(VoGWFXLXUUAP?Mdb>XUlO5C zCs|UN5+<*hyd|}9gQLpgYyF8WJq;{VwLEHUgPRmo&n|t+$Z2=d>U7`#`x}eJ8>HDE z6i5U*OP@BpxW!}Tc0*e;CIj0pxsy5!b1aWuulxUSX;MS=!;>ki-(KU)UYj~^wuGRE z1pl{9rkpE;CsbZ}YqjwG@0)p_I-|C9f6Y%gYA!uFAl%6~^5_$e=T{z7!U5!}btC4mz(Cy0H6I46j6g48!Kxj=J++PKJ%?9v4+Wq%R^mRXqpe(wOF(Y$OB_82-jIp@>p@dj~aJYc%DBju$yG0x#@&THLzIt=S0t|vMO8~W6Sz*k@bvg;R_oKHa9d$xBtidAc;!n zpPVQVQJPoAxDMx^9H}TmSio|*H1F@@A5bGK!eJ3JnLNUsn4SLyH5142B}gJ9U^`lv zP&0_oD;b&oL9hmB0F8lC-zx0`O|<gEud|*fVRoq5BR>{ ziz4JLUfh$6`zQiuhCyfje*dP(DmSzkMeudAsA?;GN;dnD?8Cc7Rz*#yg;JNNKmb(= zG^v6&HKB+PLVIFpf%W=L=s537_r*#Z_%?E9W_o5I3m5` zlX3x#%>$b@tsbQUxRl+zXNc%R4JaSv=kxDV#OKj9{_W%*9eo-?4b(DAd7gJnWZ21d krxRaWmNf&7Mx)8%4-S#ia;H_!*Z=?k07*qoM6N<$f=7Nf1ONa4 literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge@3x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/icon/icn_notification_unread.imageset/Property 1=badge@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bd033bc3fb6794cff322bb77386ced77934666e6 GIT binary patch literal 1000 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!oCO|{#S9FJ79h;%I?XTvD9BhG zI{AXRZK#D7v>#cJl%U`egSh$OU(zttDBZTp1{Gv`Fg9Yb>1`2 zX@S?o&V61}ZZ?lKL82{j#ww)*XNTMC97ReUIiv-Af1OyqmObd7z}|ZmH!GzTGt`bR zw11s1)w*N(*(f#7Luvx<=aeiBJ4&vW+ViPS7)x>dor!pPVP+`psKXYJ$nl+qatP z7GM0}C~RZrcGd9Fv$TcXQ@N!0+S3GFo-f$fZ1rW^_NPnxG=#o5SQo!!6xq1w{{84H zTQ;Rnc>V7ko1;Zj&C??{RQ?_;ZGSHH{LB4Z*O*)Ls#$DZdE+kEzdg{k@cGrh=lX1d zTrZc1IF~Hre{x;JZDS1U*7JHy_DhjF zns#^x=j%q#&Zp%kCi)1Lls~_@Wp|rt^CYj;SF?B&*f+UN@Y4&D=(xD`vBgdvv6ZZ6 z7aec;+Oq2wyL6F)H_O`8FE-7wCmKVVVy?eE@XPJI%;aUtvL`&W96LU-Xfgb)`XFjF zXQ7sW(a9A&jy5)+oa_Puy?3Fp;AEj$_A zPCBXE=1J959i2Q&L{P zF-p@}B5(Svq$!(LeE)V$I?y~=%uJE%|A!9={9a3@=&7ZaOnbBAgNWBM5y!P**$%sx z#?O0JS9q?-%6eUhSFE|bctp^Ym?PQ?9#+o#(tW8x`}C>uNa4c [NotificationDummy] { -// return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.likeContent, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.writeComment, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.likeComment, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.welcome, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.transparency, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.violation, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.contentTransparency, -// minutes: "12분전"), -// NotificationDummy(profile: ImageLiterals.Notification.imgDog, -// userName: "벼니주", -// description: StringLiterals.Notification.commentTransparency, -// minutes: "12분전")] - return [] + return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.likeContent, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.writeComment, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.likeComment, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.welcome, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.transparency, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.violation, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.contentTransparency, + minutes: "12분전"), + NotificationDummy(profile: ImageLiterals.Notification.imgDog, + userName: "벼니주", + description: StringLiterals.Notification.commentTransparency, + minutes: "12분전")] +// return [] } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarController.swift b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarController.swift index 94e6790d..b6ca8b43 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarController.swift @@ -17,7 +17,7 @@ final class DontBeTabBarController: UITabBarController { self.setUI() self.setTabBarController() self.setInitialFont() - self.delegate = self + self.getTabBarBadgeAPI() } override func viewWillAppear(_ animated: Bool) { @@ -40,8 +40,10 @@ final class DontBeTabBarController: UITabBarController { // MARK: - Set UI private func setUI() { + self.delegate = self self.tabBar.backgroundColor = UIColor.donWhite // 탭바 배경색 설정 self.tabBar.isTranslucent = false // 배경이 투명하지 않도록 설정 + self.tabBar.clipsToBounds = true // 탭바 위쪽에 선 생기는 거 없앰 } // MARK: - Methods @@ -111,22 +113,29 @@ final class DontBeTabBarController: UITabBarController { } tabBarItem.setTitleTextAttributes(attributes, for: .normal) } + + private func getTabBarBadgeAPI() { + // 서버통신 -> 알림이 있으면 아래코드 처리 + // 현재는 앱 처음 시작할 때만 badge가 보임 -> 알림 탭 누르면 아예 기본으로 변경됨 + self.tabBar.items?[2].image = ImageLiterals.TabBar.icnNotificationUnread + } } extension DontBeTabBarController: UITabBarControllerDelegate { func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { - if selectedIndex == 1 { self.selectedIndex = 0 } + if selectedIndex == 2 { + self.tabBar.items?[2].image = ImageLiterals.TabBar.icnNotificationRead + } + if let selectedViewController = tabBarController.selectedViewController { applyFontColorAttributes(to: selectedViewController.tabBarItem, isSelected: true) } - let myViewController = tabBarController.viewControllers ?? [UIViewController()] - for (index, controller) in myViewController.enumerated() { if let tabBarItem = controller.tabBarItem { if index != tabBarController.selectedIndex { @@ -138,12 +147,10 @@ extension DontBeTabBarController: UITabBarControllerDelegate { func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { let index = viewControllers?.firstIndex(of: viewController) - if index == 1 { let destinationViewController = WriteViewController() self.navigationController?.pushViewController(destinationViewController, animated: true) } - return true } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift index 3c62e81e..d426e146 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/TabBar/DontBeTabBarItem.swift @@ -17,7 +17,7 @@ enum DontBeTabBarItem: CaseIterable { switch self { case .home: return ImageLiterals.TabBar.icnHome case .writing: return ImageLiterals.TabBar.icnWriting - case .notification: return ImageLiterals.TabBar.icnNotification + case .notification: return ImageLiterals.TabBar.icnNotificationRead case .myPage: return ImageLiterals.TabBar.icnMyPage } } From ef555e0d0f3e9bc5b78415302ef583bf027300c7 Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 23:15:30 +0900 Subject: [PATCH 12/25] =?UTF-8?q?[Feat]=20#33=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=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 --- DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift | 2 +- .../Notification/Cells/NotificationEmptyViewCell.swift | 1 - .../Notification/Cells/NotificationTableViewCell.swift | 2 ++ .../ViewControllers/NotificationViewController.swift | 2 -- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift index 926e67a4..4d5e9af2 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift @@ -56,7 +56,7 @@ extension UILabel { } class func lineNumber(label: UILabel, labelWidth: CGFloat) -> Int { - let boundingRect = label.text?.boundingRect(with: .zero, options: [.usesFontLeading], attributes: [.font: label.font!], context: nil) + let boundingRect = label.text?.boundingRect(with: .zero, options: [.usesFontLeading], attributes: [.font: label.font as Any], context: nil) return Int((boundingRect?.width ?? 0) / labelWidth + 1) } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift index a420ffd6..74a12da1 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationEmptyViewCell.swift @@ -37,7 +37,6 @@ final class NotificationEmptyViewCell: UITableViewCell, UITableViewCellRegistera return emptyDescirption }() - // MARK: - Life Cycles override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift index 7a884b2b..f9b6d6ed 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Cells/NotificationTableViewCell.swift @@ -16,6 +16,7 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera // MARK: - UI Components private let profileImage = UIImageView() + let notificationLabel: UILabel = { let notificationLabel = UILabel() notificationLabel.textColor = .donGray12 @@ -24,6 +25,7 @@ final class NotificationTableViewCell: UITableViewCell, UITableViewCellRegistera notificationLabel.lineBreakMode = .byCharWrapping return notificationLabel }() + private let minutes: UILabel = { let minutes = UILabel() minutes.textColor = .donGray8 diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift index 4c4f8bb9..955e60d6 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/ViewControllers/NotificationViewController.swift @@ -32,10 +32,8 @@ final class NotificationViewController: UIViewController { return notificationTableFooterView }() - // MARK: - Life Cycles - override func viewDidLoad() { super.viewDidLoad() From 1b0b17e725abab0c083988a1b6d4ff47c6a7592a Mon Sep 17 00:00:00 2001 From: heejoo Date: Fri, 12 Jan 2024 23:59:39 +0900 Subject: [PATCH 13/25] =?UTF-8?q?[Feat]=20#33=20-=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index 92bf1de0..67336642 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ 2AC9FB1B2B4DE77400D31071 /* AgreementListCustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1A2B4DE77400D31071 /* AgreementListCustomView.swift */; }; 2AC9FB1F2B4E634A00D31071 /* JoinAgreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */; }; 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */; }; + 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */; }; 2F1741882B500C270089FC4D /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741872B500C270089FC4D /* UIApplication+.swift */; }; 2F17418A2B500CC20089FC4D /* HomeBottomsheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; @@ -54,7 +55,6 @@ 2F8735442B4BE67300E55552 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735432B4BE67300E55552 /* HomeViewModel.swift */; }; 2F8735462B4C34A500E55552 /* HomeCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735452B4C34A500E55552 /* HomeCollectionView.swift */; }; 2F8735482B4C355100E55552 /* HomeCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735472B4C355100E55552 /* HomeCollectionViewCell.swift */; }; - 2F87354A2B4C427000E55552 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */; }; 2F87354C2B4D28D700E55552 /* HomeCollectionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87354B2B4D28D700E55552 /* HomeCollectionFooterView.swift */; }; 3C01692A2B4DC82D0075334B /* DontBePopupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C0920DB2B4D78DA003BD080 /* DontBePopupView.swift */; }; 3C0920DE2B4D98CD003BD080 /* DontBeToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C0920DD2B4D98CD003BD080 /* DontBeToastView.swift */; }; @@ -133,6 +133,7 @@ 2AC9FB1A2B4DE77400D31071 /* AgreementListCustomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgreementListCustomView.swift; sourceTree = ""; }; 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinAgreeView.swift; sourceTree = ""; }; 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyViewCell.swift; sourceTree = ""; }; + 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; 2F1741872B500C270089FC4D /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBottomsheetView.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -140,7 +141,6 @@ 2F8735432B4BE67300E55552 /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; 2F8735452B4C34A500E55552 /* HomeCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCollectionView.swift; sourceTree = ""; }; 2F8735472B4C355100E55552 /* HomeCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCollectionViewCell.swift; sourceTree = ""; }; - 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; 2F87354B2B4D28D700E55552 /* HomeCollectionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCollectionFooterView.swift; sourceTree = ""; }; 3C0920DB2B4D78DA003BD080 /* DontBePopupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DontBePopupView.swift; sourceTree = ""; }; 3C0920DD2B4D98CD003BD080 /* DontBeToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DontBeToastView.swift; sourceTree = ""; }; @@ -538,7 +538,6 @@ 2F87353F2B4BE65300E55552 /* HomeView.swift */, 2F8735452B4C34A500E55552 /* HomeCollectionView.swift */, 2F87354B2B4D28D700E55552 /* HomeCollectionFooterView.swift */, - 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */, 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */, ); path = Views; @@ -597,6 +596,7 @@ 3CE9C1332B4C4BA90086E4A3 /* Helpers */ = { isa = PBXGroup; children = ( + 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */, 3C0920DB2B4D78DA003BD080 /* DontBePopupView.swift */, 3C0920DD2B4D98CD003BD080 /* DontBeToastView.swift */, 3CEE4CBC2B500A7800F506AF /* DontBeTransparencyInfoView.swift */, @@ -607,7 +607,6 @@ 2A8D70C22B4D7FF5009F4C6C /* BackButton.swift */, 2A8D70B32B4C999F009F4C6C /* CustomButton.swift */, 2A31FF582B4F3A8B00FEEED9 /* UserInfo.swift */, - 2F8735492B4C427000E55552 /* UICollectionViewRegisterable.swift */, 2A5220EB2B507F2A001510B7 /* UITableViewCellRegisterable.swift */, ); path = Helpers; @@ -734,9 +733,9 @@ buildActionMask = 2147483647; files = ( 2A8D70CA2B4D9787009F4C6C /* IntroductionView.swift in Sources */, - 2F87354A2B4C427000E55552 /* UICollectionViewRegisterable.swift in Sources */, 2A8D70CA2B4D9787009F4C6C /* IntroductionView.swift in Sources */, 3C6193172B3A7A7B00220CEB /* UIStackView+.swift in Sources */, + 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */, 3CE9C12E2B4C08AE0086E4A3 /* WriteTextView.swift in Sources */, 2A8868D62B5069790017513A /* OnboardingEndingView.swift in Sources */, 2A8D70BD2B4D61A1009F4C6C /* OnboardingDummy.swift in Sources */, From f979fb2357fcdd89cfc737857be5ccadcf52b866 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 00:38:18 +0900 Subject: [PATCH 14/25] =?UTF-8?q?[Feat]=20#45=20-=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=EC=A0=88=EB=B0=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 4 + .../Global/Literals/StringLiterals.swift | 1 + .../MyPageEditProfileViewController.swift | 111 ++++++++++- .../MyPage/Views/MyPageEditView.swift | 182 ++++++++++++++++++ 4 files changed, 291 insertions(+), 7 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index 67336642..9d959ba1 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ 2AC9FB1F2B4E634A00D31071 /* JoinAgreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */; }; 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */; }; 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */; }; + 2AF069B22B518F8E00CA3E48 /* MyPageEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */; }; 2F1741882B500C270089FC4D /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741872B500C270089FC4D /* UIApplication+.swift */; }; 2F17418A2B500CC20089FC4D /* HomeBottomsheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; @@ -134,6 +135,7 @@ 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinAgreeView.swift; sourceTree = ""; }; 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyViewCell.swift; sourceTree = ""; }; 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; + 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditView.swift; sourceTree = ""; }; 2F1741872B500C270089FC4D /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBottomsheetView.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -637,6 +639,7 @@ 3CF184D02B4EFF9900816D5F /* MyPageProfileView.swift */, 3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */, 3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */, + 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */, ); path = Views; sourceTree = ""; @@ -755,6 +758,7 @@ 3C49936B2B4FF118002A99CF /* MyPageSegmentedControlView.swift in Sources */, 3C2F54522B51224500E7BF01 /* MyPageAccountInfoViewController.swift in Sources */, 3CE9C1352B4C4BC20086E4A3 /* CircleProgressbar.swift in Sources */, + 2AF069B22B518F8E00CA3E48 /* MyPageEditView.swift in Sources */, 3C6193192B3A7AC700220CEB /* Config.swift in Sources */, 3CF184D12B4EFF9900816D5F /* MyPageProfileView.swift in Sources */, 3C61931B2B3A7AD000220CEB /* NetworkError.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index eac93887..4c73f7c3 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -56,6 +56,7 @@ enum StringLiterals { static let next = "다음으로" static let start = "시작하기" static let finish = "완료하기" + static let editFinish = "수정완료" } enum Toast { diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift index a4444e43..714df41f 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift @@ -9,26 +9,123 @@ import UIKit class MyPageEditProfileViewController: UIViewController { + // MARK: - Properties + + var isTrue: Bool = true + + // MARK: - UI Components + + let originView = MyPageEditView() + + // MARK: - Life Cycles + + override func loadView() { + super.loadView() + + view = originView + } + override func viewDidLoad() { super.viewDidLoad() - - tabBarController?.tabBar.isHidden = true - self.view.backgroundColor = .donPrimary - self.title = StringLiterals.MyPage.MyPageEditNavigationTitle + + setUI() + setAddTarget() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + } +} + +// MARK: - Extensions + +extension MyPageEditProfileViewController { + private func setUI() { + tabBarController?.tabBar.isHidden = true + self.view.backgroundColor = .donGray1 + self.title = StringLiterals.MyPage.MyPageEditNavigationTitle self.navigationItem.hidesBackButton = true self.navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.donBlack] - - let backButton = UIBarButtonItem.backButton(target: self, action: #selector(backButtonPressed)) + } + + private func setAddTarget() { + let backButton = UIBarButtonItem.backButton(target: self, action: #selector(backButtonTapped)) self.navigationItem.leftBarButtonItem = backButton + + originView.duplicationCheckButton.addTarget(self, action: #selector(duplicationCheckButtonTapped), for: .touchUpInside) } @objc - private func backButtonPressed() { + private func backButtonTapped() { self.navigationController?.popViewController(animated: true) } + + @objc + private func duplicationCheckButtonTapped() { + // 중복확인 서버통신에 성공 + self.originView.nickNameTextField.resignFirstResponder() + self.originView.finishActiveButton.isHidden = !isTrue + + // 중복확인 -> 성공 (서버통신으로 isTrue 값 변경해주어야함) + if isTrue { + self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass + self.originView.duplicationCheckDescription.textColor = .donSecondary + } + // 중복확인 -> 실패 + else { + self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationNotPass + self.originView.duplicationCheckDescription.textColor = .donError + } + } +} + +// MARK: - UITextFieldDelegate + +extension MyPageEditProfileViewController: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let maxLength = 12 // 글자수 제한 + let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text + let addedText = string // 입력한 text + let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 + let newTextLength = newText.count // 합쳐진 text의 길이 + + // 글자수 제한 + if newTextLength <= maxLength { + return true + } + + let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 + let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 + let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 + + if separatedCharactersCount == 1 && !addedText.isConsonant { + return true + } else if separatedCharactersCount == 2 && addedText.isConsonant { + return true + } else if separatedCharactersCount == 3 && addedText.isConsonant { + return true + } + return false + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" // textField에 수정이 반영된 후의 text + let maxLength = 12 // 글자 수 제한 + if text.count >= maxLength { + let startIndex = text.startIndex + let endIndex = text.index(startIndex, offsetBy: maxLength - 1) + let fixedText = String(text[startIndex...endIndex]) + textField.text = fixedText + self.originView.numOfLetters.text = "12/12" + } else { + self.originView.numOfLetters.text = "\(text.count)/12" + } + } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift new file mode 100644 index 00000000..24e949f7 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift @@ -0,0 +1,182 @@ +// +// MyPageEditView.swift +// DontBe-iOS +// +// Created by 변희주 on 1/13/24. +// + +import UIKit + +import SnapKit + +final class MyPageEditView: UIView { + + // MARK: - Properties + + // MARK: - UI Components + + private let topDivisionLine = UIView().makeDivisionLine() + + let profileImage: UIImageView = { + let profileImage = UIImageView() + profileImage.setCircularImage(image: ImageLiterals.Common.imgProfile) + return profileImage + }() + + let plusButton: UIButton = { + let plusButton = UIButton() + plusButton.setImage(ImageLiterals.Join.btnPlus, for: .normal) + return plusButton + }() + + private let nickNameLabel: UILabel = { + let nickNameLabel = UILabel() + nickNameLabel.text = StringLiterals.Join.nickName + nickNameLabel.textColor = .donBlack + nickNameLabel.font = .font(.body1) + return nickNameLabel + }() + + let nickNameTextField: UITextField = { + let nickNameTextField = UITextField() + nickNameTextField.placeholder = StringLiterals.Join.nickNamePlaceHolder + nickNameTextField.textAlignment = .left + nickNameTextField.textColor = .donBlack + nickNameTextField.font = .font(.body4) + nickNameTextField.backgroundColor = .donGray2 + nickNameTextField.layer.cornerRadius = 4.adjusted + nickNameTextField.setPlaceholderColor(.donGray7) + nickNameTextField.setLeftPaddingPoints(14.adjusted) + nickNameTextField.setRightPaddingPoints(14.adjusted) + return nickNameTextField + }() + + let numOfLetters: UILabel = { + let label = UILabel() + label.text = "(0/12)" + label.textColor = .donGray7 + label.font = .font(.caption4) + return label + }() + + let duplicationCheckButton: UIButton = { + let duplicationCheckButton = UIButton() + duplicationCheckButton.setTitle(StringLiterals.Join.duplicationCheck, for: .normal) + duplicationCheckButton.setTitleColor(.donBlack, for: .normal) + duplicationCheckButton.titleLabel?.font = .font(.body3) + duplicationCheckButton.layer.cornerRadius = 4.adjusted + duplicationCheckButton.layer.masksToBounds = true + duplicationCheckButton.backgroundColor = .donPrimary + return duplicationCheckButton + }() + + let duplicationCheckDescription: UILabel = { + let duplicationCheckDescription = UILabel() + duplicationCheckDescription.text = StringLiterals.Join.duplicationCheckDescription + duplicationCheckDescription.textColor = .donGray8 + duplicationCheckDescription.font = .font(.caption4) + return duplicationCheckDescription + }() + + let finishButton: UIButton = { + let finishButton = CustomButton(title: StringLiterals.Button.finish, backColor: .donGray4, titleColor: .donGray9) + finishButton.isEnabled = false + return finishButton + }() + + let finishActiveButton: UIButton = { + let finishActiveButton = CustomButton(title: StringLiterals.Button.editFinish, backColor: .donBlack, titleColor: .donWhite) + finishActiveButton.isHidden = true + return finishActiveButton + }() + + // MARK: - Life Cycles + + override init(frame: CGRect) { + super.init(frame: frame) + + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extensions + +extension MyPageEditView { + func setHierarchy() { + self.addSubviews(topDivisionLine, + profileImage, + plusButton, + nickNameLabel, + nickNameTextField, + duplicationCheckButton, + duplicationCheckDescription, + finishButton, + finishActiveButton) + + nickNameTextField.addSubview(numOfLetters) + } + + func setLayout() { + topDivisionLine.snp.makeConstraints { + $0.leading.trailing.equalToSuperview() + $0.top.equalTo(self.safeAreaLayoutGuide) + $0.height.equalTo(1.adjusted) + } + + profileImage.snp.makeConstraints { + $0.top.equalTo(self.safeAreaLayoutGuide).inset(52.adjustedH) + $0.centerX.equalToSuperview() + $0.size.equalTo(100.adjusted) + } + + plusButton.snp.makeConstraints { + $0.top.equalTo(profileImage).offset(72.adjusted) + $0.leading.equalTo(profileImage).offset(78.adjusted) + $0.size.equalTo(34.adjusted) + } + + nickNameLabel.snp.makeConstraints { + $0.top.equalTo(self.safeAreaLayoutGuide).inset(171.adjustedH) + $0.leading.equalToSuperview().inset(16.adjusted) + } + + nickNameTextField.snp.makeConstraints { + $0.top.equalTo(nickNameLabel.snp.bottom).offset(10.adjustedH) + $0.leading.equalToSuperview().inset(16.adjusted) + $0.trailing.equalToSuperview().inset(107.adjusted) + $0.height.equalTo(44.adjusted) + } + + numOfLetters.snp.makeConstraints { + $0.trailing.equalToSuperview().inset(14.adjusted) + $0.centerY.equalToSuperview() + } + + duplicationCheckButton.snp.makeConstraints { + $0.centerY.height.equalTo(nickNameTextField) + $0.leading.equalTo(nickNameTextField.snp.trailing).offset(6.adjusted) + $0.trailing.equalToSuperview().inset(16.adjusted) + } + + duplicationCheckDescription.snp.makeConstraints { + $0.top.equalTo(nickNameTextField.snp.bottom).offset(6.adjustedH) + $0.leading.equalToSuperview().inset(16.adjusted) + } + + finishButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } + + finishActiveButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } + } +} From 3ce607de1877d2dcc6a71d536e685e8a8acbad26 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 06:02:40 +0900 Subject: [PATCH 15/25] =?UTF-8?q?[Feat]=20#45=20-=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20UI=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 4 + .../Global/Extension/UITextView+.swift | 17 ++- .../Global/Literals/StringLiterals.swift | 2 + .../MyPageEditProfileViewController.swift | 108 ++++++-------- .../MyPage/Views/MyPageEditTextView.swift | 139 ++++++++++++++++++ .../MyPage/Views/MyPageEditView.swift | 78 +++++++--- .../Write/Views/WriteTextView.swift | 3 +- 7 files changed, 257 insertions(+), 94 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index 9d959ba1..a98bcf01 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */; }; 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */; }; 2AF069B22B518F8E00CA3E48 /* MyPageEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */; }; + 2AF069B42B5194F300CA3E48 /* MyPageEditTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */; }; 2F1741882B500C270089FC4D /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741872B500C270089FC4D /* UIApplication+.swift */; }; 2F17418A2B500CC20089FC4D /* HomeBottomsheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; @@ -136,6 +137,7 @@ 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyViewCell.swift; sourceTree = ""; }; 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditView.swift; sourceTree = ""; }; + 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditTextView.swift; sourceTree = ""; }; 2F1741872B500C270089FC4D /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBottomsheetView.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -640,6 +642,7 @@ 3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */, 3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */, 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */, + 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */, ); path = Views; sourceTree = ""; @@ -802,6 +805,7 @@ 3C2F54542B5126C600E7BF01 /* UIBarButtonItem+.swift in Sources */, 2A2671FD2B4C3A9F009D214F /* LoginViewModel.swift in Sources */, 3C6192EF2B3A719A00220CEB /* SceneDelegate.swift in Sources */, + 2AF069B42B5194F300CA3E48 /* MyPageEditTextView.swift in Sources */, 2A2672052B4C3C00009D214F /* CancelBag.swift in Sources */, 2A84465A2B4EE8B400F98F2A /* JoinProfileViewController.swift in Sources */, 2A8868DA2B506D720017513A /* NotificationViewController.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UITextView+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UITextView+.swift index 4131ed22..3fad8b52 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UITextView+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UITextView+.swift @@ -10,24 +10,25 @@ import UIKit import SnapKit extension UITextView { - func addPlaceholder(_ placeholder: String) { + func addPlaceholder(_ placeholder: String, padding: UIEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)) { let placeholderLabel = UILabel() placeholderLabel.text = placeholder placeholderLabel.textColor = .donGray8 placeholderLabel.textAlignment = .left placeholderLabel.font = self.font - + placeholderLabel.numberOfLines = 0 + self.addSubview(placeholderLabel) - + placeholderLabel.snp.makeConstraints { - $0.edges.equalToSuperview() + $0.edges.equalToSuperview().inset(padding) } - + NotificationCenter.default.addObserver(self, selector: #selector(textViewTextDidChange), name: UITextView.textDidChangeNotification, object: nil) - + updatePlaceholderVisibility(placeholderLabel) } - + @objc private func textViewTextDidChange() { for subview in self.subviews { if let placeholderLabel = subview as? UILabel, placeholderLabel.textColor == .donGray8 { @@ -35,7 +36,7 @@ extension UITextView { } } } - + private func updatePlaceholderVisibility(_ placeholderLabel: UILabel) { placeholderLabel.isHidden = !self.text.isEmpty } diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 4c73f7c3..452ede8f 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -83,6 +83,8 @@ enum StringLiterals { static let MyPageNavigationTitle = "마이" static let MyPageEditNavigationTitle = "프로필 편집" static let MyPageAccountInfoNavigationTitle = "계정 정보" + static let myPageEditIntroduction = "한줄 소개" + static let myPageEditIntroductionPlease = "한 줄로 자신을 소개해주세요." } enum TransparencyInfo { diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift index 714df41f..8208ac85 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift @@ -7,51 +7,72 @@ import UIKit -class MyPageEditProfileViewController: UIViewController { +import SnapKit +final class MyPageEditProfileViewController: UIViewController { + // MARK: - Properties var isTrue: Bool = true - + // MARK: - UI Components let originView = MyPageEditView() + let editView = MyPageEditTextView() - // MARK: - Life Cycles - - override func loadView() { - super.loadView() - - view = originView + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + view.endEditing(true) } + // MARK: - Life Cycles + override func viewDidLoad() { super.viewDidLoad() setUI() + setHierarchy() + setLayout() setAddTarget() } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - } } // MARK: - Extensions extension MyPageEditProfileViewController { private func setUI() { - tabBarController?.tabBar.isHidden = true + self.tabBarController?.tabBar.isHidden = true + self.tabBarController?.tabBar.isTranslucent = true self.view.backgroundColor = .donGray1 self.title = StringLiterals.MyPage.MyPageEditNavigationTitle self.navigationItem.hidesBackButton = true self.navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.donBlack] } + private func setHierarchy() { + self.view.addSubviews(originView, + editView) + } + + private func setLayout() { + originView.snp.makeConstraints { + $0.top.equalTo(self.view.safeAreaLayoutGuide) + $0.leading.trailing.equalToSuperview() + $0.bottom.equalToSuperview().inset(323.adjusted) + } + + editView.snp.makeConstraints { + $0.top.equalTo(originView.duplicationCheckDescription.snp.bottom).offset(16.adjustedH) + $0.leading.trailing.bottom.equalToSuperview() + $0.bottom.equalTo(self.view.safeAreaLayoutGuide) + } + } + private func setAddTarget() { let backButton = UIBarButtonItem.backButton(target: self, action: #selector(backButtonTapped)) self.navigationItem.leftBarButtonItem = backButton + NotificationCenter.default.addObserver(self, selector: #selector(textFieldTisEmpty), name: UITextField.textDidChangeNotification, object: nil) + originView.duplicationCheckButton.addTarget(self, action: #selector(duplicationCheckButtonTapped), for: .touchUpInside) } @@ -64,68 +85,29 @@ extension MyPageEditProfileViewController { private func duplicationCheckButtonTapped() { // 중복확인 서버통신에 성공 self.originView.nickNameTextField.resignFirstResponder() - self.originView.finishActiveButton.isHidden = !isTrue + self.editView.postButton.isEnabled = !isTrue // 중복확인 -> 성공 (서버통신으로 isTrue 값 변경해주어야함) if isTrue { self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass + self.editView.postButton.setTitleColor(.donWhite, for: .normal) + self.editView.postButton.backgroundColor = .donBlack self.originView.duplicationCheckDescription.textColor = .donSecondary } // 중복확인 -> 실패 else { self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationNotPass + self.editView.postButton.setTitleColor(.donGray9, for: .normal) + self.editView.postButton.backgroundColor = .donGray4 self.originView.duplicationCheckDescription.textColor = .donError } } -} - - -// MARK: - UITextFieldDelegate - -extension MyPageEditProfileViewController: UITextFieldDelegate { - func textFieldShouldReturn(_ textField: UITextField) -> Bool{ - // 키보드 내리면서 동작 - textField.resignFirstResponder() - return true - } - - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - let maxLength = 12 // 글자수 제한 - let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text - let addedText = string // 입력한 text - let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 - let newTextLength = newText.count // 합쳐진 text의 길이 - - // 글자수 제한 - if newTextLength <= maxLength { - return true - } - - let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 - let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 - let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 - - if separatedCharactersCount == 1 && !addedText.isConsonant { - return true - } else if separatedCharactersCount == 2 && addedText.isConsonant { - return true - } else if separatedCharactersCount == 3 && addedText.isConsonant { - return true - } - return false - } - func textFieldDidChangeSelection(_ textField: UITextField) { - let text = textField.text ?? "" // textField에 수정이 반영된 후의 text - let maxLength = 12 // 글자 수 제한 - if text.count >= maxLength { - let startIndex = text.startIndex - let endIndex = text.index(startIndex, offsetBy: maxLength - 1) - let fixedText = String(text[startIndex...endIndex]) - textField.text = fixedText - self.originView.numOfLetters.text = "12/12" - } else { - self.originView.numOfLetters.text = "\(text.count)/12" - } + @objc + private func textFieldTisEmpty() { + print("dsafadsfdsafdsafadsf") + self.editView.postButton.setTitleColor(.donGray9, for: .normal) + self.editView.postButton.backgroundColor = .donGray4 + self.editView.postButton.isEnabled = false } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift new file mode 100644 index 00000000..034e64cc --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift @@ -0,0 +1,139 @@ +// +// MyPageEditTextView.swift +// DontBe-iOS +// +// Created by 변희주 on 1/13/24. +// + +import UIKit + +import SnapKit + +final class MyPageEditTextView: UIView { + + // MARK: - Properties + + let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .heavy) // 햅틱 기능 + let maxLength = 50 // 최대 글자 수 + + // MARK: - UI Components + + private let introduction: UILabel = { + let introduction = UILabel() + introduction.text = StringLiterals.MyPage.myPageEditIntroduction + introduction.textColor = .donBlack + introduction.font = .font(.body1) + return introduction + }() + + private let contentTextView: UITextView = { + let textView = UITextView() + textView.font = UIFont.font(.body4) + textView.textColor = .donGray12 + textView.tintColor = .donPrimary + textView.backgroundColor = .donGray2 + textView.textContainerInset = UIEdgeInsets(top: 14.adjusted, left: 14.adjusted, bottom: 14.adjusted, right: 14.adjusted) + textView.addPlaceholder(StringLiterals.MyPage.myPageEditIntroductionPlease, padding: UIEdgeInsets(top: 14.adjusted, left: 14.adjusted, bottom: 14.adjusted, right: 14.adjusted)) + textView.layer.cornerRadius = 4.adjusted + textView.layer.masksToBounds = true + textView.textContainer.lineFragmentPadding = 0 + textView.textContainer.lineBreakMode = .byWordWrapping + textView.textContainer.maximumNumberOfLines = 0 + return textView + }() + + private let numOfLetters: UILabel = { + let numOfLetters = UILabel() + numOfLetters.text = "0/50" + numOfLetters.textColor = .donGray7 + numOfLetters.font = .font(.caption4) + return numOfLetters + }() + + let postButton: UIButton = { + let button = UIButton() + button.setTitle(StringLiterals.Button.editFinish, for: .normal) + button.setTitleColor(.donGray9, for: .normal) + button.titleLabel?.font = UIFont.font(.body3) + button.backgroundColor = .donGray4 + button.layer.cornerRadius = 4.adjusted + button.isEnabled = false + return button + }() + + // MARK: - Life Cycles + + override init(frame: CGRect) { + super.init(frame: frame) + + setDelegate() + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extensions + +extension MyPageEditTextView { + func setDelegate() { + self.contentTextView.delegate = self + } + + func setHierarchy() { + self.addSubviews(introduction, + contentTextView, + numOfLetters, + postButton) + } + + func setLayout() { + introduction.snp.makeConstraints { + $0.top.equalToSuperview().inset(5.adjusted) + $0.leading.equalToSuperview().inset(16.adjusted) + } + + contentTextView.snp.makeConstraints { + $0.top.equalTo(introduction.snp.bottom).offset(6.adjustedH) + $0.leading.trailing.equalToSuperview().inset(16.adjusted) + $0.height.equalTo(120.adjusted) + } + + numOfLetters.snp.makeConstraints { + $0.trailing.equalTo(contentTextView).inset(14.adjusted) + $0.bottom.equalTo(contentTextView).inset(12.adjusted) + } + + postButton.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.width.equalTo(342.adjusted) + $0.height.equalTo(50.adjusted) + $0.bottom.equalToSuperview().inset(29.adjusted) + } + } +} + +extension MyPageEditTextView: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + let textLength = contentTextView.text.count + textView.text = String(textView.text.prefix(maxLength)) + + postButton.setTitleColor(.donWhite, for: .normal) + postButton.backgroundColor = .donBlack + postButton.isEnabled = true + + if textLength == 0 { + postButton.setTitleColor(.donGray9, for: .normal) + postButton.backgroundColor = .donGray4 + postButton.isEnabled = false + } else if textLength < 50 { + self.numOfLetters.text = "\(textLength)/50" + } else { + self.numOfLetters.text = "50/50" + } + } +} diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift index 24e949f7..c1599e5b 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift @@ -78,18 +78,6 @@ final class MyPageEditView: UIView { return duplicationCheckDescription }() - let finishButton: UIButton = { - let finishButton = CustomButton(title: StringLiterals.Button.finish, backColor: .donGray4, titleColor: .donGray9) - finishButton.isEnabled = false - return finishButton - }() - - let finishActiveButton: UIButton = { - let finishActiveButton = CustomButton(title: StringLiterals.Button.editFinish, backColor: .donBlack, titleColor: .donWhite) - finishActiveButton.isHidden = true - return finishActiveButton - }() - // MARK: - Life Cycles override init(frame: CGRect) { @@ -97,6 +85,7 @@ final class MyPageEditView: UIView { setHierarchy() setLayout() + setDelegate() } @available(*, unavailable) @@ -115,9 +104,7 @@ extension MyPageEditView { nickNameLabel, nickNameTextField, duplicationCheckButton, - duplicationCheckDescription, - finishButton, - finishActiveButton) + duplicationCheckDescription) nickNameTextField.addSubview(numOfLetters) } @@ -168,15 +155,64 @@ extension MyPageEditView { $0.top.equalTo(nickNameTextField.snp.bottom).offset(6.adjustedH) $0.leading.equalToSuperview().inset(16.adjusted) } + } + + private func setDelegate() { + self.nickNameTextField.delegate = self + } +} + +// MARK: - UITextFieldDelegate + +extension MyPageEditView: UITextFieldDelegate { + + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + + self.duplicationCheckDescription.text = StringLiterals.Join.duplicationCheckDescription + self.duplicationCheckDescription.textColor = .donGray8 - finishButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() + let maxLength = 12 // 글자수 제한 + let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text + let addedText = string // 입력한 text + let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 + let newTextLength = newText.count // 합쳐진 text의 길이 + + // 글자수 제한 + if newTextLength <= maxLength { + return true } - finishActiveButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() + let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 + let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 + let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 + + if separatedCharactersCount == 1 && !addedText.isConsonant { + return true + } else if separatedCharactersCount == 2 && addedText.isConsonant { + return true + } else if separatedCharactersCount == 3 && addedText.isConsonant { + return true + } + return false + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" // textField에 수정이 반영된 후의 text + let maxLength = 12 // 글자 수 제한 + if text.count >= maxLength { + let startIndex = text.startIndex + let endIndex = text.index(startIndex, offsetBy: maxLength - 1) + let fixedText = String(text[startIndex...endIndex]) + textField.text = fixedText + self.numOfLetters.text = "\(maxLength)/\(maxLength)" + } else { + self.numOfLetters.text = "\(text.count)/\(maxLength)" } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Write/Views/WriteTextView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Write/Views/WriteTextView.swift index 62003d9e..d4209596 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Write/Views/WriteTextView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Write/Views/WriteTextView.swift @@ -38,8 +38,7 @@ final class WriteTextView: UIView { textView.textColor = .donBlack textView.tintColor = .donPrimary textView.backgroundColor = .clear - textView.addPlaceholder(StringLiterals.Write.writeContentPlaceholder) - + textView.addPlaceholder(StringLiterals.Write.writeContentPlaceholder, padding: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)) textView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) textView.textContainer.lineFragmentPadding = 0 textView.textContainer.lineBreakMode = .byWordWrapping From 19149139e29151c0de15663ee6f93f60bd9f2ab0 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 06:08:48 +0900 Subject: [PATCH 16/25] =?UTF-8?q?[Chore]=20#45=20-=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=20=EB=AA=85=ED=99=95=ED=95=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 --- DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj | 16 ++++++++-------- .../MyPageEditProfileViewController.swift | 4 ++-- ...ew.swift => MyPageIntroductionEditView.swift} | 6 +++--- ...itView.swift => MyPageNicknameEditView.swift} | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) rename DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/{MyPageEditTextView.swift => MyPageIntroductionEditView.swift} (96%) rename DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/{MyPageEditView.swift => MyPageNicknameEditView.swift} (98%) diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index a98bcf01..7bbbeac4 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -48,8 +48,8 @@ 2AC9FB1F2B4E634A00D31071 /* JoinAgreeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */; }; 2AF069AE2B514B0100CA3E48 /* NotificationEmptyViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */; }; 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */; }; - 2AF069B22B518F8E00CA3E48 /* MyPageEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */; }; - 2AF069B42B5194F300CA3E48 /* MyPageEditTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */; }; + 2AF069B22B518F8E00CA3E48 /* MyPageNicknameEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B12B518F8E00CA3E48 /* MyPageNicknameEditView.swift */; }; + 2AF069B42B5194F300CA3E48 /* MyPageIntroductionEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B32B5194F300CA3E48 /* MyPageIntroductionEditView.swift */; }; 2F1741882B500C270089FC4D /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741872B500C270089FC4D /* UIApplication+.swift */; }; 2F17418A2B500CC20089FC4D /* HomeBottomsheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; @@ -136,8 +136,8 @@ 2AC9FB1E2B4E634A00D31071 /* JoinAgreeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinAgreeView.swift; sourceTree = ""; }; 2AF069AD2B514B0100CA3E48 /* NotificationEmptyViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationEmptyViewCell.swift; sourceTree = ""; }; 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; - 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditView.swift; sourceTree = ""; }; - 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditTextView.swift; sourceTree = ""; }; + 2AF069B12B518F8E00CA3E48 /* MyPageNicknameEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNicknameEditView.swift; sourceTree = ""; }; + 2AF069B32B5194F300CA3E48 /* MyPageIntroductionEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageIntroductionEditView.swift; sourceTree = ""; }; 2F1741872B500C270089FC4D /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBottomsheetView.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -641,8 +641,8 @@ 3CF184D02B4EFF9900816D5F /* MyPageProfileView.swift */, 3C49936A2B4FF118002A99CF /* MyPageSegmentedControlView.swift */, 3C4993642B4F2471002A99CF /* MyPageSegmentedControl.swift */, - 2AF069B12B518F8E00CA3E48 /* MyPageEditView.swift */, - 2AF069B32B5194F300CA3E48 /* MyPageEditTextView.swift */, + 2AF069B12B518F8E00CA3E48 /* MyPageNicknameEditView.swift */, + 2AF069B32B5194F300CA3E48 /* MyPageIntroductionEditView.swift */, ); path = Views; sourceTree = ""; @@ -761,7 +761,7 @@ 3C49936B2B4FF118002A99CF /* MyPageSegmentedControlView.swift in Sources */, 3C2F54522B51224500E7BF01 /* MyPageAccountInfoViewController.swift in Sources */, 3CE9C1352B4C4BC20086E4A3 /* CircleProgressbar.swift in Sources */, - 2AF069B22B518F8E00CA3E48 /* MyPageEditView.swift in Sources */, + 2AF069B22B518F8E00CA3E48 /* MyPageNicknameEditView.swift in Sources */, 3C6193192B3A7AC700220CEB /* Config.swift in Sources */, 3CF184D12B4EFF9900816D5F /* MyPageProfileView.swift in Sources */, 3C61931B2B3A7AD000220CEB /* NetworkError.swift in Sources */, @@ -805,7 +805,7 @@ 3C2F54542B5126C600E7BF01 /* UIBarButtonItem+.swift in Sources */, 2A2671FD2B4C3A9F009D214F /* LoginViewModel.swift in Sources */, 3C6192EF2B3A719A00220CEB /* SceneDelegate.swift in Sources */, - 2AF069B42B5194F300CA3E48 /* MyPageEditTextView.swift in Sources */, + 2AF069B42B5194F300CA3E48 /* MyPageIntroductionEditView.swift in Sources */, 2A2672052B4C3C00009D214F /* CancelBag.swift in Sources */, 2A84465A2B4EE8B400F98F2A /* JoinProfileViewController.swift in Sources */, 2A8868DA2B506D720017513A /* NotificationViewController.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift index 8208ac85..db3416f6 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift @@ -17,8 +17,8 @@ final class MyPageEditProfileViewController: UIViewController { // MARK: - UI Components - let originView = MyPageEditView() - let editView = MyPageEditTextView() + let originView = MyPageNicknameEditView() + let editView = MyPageIntroductionEditView() override func touchesBegan(_ touches: Set, with event: UIEvent?) { view.endEditing(true) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift similarity index 96% rename from DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift rename to DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift index 034e64cc..342e6a68 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditTextView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift @@ -9,7 +9,7 @@ import UIKit import SnapKit -final class MyPageEditTextView: UIView { +final class MyPageIntroductionEditView: UIView { // MARK: - Properties @@ -79,7 +79,7 @@ final class MyPageEditTextView: UIView { // MARK: - Extensions -extension MyPageEditTextView { +extension MyPageIntroductionEditView { func setDelegate() { self.contentTextView.delegate = self } @@ -117,7 +117,7 @@ extension MyPageEditTextView { } } -extension MyPageEditTextView: UITextViewDelegate { +extension MyPageIntroductionEditView: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { let textLength = contentTextView.text.count textView.text = String(textView.text.prefix(maxLength)) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift similarity index 98% rename from DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift rename to DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift index c1599e5b..384698cf 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift @@ -9,7 +9,7 @@ import UIKit import SnapKit -final class MyPageEditView: UIView { +final class MyPageNicknameEditView: UIView { // MARK: - Properties @@ -96,7 +96,7 @@ final class MyPageEditView: UIView { // MARK: - Extensions -extension MyPageEditView { +extension MyPageNicknameEditView { func setHierarchy() { self.addSubviews(topDivisionLine, profileImage, @@ -164,7 +164,7 @@ extension MyPageEditView { // MARK: - UITextFieldDelegate -extension MyPageEditView: UITextFieldDelegate { +extension MyPageNicknameEditView: UITextFieldDelegate { func textFieldShouldReturn(_ textField: UITextField) -> Bool{ // 키보드 내리면서 동작 From a363cf2a67616178076c22d2749a47a68fcda65f Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 07:01:09 +0900 Subject: [PATCH 17/25] =?UTF-8?q?[Feat]=20#45=20-=20=20TextFieldDelegate?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20&&=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85,=20=EC=98=A8=EB=B3=B4=EB=94=A9=20=EC=9E=AC?= =?UTF-8?q?=EC=A0=95=EB=B9=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/Extension/UILabel+.swift | 4 +- .../Presentation/Helpers/UserInfo.swift | 1 + .../JoinProfileViewController.swift | 65 ++++--------------- .../Join/Views/JoinAgreeView.swift | 3 +- .../Join/Views/JoinProfileView.swift | 60 ++++++++++++++++- .../ViewControllers/LoginViewController.swift | 11 +++- .../MyPageEditProfileViewController.swift | 1 - .../OnboardingEndingViewController.swift | 55 +--------------- .../OnboardingViewController.swift | 2 +- .../Onboarding/Views/IntroductionView.swift | 2 +- .../Views/OnboardingEndingView.swift | 53 ++++++++++++++- 11 files changed, 139 insertions(+), 118 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift index 4d5e9af2..79c67c28 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Extension/UILabel+.swift @@ -8,7 +8,7 @@ import UIKit extension UILabel { - func setTextWithLineHeight(text: String?, lineHeight: CGFloat){ + func setTextWithLineHeight(text: String?, lineHeight: CGFloat, alignment: NSTextAlignment){ if let text = text { let style = NSMutableParagraphStyle() style.maximumLineHeight = lineHeight @@ -21,7 +21,7 @@ extension UILabel { let attrString = NSAttributedString(string: text, attributes: attributes) self.attributedText = attrString - self.textAlignment = .center + self.textAlignment = alignment } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift b/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift index 5e4c5124..e8a1d942 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Helpers/UserInfo.swift @@ -10,6 +10,7 @@ import Foundation struct UserInfo: Codable { let isSocialLogined: Bool let isJoinedApp: Bool + let isNotFirstUser: Bool let isOnboardingFinished: Bool let userNickname: String } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift index c239b24c..6472e13a 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/ViewControllers/JoinProfileViewController.swift @@ -87,7 +87,7 @@ extension JoinProfileViewController { } private func setDelegate() { - self.originView.nickNameTextField.delegate = self + NotificationCenter.default.addObserver(self, selector: #selector(textFieldTisEmpty), name: UITextField.textDidChangeNotification, object: nil) } private func bindViewModel() { @@ -100,7 +100,8 @@ extension JoinProfileViewController { self.navigationController?.popViewController(animated: true) } else { saveUserData(UserInfo(isSocialLogined: true, - isJoinedApp: true, + isJoinedApp: true, + isNotFirstUser: false, isOnboardingFinished: false, userNickname: self.originView.nickNameTextField.text ?? "")) @@ -112,10 +113,10 @@ extension JoinProfileViewController { .store(in: self.cancelBag) output.isEnable - .sink { isTrue in + .sink { isEnable in self.originView.nickNameTextField.resignFirstResponder() - self.originView.finishActiveButton.isHidden = !isTrue - if isTrue { + self.originView.finishActiveButton.isHidden = !isEnable + if isEnable { self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass self.originView.duplicationCheckDescription.textColor = .donSecondary } else { @@ -125,54 +126,12 @@ extension JoinProfileViewController { } .store(in: self.cancelBag) } -} - -// MARK: - UITextFieldDelegate -extension JoinProfileViewController: UITextFieldDelegate { - func textFieldShouldReturn(_ textField: UITextField) -> Bool{ - // 키보드 내리면서 동작 - textField.resignFirstResponder() - return true - } - - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - let maxLength = 12 // 글자수 제한 - let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text - let addedText = string // 입력한 text - let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 - let newTextLength = newText.count // 합쳐진 text의 길이 - - // 글자수 제한 - if newTextLength <= maxLength { - return true - } - - let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 - let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 - let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 - - if separatedCharactersCount == 1 && !addedText.isConsonant { - return true - } else if separatedCharactersCount == 2 && addedText.isConsonant { - return true - } else if separatedCharactersCount == 3 && addedText.isConsonant { - return true - } - return false - } - - func textFieldDidChangeSelection(_ textField: UITextField) { - let text = textField.text ?? "" // textField에 수정이 반영된 후의 text - let maxLength = 12 // 글자 수 제한 - if text.count >= maxLength { - let startIndex = text.startIndex - let endIndex = text.index(startIndex, offsetBy: maxLength - 1) - let fixedText = String(text[startIndex...endIndex]) - textField.text = fixedText - self.originView.numOfLetters.text = "12/12" - } else { - self.originView.numOfLetters.text = "\(text.count)/12" - } + @objc + private func textFieldTisEmpty() { + self.originView.finishActiveButton.isHidden = true + self.originView.finishButton.isHidden = false + self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationCheckDescription + self.originView.duplicationCheckDescription.textColor = .donGray8 } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinAgreeView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinAgreeView.swift index 2d189228..cae40c2e 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinAgreeView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinAgreeView.swift @@ -32,7 +32,8 @@ final class JoinAgreeView: UIView { descriptionLabel.textColor = .donGray9 descriptionLabel.font = .font(.body2) descriptionLabel.numberOfLines = 2 - descriptionLabel.setTextWithLineHeight(text: descriptionLabel.text, lineHeight: 25.adjusted) + descriptionLabel.textAlignment = .left + descriptionLabel.setTextWithLineHeight(text: descriptionLabel.text, lineHeight: 25.adjusted, alignment: .left) return descriptionLabel }() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift index 2560083b..ac015937 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift @@ -97,6 +97,7 @@ final class JoinProfileView: UIView { setHierarchy() setLayout() + setDelegate() } @available(*, unavailable) @@ -108,7 +109,7 @@ final class JoinProfileView: UIView { // MARK: - Extensions extension JoinProfileView { - func setHierarchy() { + private func setHierarchy() { self.addSubviews(topDivisionLine, profileImage, plusButton, @@ -122,7 +123,7 @@ extension JoinProfileView { nickNameTextField.addSubview(numOfLetters) } - func setLayout() { + private func setLayout() { topDivisionLine.snp.makeConstraints { $0.leading.trailing.equalToSuperview() $0.top.equalTo(self.safeAreaLayoutGuide) @@ -179,4 +180,59 @@ extension JoinProfileView { $0.centerX.equalToSuperview() } } + + private func setDelegate() { + self.nickNameTextField.delegate = self + } +} + +// MARK: - UITextFieldDelegate + +extension JoinProfileView: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + + let maxLength = 12 // 글자수 제한 + let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text + let addedText = string // 입력한 text + let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 + let newTextLength = newText.count // 합쳐진 text의 길이 + + // 글자수 제한 + if newTextLength <= maxLength { + return true + } + + let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 + let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 + let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 + + if separatedCharactersCount == 1 && !addedText.isConsonant { + return true + } else if separatedCharactersCount == 2 && addedText.isConsonant { + return true + } else if separatedCharactersCount == 3 && addedText.isConsonant { + return true + } + return false + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" // textField에 수정이 반영된 후의 text + let maxLength = 12 // 글자 수 제한 + if text.count >= maxLength { + let startIndex = text.startIndex + let endIndex = text.index(startIndex, offsetBy: maxLength - 1) + let fixedText = String(text[startIndex...endIndex]) + textField.text = fixedText + self.numOfLetters.text = "\(maxLength)/\(maxLength)" + } else { + self.numOfLetters.text = "\(text.count)/\(maxLength)" + } + } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift index 3dc6b039..57b886a4 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift @@ -31,7 +31,7 @@ final class LoginViewController: UIViewController { title.textColor = .black title.numberOfLines = 2 title.font = .font(.head1) - title.setTextWithLineHeight(text: title.text, lineHeight: 37.adjusted) + title.setTextWithLineHeight(text: title.text, lineHeight: 37.adjusted, alignment: .left) return title }() @@ -119,12 +119,17 @@ extension LoginViewController { // 서버통신 -> 첫 로그인 유저면 여기 let viewController = JoinAgreementViewController(viewModel: JoinAgreeViewModel()) saveUserData(UserInfo(isSocialLogined: true, - isJoinedApp: false, + isJoinedApp: false, + isNotFirstUser: false, isOnboardingFinished: false, userNickname: "")) // 서버통신 -> 이미 로그인 유저면 // let viewController = OnboardingViewController() -// saveUserData(UserInfo(isExist: true, userNickname: "벼니주")) // 서버통신 후에 실제 닉네임을 넣음 (뷰모델에서) + saveUserData(UserInfo(isSocialLogined: true, + isJoinedApp: true, + isNotFirstUser: true, + isOnboardingFinished: false, + userNickname: "")) self.navigationController?.pushViewController(viewController, animated: true) } .store(in: self.cancelBag) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift index db3416f6..6a15eea9 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift @@ -105,7 +105,6 @@ extension MyPageEditProfileViewController { @objc private func textFieldTisEmpty() { - print("dsafadsfdsafdsafadsf") self.editView.postButton.setTitleColor(.donGray9, for: .normal) self.editView.postButton.backgroundColor = .donGray4 self.editView.postButton.isEnabled = false diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index 3f47ebb5..b817d168 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -20,7 +20,8 @@ final class OnboardingEndingViewController: UIViewController { private lazy var startButtonTapped = self.originView.startButton.publisher(for: .touchUpInside).map { _ in saveUserData(UserInfo(isSocialLogined: loadUserData()?.isSocialLogined ?? true, - isJoinedApp: true, + isJoinedApp: true, + isNotFirstUser: true, isOnboardingFinished: true, userNickname: loadUserData()?.userNickname ?? "")) }.eraseToAnyPublisher() @@ -56,7 +57,6 @@ final class OnboardingEndingViewController: UIViewController { super.viewDidLoad() setUI() - setDelegate() bindViewModel() } @@ -75,10 +75,6 @@ extension OnboardingEndingViewController { self.view.backgroundColor = .donGray1 } - private func setDelegate() { - self.originView.introductionView.introduction.delegate = self - } - private func bindViewModel() { let input = OnboardingEndingViewModel.Input( backButtonTapped: backButtonTapped, @@ -100,50 +96,3 @@ extension OnboardingEndingViewController { .store(in: self.cancelBag) } } - -extension OnboardingEndingViewController: UITextFieldDelegate { - func textFieldShouldReturn(_ textField: UITextField) -> Bool{ - // 키보드 내리면서 동작 - textField.resignFirstResponder() - return true - } - - func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { - let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text - let addedText = string // 입력한 text - let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 - let newTextLength = newText.count // 합쳐진 text의 길이 - - // 글자수 제한 - if newTextLength > 0 { - return true - } - - let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 - let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 - let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 - - if separatedCharactersCount == 1 && !addedText.isConsonant { - return true - } else if separatedCharactersCount == 2 && addedText.isConsonant { - return true - } else if separatedCharactersCount == 3 && addedText.isConsonant { - return true - } - return false - } - - func textFieldDidChangeSelection(_ textField: UITextField) { - let text = textField.text ?? "" // textField에 수정이 반영된 후의 text - if text.count > 0 { - self.originView.startButton.isEnabled = true - self.originView.startButton.setTitleColor(.donBlack, for: .normal) - self.originView.startButton.backgroundColor = .donPrimary - } else { - self.originView.startButton.isEnabled = false - self.originView.startButton.setTitleColor(.donGray9, for: .normal) - self.originView.startButton.backgroundColor = .donGray4 - } - } -} - diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift index e83dbe25..4be6afbb 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift @@ -139,7 +139,7 @@ extension OnboardingViewController { } } - if loadUserData()?.isSocialLogined == true { + if loadUserData()?.isNotFirstUser == true { nextButton.snp.makeConstraints { $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) $0.centerX.equalToSuperview() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift index cb58bb5b..6ee9b293 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/IntroductionView.swift @@ -43,7 +43,7 @@ final class IntroductionView: UIView { label.numberOfLines = 2 label.textColor = .donGray8 label.font = .font(.caption4) - label.setTextWithLineHeight(text: label.text, lineHeight: 17.adjusted) + label.setTextWithLineHeight(text: label.text, lineHeight: 17.adjusted, alignment: .center) return label }() diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift index 837b2dd2..9d211604 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift @@ -57,6 +57,7 @@ final class OnboardingEndingView: UIView { setHierarchy() setLayout() + setDelegate() } @available(*, unavailable) @@ -117,7 +118,7 @@ extension OnboardingEndingView { $0.top.equalTo(profileImage).offset(50.adjusted) } - if loadUserData()?.isSocialLogined == true { + if loadUserData()?.isNotFirstUser == true { startButton.snp.makeConstraints { $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) $0.centerX.equalToSuperview() @@ -138,5 +139,55 @@ extension OnboardingEndingView { } } } + + private func setDelegate() { + self.introductionView.introduction.delegate = self + } +} + +extension OnboardingEndingView: UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool{ + // 키보드 내리면서 동작 + textField.resignFirstResponder() + return true + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + let oldText = textField.text ?? "" // 입력하기 전 textField에 표시되어있던 text + let addedText = string // 입력한 text + let newText = oldText + addedText // 입력하기 전 text와 입력한 후 text를 합침 + let newTextLength = newText.count // 합쳐진 text의 길이 + + // 글자수 제한 + if newTextLength > 0 { + return true + } + + let lastWordOfOldText = String(oldText[oldText.index(before: oldText.endIndex)]) // 입력하기 전 text의 마지막 글자 + let separatedCharacters = lastWordOfOldText.decomposedStringWithCanonicalMapping.unicodeScalars.map{ String($0) } // 입력하기 전 text의 마지막 글자를 자음과 모음으로 분리 + let separatedCharactersCount = separatedCharacters.count // 분리된 자음, 모음의 개수 + + if separatedCharactersCount == 1 && !addedText.isConsonant { + return true + } else if separatedCharactersCount == 2 && addedText.isConsonant { + return true + } else if separatedCharactersCount == 3 && addedText.isConsonant { + return true + } + return false + } + + func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" // textField에 수정이 반영된 후의 text + if text.count > 0 { + self.startButton.isEnabled = true + self.startButton.setTitleColor(.donBlack, for: .normal) + self.startButton.backgroundColor = .donPrimary + } else { + self.startButton.isEnabled = false + self.startButton.setTitleColor(.donGray9, for: .normal) + self.startButton.backgroundColor = .donGray4 + } + } } From 369c89357a007d383de9b654e5cca8198c9ef6be Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 07:20:26 +0900 Subject: [PATCH 18/25] =?UTF-8?q?[Feat]=20#45=20-=20=EC=98=A8=EB=B3=B4?= =?UTF-8?q?=EB=94=A9=20=EB=B7=B0,=20=EB=B7=B0=EC=BB=A8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DontBe-iOS.xcodeproj/project.pbxproj | 4 + .../OnboardingViewController.swift | 134 ++------------- .../Onboarding/Views/OnboardingView.swift | 153 ++++++++++++++++++ 3 files changed, 171 insertions(+), 120 deletions(-) create mode 100644 DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift diff --git a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj index 7bbbeac4..1d7c35e7 100644 --- a/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj +++ b/DontBe-iOS/DontBe-iOS.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 2AF069B02B518B3F00CA3E48 /* UICollectionViewRegisterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */; }; 2AF069B22B518F8E00CA3E48 /* MyPageNicknameEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B12B518F8E00CA3E48 /* MyPageNicknameEditView.swift */; }; 2AF069B42B5194F300CA3E48 /* MyPageIntroductionEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B32B5194F300CA3E48 /* MyPageIntroductionEditView.swift */; }; + 2AF069B62B51F0D400CA3E48 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF069B52B51F0D400CA3E48 /* OnboardingView.swift */; }; 2F1741882B500C270089FC4D /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741872B500C270089FC4D /* UIApplication+.swift */; }; 2F17418A2B500CC20089FC4D /* HomeBottomsheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */; }; 2F8735402B4BE65300E55552 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F87353F2B4BE65300E55552 /* HomeView.swift */; }; @@ -138,6 +139,7 @@ 2AF069AF2B518B3F00CA3E48 /* UICollectionViewRegisterable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewRegisterable.swift; sourceTree = ""; }; 2AF069B12B518F8E00CA3E48 /* MyPageNicknameEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNicknameEditView.swift; sourceTree = ""; }; 2AF069B32B5194F300CA3E48 /* MyPageIntroductionEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageIntroductionEditView.swift; sourceTree = ""; }; + 2AF069B52B51F0D400CA3E48 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 2F1741872B500C270089FC4D /* UIApplication+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; 2F1741892B500CC20089FC4D /* HomeBottomsheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBottomsheetView.swift; sourceTree = ""; }; 2F87353F2B4BE65300E55552 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; @@ -331,6 +333,7 @@ isa = PBXGroup; children = ( 2A8D70C92B4D9787009F4C6C /* IntroductionView.swift */, + 2AF069B52B51F0D400CA3E48 /* OnboardingView.swift */, 2A8868D52B5069790017513A /* OnboardingEndingView.swift */, ); path = Views; @@ -807,6 +810,7 @@ 3C6192EF2B3A719A00220CEB /* SceneDelegate.swift in Sources */, 2AF069B42B5194F300CA3E48 /* MyPageIntroductionEditView.swift in Sources */, 2A2672052B4C3C00009D214F /* CancelBag.swift in Sources */, + 2AF069B62B51F0D400CA3E48 /* OnboardingView.swift in Sources */, 2A84465A2B4EE8B400F98F2A /* JoinProfileViewController.swift in Sources */, 2A8868DA2B506D720017513A /* NotificationViewController.swift in Sources */, 3C4993652B4F2471002A99CF /* MyPageSegmentedControl.swift in Sources */, diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift index 4be6afbb..8174f41e 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingViewController.swift @@ -18,47 +18,20 @@ final class OnboardingViewController: UIViewController { // MARK: - UI Components - private let progressImage: UIImageView = { - let progress = UIImageView() - progress.image = ImageLiterals.Onboarding.progressbar1 - return progress - }() - - private let titleImage: UIImageView = { - let title = UIImageView() - title.image = ImageLiterals.Onboarding.imgOneTitle - title.contentMode = .scaleAspectFit - return title - }() - - private let mainImage: UIImageView = { - let mainImage = UIImageView() - mainImage.image = ImageLiterals.Onboarding.imgOne - mainImage.contentMode = .scaleAspectFit - return mainImage - }() - - private let backButton: UIButton = { - let backButton = BackButton() - backButton.isHidden = true - return backButton - }() - - private let nextButton: UIButton = { - let nextButton = CustomButton(title: StringLiterals.Button.next, backColor: .donBlack, titleColor: .donWhite) - return nextButton - }() - - private let skipButton = CustomButton(title: StringLiterals.Button.skip, backColor: .clear, titleColor: .donGray7) + private let originView = OnboardingView() // MARK: - Life Cycles + override func loadView() { + super.loadView() + + view = originView + } + override func viewDidLoad() { super.viewDidLoad() setUI() - setHierarchy() - setLayout() setAddTarget() } @@ -78,96 +51,17 @@ extension OnboardingViewController { self.view.backgroundColor = .donGray1 } - private func setHierarchy() { - self.view.addSubviews(backButton, - progressImage, - titleImage, - mainImage, - nextButton, - skipButton) - } - - private func setLayout() { - backButton.snp.makeConstraints { - $0.top.equalToSuperview().inset(statusBarHeight + 38.adjusted) - $0.leading.equalToSuperview().inset(23.adjusted) - } - - progressImage.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.top.equalToSuperview().inset(statusBarHeight + 46.adjusted) - $0.width.equalTo(48.adjusted) - $0.height.equalTo(6.adjusted) - } - - if OnboardingViewController.pushCount == 0 { - titleImage.snp.makeConstraints { - $0.leading.trailing.equalToSuperview().inset(91.adjusted) - $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) - $0.height.equalTo(72.adjusted) - } - - mainImage.snp.makeConstraints { - $0.center.equalToSuperview() - $0.width.equalTo(360.adjusted) - $0.height.equalTo(340.adjusted) - } - } else if OnboardingViewController.pushCount == 1 { - titleImage.snp.makeConstraints { - $0.leading.trailing.equalToSuperview().inset(80.adjusted) - $0.top.equalToSuperview().inset(statusBarHeight + 83.adjustedH) - $0.height.equalTo(102.adjusted) - } - - mainImage.snp.makeConstraints { - $0.centerY.equalToSuperview().offset(38.adjusted) - $0.width.equalToSuperview() - $0.height.equalTo(230.adjusted) - } - } else { - titleImage.snp.makeConstraints { - $0.leading.trailing.equalToSuperview().inset(49.adjusted) - $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) - $0.height.equalTo(72.adjusted) - } - - mainImage.snp.makeConstraints { - $0.centerX.equalToSuperview() - $0.centerY.equalToSuperview().offset(31.adjusted) - $0.width.equalTo(336.adjusted) - $0.height.equalTo(238.adjusted) - } - } - - if loadUserData()?.isNotFirstUser == true { - nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - } - - skipButton.snp.makeConstraints { - $0.top.equalTo(nextButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() - } - } else { - nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.view.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() - } - } - } - private func setAddTarget() { - backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside) - skipButton.addTarget(self, action: #selector(skipButtonTapped), for: .touchUpInside) - nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside) + self.originView.backButton.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside) + self.originView.skipButton.addTarget(self, action: #selector(skipButtonTapped), for: .touchUpInside) + self.originView.nextButton.addTarget(self, action: #selector(nextButtonTapped), for: .touchUpInside) } private func setOnboardingView(viewController: OnboardingViewController) { - viewController.backButton.isHidden = false - viewController.progressImage.image = self.dummy[OnboardingViewController.pushCount].progress - viewController.titleImage.image = self.dummy[OnboardingViewController.pushCount].titleImage - viewController.mainImage.image = self.dummy[OnboardingViewController.pushCount].mainImage + viewController.originView.backButton.isHidden = false + viewController.originView.progressImage.image = self.dummy[OnboardingViewController.pushCount].progress + viewController.originView.titleImage.image = self.dummy[OnboardingViewController.pushCount].titleImage + viewController.originView.mainImage.image = self.dummy[OnboardingViewController.pushCount].mainImage } @objc diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift new file mode 100644 index 00000000..0335c661 --- /dev/null +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift @@ -0,0 +1,153 @@ +// +// OnboardingView.swift +// DontBe-iOS +// +// Created by 변희주 on 1/13/24. +// + +import UIKit + +import SnapKit + +final class OnboardingView: UIView { + + // MARK: - Properties + + // MARK: - UI Components + + let progressImage: UIImageView = { + let progress = UIImageView() + progress.image = ImageLiterals.Onboarding.progressbar1 + return progress + }() + + let titleImage: UIImageView = { + let title = UIImageView() + title.image = ImageLiterals.Onboarding.imgOneTitle + title.contentMode = .scaleAspectFit + return title + }() + + let mainImage: UIImageView = { + let mainImage = UIImageView() + mainImage.image = ImageLiterals.Onboarding.imgOne + mainImage.contentMode = .scaleAspectFit + return mainImage + }() + + let backButton: UIButton = { + let backButton = BackButton() + backButton.isHidden = true + return backButton + }() + + let nextButton: UIButton = { + let nextButton = CustomButton(title: StringLiterals.Button.next, backColor: .donBlack, titleColor: .donWhite) + return nextButton + }() + + let skipButton = CustomButton(title: StringLiterals.Button.skip, backColor: .clear, titleColor: .donGray7) + + // MARK: - Life Cycles + + override init(frame: CGRect) { + super.init(frame: frame) + + setHierarchy() + setLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extensions + +extension OnboardingView { + private func setHierarchy() { + self.addSubviews(backButton, + progressImage, + titleImage, + mainImage, + nextButton, + skipButton) + } + + private func setLayout() { + let statusBarHeight = UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .first? + .statusBarManager? + .statusBarFrame.height ?? 20 + + backButton.snp.makeConstraints { + $0.top.equalToSuperview().inset(statusBarHeight + 38.adjusted) + $0.leading.equalToSuperview().inset(23.adjusted) + } + + progressImage.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalToSuperview().inset(statusBarHeight + 46.adjusted) + $0.width.equalTo(48.adjusted) + $0.height.equalTo(6.adjusted) + } + + if OnboardingViewController.pushCount == 0 { + titleImage.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(91.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) + $0.height.equalTo(72.adjusted) + } + + mainImage.snp.makeConstraints { + $0.center.equalToSuperview() + $0.width.equalTo(360.adjusted) + $0.height.equalTo(340.adjusted) + } + } else if OnboardingViewController.pushCount == 1 { + titleImage.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(80.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 83.adjustedH) + $0.height.equalTo(102.adjusted) + } + + mainImage.snp.makeConstraints { + $0.centerY.equalToSuperview().offset(38.adjusted) + $0.width.equalToSuperview() + $0.height.equalTo(230.adjusted) + } + } else { + titleImage.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(49.adjusted) + $0.top.equalToSuperview().inset(statusBarHeight + 90.adjustedH) + $0.height.equalTo(72.adjusted) + } + + mainImage.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalToSuperview().offset(31.adjusted) + $0.width.equalTo(336.adjusted) + $0.height.equalTo(238.adjusted) + } + } + + if loadUserData()?.isNotFirstUser == true { + nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + } + + skipButton.snp.makeConstraints { + $0.top.equalTo(nextButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() + } + } else { + nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } + } + } +} From 5a551dc1d51258dda1297d919c23047dbcc660e0 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 07:38:32 +0900 Subject: [PATCH 19/25] =?UTF-8?q?[Feat]=20#45=20-=20=EC=9D=B4=EB=AF=B8=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=ED=95=9C=20=EC=9C=A0=EC=A0=80=20->=20?= =?UTF-8?q?=EC=98=A8=EB=B3=B4=EB=94=A9=20=ED=99=94=EB=A9=B4=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=84=98=EC=96=B4=EA=B0=88=20=EB=95=8C=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=8A=A4=EB=A0=88=EB=93=9C=EC=97=90=EC=84=9C=20UI=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewControllers/LoginViewController.swift | 14 ++++----- .../Onboarding/Views/OnboardingView.swift | 30 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift index 57b886a4..30ecd4be 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift @@ -123,14 +123,14 @@ extension LoginViewController { isNotFirstUser: false, isOnboardingFinished: false, userNickname: "")) - // 서버통신 -> 이미 로그인 유저면 +// 서버통신 -> 이미 로그인 유저면 // let viewController = OnboardingViewController() - saveUserData(UserInfo(isSocialLogined: true, - isJoinedApp: true, - isNotFirstUser: true, - isOnboardingFinished: false, - userNickname: "")) - self.navigationController?.pushViewController(viewController, animated: true) +// saveUserData(UserInfo(isSocialLogined: true, +// isJoinedApp: true, +// isNotFirstUser: true, +// isOnboardingFinished: false, +// userNickname: "")) +// self.navigationController?.pushViewController(viewController, animated: true) } .store(in: self.cancelBag) } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift index 0335c661..6a116987 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingView.swift @@ -133,20 +133,22 @@ extension OnboardingView { } } - if loadUserData()?.isNotFirstUser == true { - nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - } - - skipButton.snp.makeConstraints { - $0.top.equalTo(nextButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() - } - } else { - nextButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() + DispatchQueue.main.async { + if loadUserData()?.isNotFirstUser == true { + self.nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + } + + self.skipButton.snp.makeConstraints { + $0.top.equalTo(self.nextButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() + } + } else { + self.nextButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) + $0.centerX.equalToSuperview() + } } } } From 01419f0e98afb1e56fc8e9c0563cbe45927a6d48 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 08:15:15 +0900 Subject: [PATCH 20/25] =?UTF-8?q?[Feat]=20#45=20-=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=EB=B7=B0=20=EB=85=B8=ED=8B=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=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 --- .../Global/Literals/ImageLiterals.swift | 2 +- .../etc/img_dog.imageset/image_profile.png | Bin 2918 -> 0 bytes .../etc/img_dog.imageset/image_profile@2x.png | Bin 8357 -> 0 bytes .../etc/img_dog.imageset/image_profile@3x.png | Bin 16360 -> 0 bytes .../Contents.json | 6 +++--- .../image_notice_management.png | Bin 0 -> 1201 bytes .../image_notice_management@2x.png | Bin 0 -> 2237 bytes .../image_notice_management@3x.png | Bin 0 -> 3270 bytes .../ViewControllers/LoginViewController.swift | 2 +- .../Helpers/NotificationDummy.swift | 16 ++++++++-------- 10 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png delete mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png delete mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png rename DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/{img_dog.imageset => img_notice.imageset}/Contents.json (63%) create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_notice.imageset/image_notice_management.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_notice.imageset/image_notice_management@2x.png create mode 100644 DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_notice.imageset/image_notice_management@3x.png diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift index aae6d5d8..000ab935 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/ImageLiterals.swift @@ -76,7 +76,7 @@ enum ImageLiterals { } enum Notification { - static var imgDog: UIImage { .load(name: "img_dog") } + static var imgNotice: UIImage { .load(name: "img_notice") } static var imgEmpty: UIImage { .load(name: "img_empty") } } diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile.png deleted file mode 100644 index 8d1f6f1b8ed81dad581adfe6d18bb167db24fb97..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2918 zcmV-s3z_tZP)cdJ_+voKxrQz1P3~^{=(}*~tpOd6bltL^p2S$ZOWD z*}SV)ug3cH>67>E+qcMmN4&TD)UaX0k~uj!skd+6o;Y>tRN~mNV~OyaM^;{@O9PcJ12g#@KDr(1y#GFOLr&KD_Mf*RLf8BHxVV8e5@BlP0hixc~Cy zONhl{p>^xl;nSy2;ql|g;oZA;;o-xFp-r1M;rsXRx$WAu%d@_#3~=0@ok%8=sfO79 zS4QLUIH4@g&CM;EF=Ix!ckf;}ckWy;gJJUI$qs7Jph2Ny$BqGZ0r&CaM+a(7h0B*O zhl>|4hW72-hi=`vIqMxdbO=3r_6&(cfRXTkFFULJsYdhh!G>^uUN6-#KD6HYwN?4UEf3Nm}kPT zG~Gq$kHo+jIwX9?vi~NW7zGeX#B*cCWiepD0M{3|OXB_e_uh_Z@ZJ(LSV9F$tZ?Yip>W~Cg)n;b=*Xl}X=9C@GG$8g9~rd) z0as~2W3WJ3IdkSrSiE>~=-s=wGr|cQgdGh6m;{2skt0WjzJ2@pSO8;;ojZ4iRjXEc z$oWmVBAEcm7(~PX1`B<#l81wlkyW{W|NiJ7ff|TnV(*c%h5Y<{*PiJ#OrJj8 z*<3Q$g>#I?4z zHtgB6#}f;C9Mn|+BR8r6%r08AsQ8D_8=K4)aS{XLcnzxz#FdVOf5Lwwz8t6>V3U_byc2zVtCNuYTgP#7OOcC0f@dul6eSuOPT0A&mSFn<63{oO(I4P&$c zB+{X@QCk@cC($}`DlRUbp9yq61M&jx%$++o^ytyU!2pybpfd*^lkKoU+k%1ue~yEb zCQb6PLj?c;;24((aD@OW2&|(+`VcJ=31{eI6HVB?d$;EX<N=SmcFoS3FJg!N43}b&Cjs`{r~_50g4Wl(G1QNVCiD)0B+s7LGh&`1MtU<~67hBpAfbG+mkx&r6MFTh4X2VyIoH5prrO_W;Rsn5%$t<)A;m<%GuL9)OIIIg`;&TF*ArDX~BY0EtnpfH;+0%o=3l z*|TTs7AEQ|xnycmlLd#O#DR=BB@+OtugFnS+BKD94O7-20u&z6n3n($2r)&7$$sW? zo>R*B`NEqy~s9_R;QJ#y{%>FiR5}4GX_>a$%HmAMKVYRIyC#>jLI0&VbRS*A(BWa+6gFR;(iSeFvNhSg^S=Z-vzkdCEx|U6`t|+7wF_NDC>U6GYSy_%FuqbdmV+`zMJWtt*E8$sh z?3MEZHduU=25YJhM5+Yboc>#z*uUhzXDaxEe1yq{n8FM{R;U0iQP;JmU5%L-rE#f6viB7e0LaTh0A`jJEPR3yH!-o&MY@~aIUdg4s0c>zjBymg& z5!yi%&X^P1>wqpNePRjZ>H6)M(ndTZk;N%LedIKj0iZ{tgvH*MFm$aBv>@FmehuW zbf*=VzQ6R*rx4SJ5~UfAtM%z;8yiV4{sL;@O4`c4y)lx{AlklxEsXG+6T^VyIpIa4 z0MO1JjA+fO=}@a6MML#gngu+4xa#x8-fg$B{x5t#?3CC?T8WjZdDb|Fhjb=LO)M|%hP+e6ftBz`sq0%Jg)JKzswM$yRl1yxW+jg{BB4&f0XZM)I!E4xn zO$MTj=?ju~vuDrtzECsQ7n*R=6G75o6j8P<-XvQOH5<{$*m`yk+S%e&bR0ig3HoVASR`2?9Ws{8ltQ$1F!lr(w z*8nMVrohIlcgy^jJI-6SY>Ao2l?E`T?M3ZB0fNt980I<8U=|H)^@S5?oi=PfFjX?4 zDB)C9SRL6}pbT#`P!8LRip+S4F|b-wD}c}+p!IE~xjJ1|94y;GNW%Uj;*p@QWNrpQY*3jfnRQoH~7CIuxlYIGbZ8)A8^!7!pA*^(HioARcVp(rJZR-B)y}Tu;eRU zvt~`AA@&>n?;B${&zLB(PwRQ6;Yi!ufaS`8hGCj4mkt4L?4}G{xv^hi8@!1|m~A}J z%wyNCU2y{#H$Z5Sx3$og@T4N4X*ja7=NNdo)Ykofd0)mR{q3Iq{ QBLDyZ07*qoM6N<$g6W!BYybcN diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@2x.png deleted file mode 100644 index cbee364bf02df8d41a05229b1e585af9caa36c8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8357 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 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_dog.imageset/image_profile@3x.png deleted file mode 100644 index 700cade6e60a1bba267e035e0a72126380bcd33b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16360 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 LNkvXXu0mjf?|w4Q literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_notice.imageset/image_notice_management@3x.png b/DontBe-iOS/DontBe-iOS/Global/Resources/Assets.xcassets/etc/img_notice.imageset/image_notice_management@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..79592268388f42ebd38c601aaba9b37537124687 GIT binary patch literal 3270 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 Ef;{y1-v9sr literal 0 HcmV?d00001 diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift index 30ecd4be..746b4cc5 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Login/ViewControllers/LoginViewController.swift @@ -130,7 +130,7 @@ extension LoginViewController { // isNotFirstUser: true, // isOnboardingFinished: false, // userNickname: "")) -// self.navigationController?.pushViewController(viewController, animated: true) + self.navigationController?.pushViewController(viewController, animated: true) } .store(in: self.cancelBag) } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift index 51ce71f6..e162f753 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Notification/Helpers/NotificationDummy.swift @@ -16,35 +16,35 @@ struct NotificationDummy { extension NotificationDummy { static func dummy() -> [NotificationDummy] { - return [NotificationDummy(profile: ImageLiterals.Notification.imgDog, + return [NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.likeContent, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.writeComment, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.likeComment, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.welcome, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.transparency, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.violation, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.contentTransparency, minutes: "12분전"), - NotificationDummy(profile: ImageLiterals.Notification.imgDog, + NotificationDummy(profile: ImageLiterals.Notification.imgNotice, userName: "벼니주", description: StringLiterals.Notification.commentTransparency, minutes: "12분전")] From f603861d1e28f86069e3bfac1d81684a6b95bcba Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 08:24:39 +0900 Subject: [PATCH 21/25] =?UTF-8?q?[Feat]=20#45=20-=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EC=88=98=EC=A0=95=20=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Join/Views/JoinProfileView.swift | 23 ++++++++++--------- .../MyPage/Views/MyPageNicknameEditView.swift | 23 ++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift index ac015937..15957e15 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift @@ -23,11 +23,12 @@ final class JoinProfileView: UIView { return profileImage }() - let plusButton: UIButton = { - let plusButton = UIButton() - plusButton.setImage(ImageLiterals.Join.btnPlus, for: .normal) - return plusButton - }() + // 2차 스프린트 +// let plusButton: UIButton = { +// let plusButton = UIButton() +// plusButton.setImage(ImageLiterals.Join.btnPlus, for: .normal) +// return plusButton +// }() private let nickNameLabel: UILabel = { let nickNameLabel = UILabel() @@ -112,7 +113,7 @@ extension JoinProfileView { private func setHierarchy() { self.addSubviews(topDivisionLine, profileImage, - plusButton, +// plusButton, nickNameLabel, nickNameTextField, duplicationCheckButton, @@ -136,11 +137,11 @@ extension JoinProfileView { $0.size.equalTo(100.adjusted) } - plusButton.snp.makeConstraints { - $0.top.equalTo(profileImage).offset(72.adjusted) - $0.leading.equalTo(profileImage).offset(78.adjusted) - $0.size.equalTo(34.adjusted) - } +// plusButton.snp.makeConstraints { +// $0.top.equalTo(profileImage).offset(72.adjusted) +// $0.leading.equalTo(profileImage).offset(78.adjusted) +// $0.size.equalTo(34.adjusted) +// } nickNameLabel.snp.makeConstraints { $0.top.equalTo(self.safeAreaLayoutGuide).inset(171.adjustedH) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift index 384698cf..a6da110f 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift @@ -23,11 +23,12 @@ final class MyPageNicknameEditView: UIView { return profileImage }() - let plusButton: UIButton = { - let plusButton = UIButton() - plusButton.setImage(ImageLiterals.Join.btnPlus, for: .normal) - return plusButton - }() + // 2차 스프린트 +// let plusButton: UIButton = { +// let plusButton = UIButton() +// plusButton.setImage(ImageLiterals.Join.btnPlus, for: .normal) +// return plusButton +// }() private let nickNameLabel: UILabel = { let nickNameLabel = UILabel() @@ -100,7 +101,7 @@ extension MyPageNicknameEditView { func setHierarchy() { self.addSubviews(topDivisionLine, profileImage, - plusButton, +// plusButton, nickNameLabel, nickNameTextField, duplicationCheckButton, @@ -122,11 +123,11 @@ extension MyPageNicknameEditView { $0.size.equalTo(100.adjusted) } - plusButton.snp.makeConstraints { - $0.top.equalTo(profileImage).offset(72.adjusted) - $0.leading.equalTo(profileImage).offset(78.adjusted) - $0.size.equalTo(34.adjusted) - } +// plusButton.snp.makeConstraints { +// $0.top.equalTo(profileImage).offset(72.adjusted) +// $0.leading.equalTo(profileImage).offset(78.adjusted) +// $0.size.equalTo(34.adjusted) +// } nickNameLabel.snp.makeConstraints { $0.top.equalTo(self.safeAreaLayoutGuide).inset(171.adjustedH) From 091747d70e7275affa1ec460c9cd8df9e1e5009a Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 08:39:35 +0900 Subject: [PATCH 22/25] =?UTF-8?q?[Feat]=20#45=20-=20=EC=98=A8=EB=B3=B4?= =?UTF-8?q?=EB=94=A9=20=EC=88=98=EC=A0=95=EC=82=AC=ED=95=AD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Global/Literals/StringLiterals.swift | 1 + .../OnboardingEndingViewController.swift | 2 +- .../Views/OnboardingEndingView.swift | 54 ++++++++----------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift index 452ede8f..df2faae4 100644 --- a/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift +++ b/DontBe-iOS/DontBe-iOS/Global/Literals/StringLiterals.swift @@ -55,6 +55,7 @@ enum StringLiterals { static let skip = "건너뛰기" static let next = "다음으로" static let start = "시작하기" + static let later = "한 줄 소개 나중에 작성하기" static let finish = "완료하기" static let editFinish = "수정완료" } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift index b817d168..6a531e5f 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/ViewControllers/OnboardingEndingViewController.swift @@ -25,7 +25,7 @@ final class OnboardingEndingViewController: UIViewController { isOnboardingFinished: true, userNickname: loadUserData()?.userNickname ?? "")) }.eraseToAnyPublisher() - private lazy var skipButtonTapped = self.originView.skipButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() + private lazy var skipButtonTapped = self.originView.laterButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() private lazy var backButtonTapped = self.originView.backButton.publisher(for: .touchUpInside).map { _ in }.eraseToAnyPublisher() // MARK: - UI Components diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift index 9d211604..953a1b32 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Onboarding/Views/OnboardingEndingView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class OnboardingEndingView: UIView { - + // MARK: - Properties // MARK: - UI Components @@ -32,7 +32,7 @@ final class OnboardingEndingView: UIView { profile.image = ImageLiterals.Common.imgProfile return profile }() - + let introductionView = IntroductionView() let backButton = BackButton() @@ -48,7 +48,7 @@ final class OnboardingEndingView: UIView { return startButton }() - let skipButton = CustomButton(title: StringLiterals.Button.skip, backColor: .clear, titleColor: .donGray7) + let laterButton = CustomButton(title: StringLiterals.Button.later, backColor: .clear, titleColor: .donGray7) // MARK: - Life Cycles @@ -71,21 +71,21 @@ final class OnboardingEndingView: UIView { extension OnboardingEndingView { private func setHierarchy() { self.addSubviews(backButton, - progressImage, - titleImage, - profileImage, - introductionView, - startButton, - skipButton) + progressImage, + titleImage, + profileImage, + introductionView, + startButton, + laterButton) self.bringSubviewToFront(profileImage) } private func setLayout() { let statusBarHeight = UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first? - .statusBarManager? - .statusBarFrame.height ?? 20 + .compactMap { $0 as? UIWindowScene } + .first? + .statusBarManager? + .statusBarFrame.height ?? 20 backButton.snp.makeConstraints { $0.top.equalToSuperview().inset(statusBarHeight + 38.adjusted) @@ -118,25 +118,17 @@ extension OnboardingEndingView { $0.top.equalTo(profileImage).offset(50.adjusted) } - if loadUserData()?.isNotFirstUser == true { - startButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) - $0.centerX.equalToSuperview() - $0.width.equalTo(342.adjusted) - $0.height.equalTo(50.adjusted) - } + startButton.snp.makeConstraints { + $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(91.adjusted) + $0.centerX.equalToSuperview() + $0.width.equalTo(342.adjusted) + $0.height.equalTo(50.adjusted) + } + + laterButton.snp.makeConstraints { + $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) + $0.centerX.equalToSuperview() - skipButton.snp.makeConstraints { - $0.top.equalTo(startButton.snp.bottom).offset(12.adjusted) - $0.centerX.equalToSuperview() - } - } else { - startButton.snp.makeConstraints { - $0.bottom.equalTo(self.safeAreaLayoutGuide).inset(29.adjusted) - $0.centerX.equalToSuperview() - $0.width.equalTo(342.adjusted) - $0.height.equalTo(50.adjusted) - } } } From d4ef91184a29306205b881c8f132fc58302b8cb6 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 08:55:24 +0900 Subject: [PATCH 23/25] =?UTF-8?q?[Feat]=20#45=20-=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20post=20=EB=B2=84=ED=8A=BC=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyPageEditProfileViewController.swift | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift index 6a15eea9..97294b1b 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/ViewControllers/MyPageEditProfileViewController.swift @@ -17,8 +17,8 @@ final class MyPageEditProfileViewController: UIViewController { // MARK: - UI Components - let originView = MyPageNicknameEditView() - let editView = MyPageIntroductionEditView() + let nicknameEditView = MyPageNicknameEditView() + let introductionEditView = MyPageIntroductionEditView() override func touchesBegan(_ touches: Set, with event: UIEvent?) { view.endEditing(true) @@ -49,19 +49,19 @@ extension MyPageEditProfileViewController { } private func setHierarchy() { - self.view.addSubviews(originView, - editView) + self.view.addSubviews(nicknameEditView, + introductionEditView) } private func setLayout() { - originView.snp.makeConstraints { + nicknameEditView.snp.makeConstraints { $0.top.equalTo(self.view.safeAreaLayoutGuide) $0.leading.trailing.equalToSuperview() $0.bottom.equalToSuperview().inset(323.adjusted) } - editView.snp.makeConstraints { - $0.top.equalTo(originView.duplicationCheckDescription.snp.bottom).offset(16.adjustedH) + introductionEditView.snp.makeConstraints { + $0.top.equalTo(nicknameEditView.duplicationCheckDescription.snp.bottom).offset(16.adjustedH) $0.leading.trailing.bottom.equalToSuperview() $0.bottom.equalTo(self.view.safeAreaLayoutGuide) } @@ -73,7 +73,8 @@ extension MyPageEditProfileViewController { NotificationCenter.default.addObserver(self, selector: #selector(textFieldTisEmpty), name: UITextField.textDidChangeNotification, object: nil) - originView.duplicationCheckButton.addTarget(self, action: #selector(duplicationCheckButtonTapped), for: .touchUpInside) + nicknameEditView.duplicationCheckButton.addTarget(self, action: #selector(duplicationCheckButtonTapped), for: .touchUpInside) + introductionEditView.postButton.addTarget(self, action: #selector(postButtonTapped), for: .touchUpInside) } @objc @@ -84,29 +85,35 @@ extension MyPageEditProfileViewController { @objc private func duplicationCheckButtonTapped() { // 중복확인 서버통신에 성공 - self.originView.nickNameTextField.resignFirstResponder() - self.editView.postButton.isEnabled = !isTrue + self.nicknameEditView.nickNameTextField.resignFirstResponder() + self.introductionEditView.postButton.isEnabled = !isTrue // 중복확인 -> 성공 (서버통신으로 isTrue 값 변경해주어야함) if isTrue { - self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass - self.editView.postButton.setTitleColor(.donWhite, for: .normal) - self.editView.postButton.backgroundColor = .donBlack - self.originView.duplicationCheckDescription.textColor = .donSecondary + self.nicknameEditView.duplicationCheckDescription.text = StringLiterals.Join.duplicationPass + self.introductionEditView.postButton.setTitleColor(.donWhite, for: .normal) + self.introductionEditView.postButton.backgroundColor = .donBlack + self.nicknameEditView.duplicationCheckDescription.textColor = .donSecondary } // 중복확인 -> 실패 else { - self.originView.duplicationCheckDescription.text = StringLiterals.Join.duplicationNotPass - self.editView.postButton.setTitleColor(.donGray9, for: .normal) - self.editView.postButton.backgroundColor = .donGray4 - self.originView.duplicationCheckDescription.textColor = .donError + self.nicknameEditView.duplicationCheckDescription.text = StringLiterals.Join.duplicationNotPass + self.introductionEditView.postButton.setTitleColor(.donGray9, for: .normal) + self.introductionEditView.postButton.backgroundColor = .donGray4 + self.nicknameEditView.duplicationCheckDescription.textColor = .donError } } + @objc + private func postButtonTapped() { + // 서버통신 -> POST + self.navigationController?.popViewController(animated: true) + } + @objc private func textFieldTisEmpty() { - self.editView.postButton.setTitleColor(.donGray9, for: .normal) - self.editView.postButton.backgroundColor = .donGray4 - self.editView.postButton.isEnabled = false + self.introductionEditView.postButton.setTitleColor(.donGray9, for: .normal) + self.introductionEditView.postButton.backgroundColor = .donGray4 + self.introductionEditView.postButton.isEnabled = false } } From 4832017973fcbf8f96d4e827f32f79c5e85778ab Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 17:52:21 +0900 Subject: [PATCH 24/25] =?UTF-8?q?[Feat]=20#45=20-=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=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 --- .../Presentation/Join/Views/JoinProfileView.swift | 4 ++-- .../MyPage/Views/MyPageIntroductionEditView.swift | 9 +++++---- .../MyPage/Views/MyPageNicknameEditView.swift | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift index 15957e15..3f172228 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/Join/Views/JoinProfileView.swift @@ -231,9 +231,9 @@ extension JoinProfileView: UITextFieldDelegate { let endIndex = text.index(startIndex, offsetBy: maxLength - 1) let fixedText = String(text[startIndex...endIndex]) textField.text = fixedText - self.numOfLetters.text = "\(maxLength)/\(maxLength)" + self.numOfLetters.text = "(\(maxLength)/\(maxLength))" } else { - self.numOfLetters.text = "\(text.count)/\(maxLength)" + self.numOfLetters.text = "(\(text.count)/\(maxLength))" } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift index 342e6a68..464fdefa 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift @@ -14,7 +14,6 @@ final class MyPageIntroductionEditView: UIView { // MARK: - Properties let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .heavy) // 햅틱 기능 - let maxLength = 50 // 최대 글자 수 // MARK: - UI Components @@ -44,7 +43,7 @@ final class MyPageIntroductionEditView: UIView { private let numOfLetters: UILabel = { let numOfLetters = UILabel() - numOfLetters.text = "0/50" + numOfLetters.text = "(0/50)" numOfLetters.textColor = .donGray7 numOfLetters.font = .font(.caption4) return numOfLetters @@ -119,6 +118,8 @@ extension MyPageIntroductionEditView { extension MyPageIntroductionEditView: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { + let maxLength = 50 // 최대 글자 수 + let textLength = contentTextView.text.count textView.text = String(textView.text.prefix(maxLength)) @@ -131,9 +132,9 @@ extension MyPageIntroductionEditView: UITextViewDelegate { postButton.backgroundColor = .donGray4 postButton.isEnabled = false } else if textLength < 50 { - self.numOfLetters.text = "\(textLength)/50" + self.numOfLetters.text = "(\(textLength)/\(maxLength)" } else { - self.numOfLetters.text = "50/50" + self.numOfLetters.text = "(\(maxLength)/\(maxLength)" } } } diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift index a6da110f..d1549437 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageNicknameEditView.swift @@ -211,9 +211,9 @@ extension MyPageNicknameEditView: UITextFieldDelegate { let endIndex = text.index(startIndex, offsetBy: maxLength - 1) let fixedText = String(text[startIndex...endIndex]) textField.text = fixedText - self.numOfLetters.text = "\(maxLength)/\(maxLength)" + self.numOfLetters.text = "(\(maxLength)/\(maxLength))" } else { - self.numOfLetters.text = "\(text.count)/\(maxLength)" + self.numOfLetters.text = "(\(text.count)/\(maxLength))" } } } From 1b5c0f420680ce29eacdff05e2ae63c58ae886e1 Mon Sep 17 00:00:00 2001 From: heejoo Date: Sat, 13 Jan 2024 18:29:54 +0900 Subject: [PATCH 25/25] =?UTF-8?q?[Merge]=20#45=20-=20=EB=A7=88=EC=9D=B4?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=B8=EC=A7=91=20=EB=B7=B0=20UI=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B0=8F=20=EC=84=B8=EB=B6=80=20=EC=82=AC=ED=95=AD=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 --- .../MyPage/Views/MyPageIntroductionEditView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift index 464fdefa..a6f88009 100644 --- a/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift +++ b/DontBe-iOS/DontBe-iOS/Presentation/MyPage/Views/MyPageIntroductionEditView.swift @@ -132,9 +132,9 @@ extension MyPageIntroductionEditView: UITextViewDelegate { postButton.backgroundColor = .donGray4 postButton.isEnabled = false } else if textLength < 50 { - self.numOfLetters.text = "(\(textLength)/\(maxLength)" + self.numOfLetters.text = "(\(textLength)/\(maxLength))" } else { - self.numOfLetters.text = "(\(maxLength)/\(maxLength)" + self.numOfLetters.text = "(\(maxLength)/\(maxLength))" } } }