From 626c285140a09f56166ae1fe4036b429f9c8e0c5 Mon Sep 17 00:00:00 2001 From: Joon Baek Date: Sat, 2 Sep 2023 22:30:28 +0900 Subject: [PATCH] =?UTF-8?q?[Fix]=20#214=20-=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pophory-iOS.xcodeproj/project.pbxproj | 21 ++- .../Components/Base/BaseInputView.swift | 41 ++--- .../PophoryTextFieldCustomizable.swift | 40 +++++ .../Global/Extensions/UILabel+Extension.swift | 19 +++ .../Protocols/TextFieldManagerDelegate.swift | 18 +++ .../Global/Resources/SceneDelegate.swift | 8 +- .../Global/Utilities/TextFieldManager.swift | 153 ++++++++++++++++++ .../AlbumDetailViewController.swift | 1 + .../PhotoCollectionViewDataSource.swift | 3 + .../View/PhotoCollectionViewCell.swift | 19 ++- .../NameInputViewController.swift | 4 + .../Screen/Auth/Views/IDInputView.swift | 49 +++--- .../Screen/Auth/Views/NameInputView.swift | 121 +++----------- .../Auth/Views/PickAlbumCoverView.swift | 4 +- .../Screen/Auth/Views/StartPophoryView.swift | 2 +- 15 files changed, 342 insertions(+), 161 deletions(-) create mode 100644 pophory-iOS/Global/Components/PophoryTextFieldCustomizable.swift create mode 100644 pophory-iOS/Global/Protocols/TextFieldManagerDelegate.swift create mode 100644 pophory-iOS/Global/Utilities/TextFieldManager.swift diff --git a/pophory-iOS.xcodeproj/project.pbxproj b/pophory-iOS.xcodeproj/project.pbxproj index 65dd3550..1118c4e4 100644 --- a/pophory-iOS.xcodeproj/project.pbxproj +++ b/pophory-iOS.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 3703F0F72A60A9BE0017A381 /* SignUpIndicatorCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3703F0F62A60A9BE0017A381 /* SignUpIndicatorCollectionViewCell.swift */; }; 371A7C5D2A5120CA0095C744 /* (null) in Sources */ = {isa = PBXBuildFile; }; 371A7C632A5133C80095C744 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371A7C622A5133C80095C744 /* UIViewController+Extension.swift */; }; + 371E940A2A837CED0098280F /* TextFieldManagerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371E94092A837CED0098280F /* TextFieldManagerDelegate.swift */; }; 372F7F362A540D270032B7BC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F7F352A540D270032B7BC /* OnboardingView.swift */; }; 372F7F382A5446390032B7BC /* KeyboardFollowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372F7F372A5446390032B7BC /* KeyboardFollowable.swift */; }; 37310F142A5535B20046E777 /* PickAlbumButtonCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37310F132A5535B20046E777 /* PickAlbumButtonCollectionViewCell.swift */; }; @@ -32,12 +33,15 @@ 3780FBCC2A6187710002A0FD /* PostIsDuplicatedDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3780FBCB2A6187710002A0FD /* PostIsDuplicatedDTO.swift */; }; 3780FBCE2A6265E20002A0FD /* LoginAPIDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3780FBCD2A6265E20002A0FD /* LoginAPIDTO.swift */; }; 3780FBD02A6274390002A0FD /* PophoryTokenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3780FBCF2A6274390002A0FD /* PophoryTokenManager.swift */; }; - 3782F4322A51E6D200FE3846 /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782F4312A51E6D200FE3846 /* KeyboardManager.swift */; }; 3782F4382A528BAF00FE3846 /* StartPophoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782F4372A528BAF00FE3846 /* StartPophoryViewController.swift */; }; 3782F4402A52C6EB00FE3846 /* BaseInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782F43F2A52C6EB00FE3846 /* BaseInputView.swift */; }; 3782F4422A52CD5A00FE3846 /* StartPophoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782F4412A52CD5A00FE3846 /* StartPophoryView.swift */; }; 3782F4462A52F4BF00FE3846 /* OnboardingContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3782F4452A52F4BF00FE3846 /* OnboardingContentCollectionViewCell.swift */; }; 37ABA3472A7FA08400201A39 /* AsyncMoyaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37ABA3462A7FA08400201A39 /* AsyncMoyaProvider.swift */; }; + 37CC62062A6D7B6E003B00B0 /* PophoryTextFieldCustomizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC62052A6D7B6E003B00B0 /* PophoryTextFieldCustomizable.swift */; }; + 37CC62082A6D9E43003B00B0 /* TextFieldManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC62072A6D9E43003B00B0 /* TextFieldManager.swift */; }; + 37CC620A2A6E5C14003B00B0 /* PophoryHeaderLabelCustomizable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC62092A6E5C14003B00B0 /* PophoryHeaderLabelCustomizable.swift */; }; + 37DCA6832A4A97AF00FF8F90 /* Moya in Frameworks */ = {isa = PBXBuildFile; productRef = 37DCA6822A4A97AF00FF8F90 /* Moya */; }; 37DCA6862A4A97CA00FF8F90 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 37DCA6852A4A97CA00FF8F90 /* SnapKit */; }; 37DCA69A2A4AB6C900FF8F90 /* HomeAlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA6992A4AB6C900FF8F90 /* HomeAlbumViewController.swift */; }; 37DCA69C2A4AB6D300FF8F90 /* LeaveServiceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DCA69B2A4AB6D300FF8F90 /* LeaveServiceViewController.swift */; }; @@ -196,6 +200,7 @@ 3703F0F42A6071B50017A381 /* PostIdentityTokenDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostIdentityTokenDTO.swift; sourceTree = ""; }; 3703F0F62A60A9BE0017A381 /* SignUpIndicatorCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpIndicatorCollectionViewCell.swift; sourceTree = ""; }; 371A7C622A5133C80095C744 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; + 371E94092A837CED0098280F /* TextFieldManagerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldManagerDelegate.swift; sourceTree = ""; }; 372F7F352A540D270032B7BC /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 372F7F372A5446390032B7BC /* KeyboardFollowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardFollowable.swift; sourceTree = ""; }; 37310F132A5535B20046E777 /* PickAlbumButtonCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PickAlbumButtonCollectionViewCell.swift; sourceTree = ""; }; @@ -218,12 +223,14 @@ 3780FBCB2A6187710002A0FD /* PostIsDuplicatedDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostIsDuplicatedDTO.swift; sourceTree = ""; }; 3780FBCD2A6265E20002A0FD /* LoginAPIDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginAPIDTO.swift; sourceTree = ""; }; 3780FBCF2A6274390002A0FD /* PophoryTokenManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PophoryTokenManager.swift; sourceTree = ""; }; - 3782F4312A51E6D200FE3846 /* KeyboardManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = ""; }; 3782F4372A528BAF00FE3846 /* StartPophoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartPophoryViewController.swift; sourceTree = ""; }; 3782F43F2A52C6EB00FE3846 /* BaseInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseInputView.swift; sourceTree = ""; }; 3782F4412A52CD5A00FE3846 /* StartPophoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartPophoryView.swift; sourceTree = ""; }; 3782F4452A52F4BF00FE3846 /* OnboardingContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingContentCollectionViewCell.swift; sourceTree = ""; }; 37ABA3462A7FA08400201A39 /* AsyncMoyaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncMoyaProvider.swift; sourceTree = ""; }; + 37CC62052A6D7B6E003B00B0 /* PophoryTextFieldCustomizable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PophoryTextFieldCustomizable.swift; sourceTree = ""; }; + 37CC62072A6D9E43003B00B0 /* TextFieldManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldManager.swift; sourceTree = ""; }; + 37CC62092A6E5C14003B00B0 /* PophoryHeaderLabelCustomizable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PophoryHeaderLabelCustomizable.swift; sourceTree = ""; }; 37DCA6992A4AB6C900FF8F90 /* HomeAlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeAlbumViewController.swift; sourceTree = ""; }; 37DCA69B2A4AB6D300FF8F90 /* LeaveServiceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeaveServiceViewController.swift; sourceTree = ""; }; 37DCA69D2A4AB6DC00FF8F90 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; @@ -345,7 +352,6 @@ B1631F582A175F470050974F /* Notification.Name+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification.Name+Extension.swift"; sourceTree = ""; }; B1631F5A2A175F850050974F /* UITextField+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Extension.swift"; sourceTree = ""; }; B1631F5C2A175F9C0050974F /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; - B1631F652A1760180050974F /* .keep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .keep; sourceTree = ""; }; B1B7E70C2A680F5700CFE64C /* PhotoUrlResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoUrlResponseDto.swift; sourceTree = ""; }; B1D0297F2A5D907F000B5B53 /* UserDefault+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefault+Extension.swift"; sourceTree = ""; }; B1D029822A5DA7AF000B5B53 /* MyPageNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNetworkManager.swift; sourceTree = ""; }; @@ -407,6 +413,7 @@ 91F3A7572A51760B00C06D1B /* Navigatable.swift */, 372F7F372A5446390032B7BC /* KeyboardFollowable.swift */, 37DEF38F2A822561000068A1 /* NextButtonDelegate.swift */, + 371E94092A837CED0098280F /* TextFieldManagerDelegate.swift */, ); path = Protocols; sourceTree = ""; @@ -1044,11 +1051,10 @@ B1631F642A1760050050974F /* Utilities */ = { isa = PBXGroup; children = ( - B1631F652A1760180050974F /* .keep */, - 3782F4312A51E6D200FE3846 /* KeyboardManager.swift */, 6BB37C722A55EB7400E49B12 /* DateManager.swift */, 9127801D2A6867CA0005BBA3 /* NoNetworkView.swift */, 9127801F2A68698F0005BBA3 /* NetworkMonitor.swift */, + 37CC62072A6D9E43003B00B0 /* TextFieldManager.swift */, ); path = Utilities; sourceTree = ""; @@ -1066,6 +1072,8 @@ B1F310E82A5EC91200BF989B /* PophoryWebViewController.swift */, 912780112A67D0900005BBA3 /* CarouselCollectionViewFlowLayout.swift */, 37E1FC7D2A68066D009AF774 /* PophoryErrorViewController.swift */, + 37CC62052A6D7B6E003B00B0 /* PophoryTextFieldCustomizable.swift */, + 37CC62092A6E5C14003B00B0 /* PophoryHeaderLabelCustomizable.swift */, ); path = Components; sourceTree = ""; @@ -1289,9 +1297,11 @@ B1631F502A175E840050974F /* String+Extension.swift in Sources */, 37DCA6A22A4AB6EF00FF8F90 /* TabBarViewController.swift in Sources */, 6BB37C672A558A8B00E49B12 /* CustomModalTransitionDelegate.swift in Sources */, + 371E940A2A837CED0098280F /* TextFieldManagerDelegate.swift in Sources */, 91F3A7A62A52D57700C06D1B /* DefaultStudioRepository.swift in Sources */, 91F3A7602A517E9E00C06D1B /* ImageLiterals.swift in Sources */, 37DEF3902A822561000068A1 /* NextButtonDelegate.swift in Sources */, + 37CC620A2A6E5C14003B00B0 /* PophoryHeaderLabelCustomizable.swift in Sources */, 6BCE475A2A684D540060EC78 /* PatchSharePhotoRequestDTO.swift in Sources */, B1631F0F2A1759EA0050974F /* ViewController.swift in Sources */, 91F3A78C2A52B21000C06D1B /* StudiosAPI.swift in Sources */, @@ -1400,6 +1410,7 @@ 37DCA6AC2A4ABAE200FF8F90 /* ColorLiterals.swift in Sources */, 9161CAE62A546DCD002B8B77 /* PhotoCollectionViewDataSource.swift in Sources */, 91F3A77B2A52AC8000C06D1B /* PatchAlbumPhotoListResponseDTO.swift in Sources */, + 37CC62082A6D9E43003B00B0 /* TextFieldManager.swift in Sources */, 91F3A7622A5206B100C06D1B /* HomeAlbumView.swift in Sources */, 372F7F382A5446390032B7BC /* KeyboardFollowable.swift in Sources */, B1F310F32A62E6C000BF989B /* SharePhotoRootView.swift in Sources */, diff --git a/pophory-iOS/Global/Components/Base/BaseInputView.swift b/pophory-iOS/Global/Components/Base/BaseInputView.swift index fa5a072b..c7fd9e66 100644 --- a/pophory-iOS/Global/Components/Base/BaseInputView.swift +++ b/pophory-iOS/Global/Components/Base/BaseInputView.swift @@ -17,16 +17,7 @@ class BaseSignUpView: UIView { // MARK: - UI Properties - lazy var headerLabel: UILabel = { - let label = UILabel() - label.text = "포포리에서 사용할\n너의 닉네임을 알려줘!" - label.textColor = .black - label.font = .head1Medium - label.numberOfLines = 0 - label.setTextWithLineHeight(lineHeight: 34) - label.applyColorAndBoldText(targetString: "너의 닉네임", color: .pophoryPurple, font: .head1Medium, boldFont: .head1Bold) - return label - }() + lazy var headerLabel: UILabel = createHeaderLabel(text: "포포리에서 사용할\n너의 닉네임을 알려줘!", lineHeight: 34, font: .head1Medium, targetString: "너의 닉네임", color: .pophoryPurple, boldFont: .head1Bold) lazy var nextButton: PophoryButton = { let buttonBuilder = PophoryButtonBuilder() @@ -83,31 +74,31 @@ extension BaseSignUpView { private func setupViews() { addSubviews([headerLabel, indicatorStackView, nextButton]) - - headerLabel.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide).offset(convertByHeightRatio(32)) - $0.leading.equalToSuperview().offset(convertByWidthRatio(20)) + + headerLabel.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide).offset(convertByHeightRatio(32)) + make.leading.equalToSuperview().offset(convertByWidthRatio(20)) } - indicatorStackView.snp.makeConstraints { - $0.top.equalTo(safeAreaLayoutGuide) - $0.leading.trailing.equalToSuperview().inset(convertByWidthRatio(6)) - $0.height.equalTo(convertByHeightRatio(3)) + indicatorStackView.snp.makeConstraints { make in + make.top.equalTo(safeAreaLayoutGuide) + make.left.right.equalToSuperview().inset(convertByWidthRatio(6)) + make.height.equalTo(convertByHeightRatio(3)) } - nextButton.snp.makeConstraints { - $0.centerX.equalToSuperview() + nextButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() if #available(iOS 15.0, *) { - $0.bottom.equalTo(keyboardLayoutGuide.snp.top).offset(-10) + make.bottom.equalTo(keyboardLayoutGuide.snp.top).offset(-10) } } } - + func setupLayoutForAlbumCoverView(_ subView: UIView, topOffset: CGFloat) { addSubview(subView) - subView.snp.makeConstraints { - $0.top.equalTo(headerLabel.snp.bottom).offset(topOffset) - $0.centerX.equalToSuperview() + subView.snp.makeConstraints { make in + make.top.equalTo(headerLabel.snp.bottom).offset(topOffset) + make.centerX.equalToSuperview() } } diff --git a/pophory-iOS/Global/Components/PophoryTextFieldCustomizable.swift b/pophory-iOS/Global/Components/PophoryTextFieldCustomizable.swift new file mode 100644 index 00000000..f6990c86 --- /dev/null +++ b/pophory-iOS/Global/Components/PophoryTextFieldCustomizable.swift @@ -0,0 +1,40 @@ +// +// PophoryTextFieldCustomizable.swift +// pophory-iOS +// +// Created by Joon Baek on 2023/07/24. +// + +import UIKit + +protocol PophoryTextFieldCustomizable: AnyObject { + func createInputTextField(placeholder: String, textFieldManager: TextFieldManager) -> UITextField + func createClearTextFieldButton(textFieldManager: TextFieldManager) -> UIButton +} + +extension PophoryTextFieldCustomizable { + func createInputTextField(placeholder: String, textFieldManager: TextFieldManager) -> UITextField { + let textField = UITextField() + textField.placeholder = placeholder + textField.textColor = .black + textField.backgroundColor = .pophoryGray100 + textField.font = .popupButton + textField.layer.borderColor = UIColor.pophoryGray400.cgColor + textField.layer.borderWidth = 1 + textField.makeRounded(radius: 18) + textField.addPadding(left: 15) + textField.addTarget(textFieldManager, action: #selector(textFieldManager.handleTextFieldEditingChanged), for: .editingChanged) + textField.rightView = createClearTextFieldButton(textFieldManager: textFieldManager) + textField.rightViewMode = .whileEditing + return textField + } + + func createClearTextFieldButton(textFieldManager: TextFieldManager) -> UIButton { + let button = UIButton(type: .custom) + button.setImage(ImageLiterals.placeholderDeleteIcon, for: .normal) + button.addTarget(textFieldManager, action: #selector(textFieldManager.handleTextFieldClearButton), for: .touchUpInside) + button.frame = CGRect(x: 0, y: 0, width: 18, height: 18) + button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 15) + return button + } +} diff --git a/pophory-iOS/Global/Extensions/UILabel+Extension.swift b/pophory-iOS/Global/Extensions/UILabel+Extension.swift index 7619c8b6..0f9fd266 100644 --- a/pophory-iOS/Global/Extensions/UILabel+Extension.swift +++ b/pophory-iOS/Global/Extensions/UILabel+Extension.swift @@ -9,6 +9,25 @@ import UIKit extension UILabel { + func setAttributedText( + text: String, + targetString: String, + lineHeight: CGFloat, + color: UIColor, + font: UIFont, + boldFont: UIFont + ) { + let defaultAttributes = setDefaultAttributes(lineHeight: lineHeight, font: font) + let fullAttributedString = NSMutableAttributedString(string: text, attributes: defaultAttributes) + + let targetRange = (text as NSString).range(of: targetString) + fullAttributedString.addAttribute(.foregroundColor, value: color, range: targetRange) + fullAttributedString.addAttribute(.font, value: boldFont, range: targetRange) + + self.attributedText = fullAttributedString + self.numberOfLines = 0 + } + /// 행간 조정 메서드 func setLineSpacing(lineSpacing: CGFloat) { if let text = self.text { diff --git a/pophory-iOS/Global/Protocols/TextFieldManagerDelegate.swift b/pophory-iOS/Global/Protocols/TextFieldManagerDelegate.swift new file mode 100644 index 00000000..f1551470 --- /dev/null +++ b/pophory-iOS/Global/Protocols/TextFieldManagerDelegate.swift @@ -0,0 +1,18 @@ +// +// TextFieldUpdatable.swift +// pophory-iOS +// +// Created by Joon Baek on 2023/08/09. +// + +import UIKit + +protocol TextFieldManagerDelegate: AnyObject { + func updateBorderColor(to color: UIColor) + func setWarningLabelHidden(isHidden: Bool) + func setCharCountLabelText(text: String) + func setWarningLabelText(text: String) + func setNextButtonEnabled(isEnabled: Bool) +} + + diff --git a/pophory-iOS/Global/Resources/SceneDelegate.swift b/pophory-iOS/Global/Resources/SceneDelegate.swift index a30c2bd1..a62c6315 100644 --- a/pophory-iOS/Global/Resources/SceneDelegate.swift +++ b/pophory-iOS/Global/Resources/SceneDelegate.swift @@ -32,10 +32,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let window = UIWindow(windowScene: windowScene) window.overrideUserInterfaceStyle = UIUserInterfaceStyle.light - let appleLoginManager = AppleLoginManager() - let rootVC = OnboardingViewController(appleLoginManager: appleLoginManager) +// let appleLoginManager = AppleLoginManager() +// let rootVC = OnboardingViewController(appleLoginManager: appleLoginManager) +// +// appleLoginManager.delegate = rootVC - appleLoginManager.delegate = rootVC + let rootVC = NameInputViewController() let navigationController = PophoryNavigationController(rootViewController: rootVC) diff --git a/pophory-iOS/Global/Utilities/TextFieldManager.swift b/pophory-iOS/Global/Utilities/TextFieldManager.swift new file mode 100644 index 00000000..87b9c80d --- /dev/null +++ b/pophory-iOS/Global/Utilities/TextFieldManager.swift @@ -0,0 +1,153 @@ +// +// TextFieldManager.swift +// pophory-iOS +// +// Created by Joon Baek on 2023/07/24. +// + +import UIKit + +final class TextFieldManager: NSObject { + + weak var delegate: TextFieldManagerDelegate? + + var maxCharCount: Int = 6 + +} + +//MARK: - Extensions + +extension TextFieldManager { + + // MARK: - @objc + + @objc func onClickClearTextFieldButton(_ textField: UITextField) { + textField.text = nil + } + + @objc func handleTextFieldEditingChanged(_ textField: UITextField) { + textFieldDidChangeSelection(textField) + } + + @objc func handleTextFieldClearButton(_ textField: UITextField) { + textField.text = "" + textFieldDidChangeSelection(textField) + } + + // MARK: - Private Methods + + private func updateUIWithValidStatus(valid: Bool) { + let borderColor: UIColor = valid ? .pophoryPurple : .pophoryRed + delegate?.updateBorderColor(to: borderColor) + } + + private func handleNextButtonStatus(charCount: Int, minCharCount: Int, maxCharCount: Int) { + delegate?.updateBorderColor(to: .pophoryPurple) + + if charCount >= minCharCount && charCount <= maxCharCount { + delegate?.setWarningLabelHidden(isHidden: true) + delegate?.setNextButtonEnabled(isEnabled: true) + } else { + delegate?.setWarningLabelHidden(isHidden: false) + delegate?.setNextButtonEnabled(isEnabled: false) + } + } + + func textFieldDidBeginEditing(_ textField: UITextField) { + if textField.text?.count == 0 { + delegate?.updateBorderColor(to: .pophoryPurple) + } else { + if !textField.text!.isContainKoreanOnly() { + delegate?.updateBorderColor(to: .pophoryRed) + } + } + } + + func textFieldDidEndEditing(_ textField: UITextField) { + delegate?.updateBorderColor(to: .pophoryGray400) + } +} + +extension TextFieldManager: UITextFieldDelegate { + @objc func textFieldDidChangeSelection(_ textField: UITextField) { + let text = textField.text ?? "" + let charCount = text.count + let countLabelText = "(\(charCount)/\(maxCharCount))" + delegate?.setCharCountLabelText(text: countLabelText) + + let isTextFieldEmpty = text.isEmpty + textField.rightView?.isHidden = isTextFieldEmpty + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { + if text.isContainKoreanOnly() { + textField.layer.borderColor = UIColor.pophoryPurple.cgColor + + if text.count >= 2 && text.count <= 6 { + self.delegate?.setWarningLabelHidden(isHidden: true) + self.delegate?.setNextButtonEnabled(isEnabled: true) + } else { + self.delegate?.setWarningLabelText(text: "2-6글자 이내로 작성해주세요") + self.delegate?.setWarningLabelHidden(isHidden: false) + self.delegate?.setNextButtonEnabled(isEnabled: false) + } + + } else { + textField.layer.borderColor = UIColor.pophoryRed.cgColor + self.delegate?.setWarningLabelText(text: "현재 한국어만 지원하고 있어요") + self.delegate?.setWarningLabelHidden(isHidden: false) + self.delegate?.setNextButtonEnabled(isEnabled: false) + } + } + } +} + +// @objc func onValueChangedTextField(_ textField: UITextField) { +// guard let text = textField.text else { return } +// +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { +// if text.isContainKoreanOnly() { +// textField.layer.borderColor = UIColor.pophoryPurple.cgColor +// +// if text.count >= 2 && text.count <= 6 { +// self.warningLabelHidden?(true) +// self.nextButtonEnabled?(true) +// } else { +// self.warningLabelText?("2-6글자 이내로 작성해주세요") +// self.warningLabelHidden?(false) +// self.nextButtonEnabled?(false) +// } +// +// } else { +// textField.layer.borderColor = UIColor.pophoryRed.cgColor +// self.warningLabelText?("현재 한국어만 지원하고 있어요") +// self.warningLabelHidden?(false) +// self.nextButtonEnabled?(false) +// } +// } +// } + +// @objc func textFieldDidChangeSelection(_ textField: UITextField) { +// let text = textField.text ?? "" +// let charCount = text.count +// let countLabelText = "(\(charCount)/\(maxCharCount))" +// setCharCountLabelText?(countLabelText) +// +// let isTextFieldEmpty = text.isEmpty +// textField.rightView?.isHidden = isTextFieldEmpty +// +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { +// if self.isKoreanID { +// let valid = text.isContainKoreanOnly() +// self.updateUIWithValidStatus(valid: valid) +// if valid { +// self.handleNextButtonStatus(charCount: charCount, minCharCount: 2, maxCharCount: 6) +// } +// } else { +// let valid = text.isValidCharacters() +// self.updateUIWithValidStatus(valid: valid) +// if valid { +// self.handleNextButtonStatus(charCount: charCount, minCharCount: 4, maxCharCount: 12) +// } +// } +// } +// } diff --git a/pophory-iOS/Screen/AlbumDetail/AlbumDetailViewController.swift b/pophory-iOS/Screen/AlbumDetail/AlbumDetailViewController.swift index d69b5378..a544551e 100644 --- a/pophory-iOS/Screen/AlbumDetail/AlbumDetailViewController.swift +++ b/pophory-iOS/Screen/AlbumDetail/AlbumDetailViewController.swift @@ -248,6 +248,7 @@ extension AlbumDetailViewController { self.albumPhotoList = mappedDefaultAlbumPhotoListDTO default : return } + self.homeAlbumView.photoCollectionView.hideSkeleton() } } } diff --git a/pophory-iOS/Screen/AlbumDetail/PhotoCollectionViewDataSource.swift b/pophory-iOS/Screen/AlbumDetail/PhotoCollectionViewDataSource.swift index c07576e6..30c4b470 100644 --- a/pophory-iOS/Screen/AlbumDetail/PhotoCollectionViewDataSource.swift +++ b/pophory-iOS/Screen/AlbumDetail/PhotoCollectionViewDataSource.swift @@ -7,6 +7,8 @@ import UIKit +import SkeletonView + final class PhotoCollectionViewDataSource { typealias collectionCell = PhotoCollectionViewCell @@ -40,6 +42,7 @@ final class PhotoCollectionViewDataSource { let photo = albumPhotoList.photos[indexPath.row] let cell: collectionCell = self.collectionView.dequeueReusableCell(forIndexPath: indexPath) + cell.showAnimatedGradientSkeleton() cell.configCell(imageUrl: photo.imageUrl) return cell } diff --git a/pophory-iOS/Screen/AlbumDetail/View/PhotoCollectionViewCell.swift b/pophory-iOS/Screen/AlbumDetail/View/PhotoCollectionViewCell.swift index 85783323..e4b84395 100644 --- a/pophory-iOS/Screen/AlbumDetail/View/PhotoCollectionViewCell.swift +++ b/pophory-iOS/Screen/AlbumDetail/View/PhotoCollectionViewCell.swift @@ -8,6 +8,7 @@ import UIKit import SnapKit +import SkeletonView import Kingfisher final class PhotoCollectionViewCell: UICollectionViewCell { @@ -23,6 +24,7 @@ final class PhotoCollectionViewCell: UICollectionViewCell { private let photoImage: UIImageView = { let imageView = UIImageView() + imageView.isSkeletonable = true imageView.image = ImageLiterals.defaultPhotoIcon imageView.layer.cornerRadius = 2 imageView.clipsToBounds = true @@ -67,12 +69,16 @@ final class PhotoCollectionViewCell: UICollectionViewCell { ) { self.cellType = cellType - if imageUrl == "" { - photoImage.image = ImageLiterals.defaultPhotoIcon - } else { - let url = URL(string: imageUrl) - photoImage.kf.setImage(with: url) - photoImage.contentMode = cellType == .albumDetail ? .scaleToFill : .scaleAspectFill + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in + if imageUrl == "" { + photoImage.image = ImageLiterals.defaultPhotoIcon + hideSkeleton() + } else { + let url = URL(string: imageUrl) + photoImage.kf.setImage(with: url) + photoImage.contentMode = cellType == .albumDetail ? .scaleToFill : .scaleAspectFill + hideSkeleton() + } } } } @@ -80,6 +86,7 @@ final class PhotoCollectionViewCell: UICollectionViewCell { extension PhotoCollectionViewCell { private func setupLayout() { isSelected = false + isSkeletonable = true addSubviews([ photoImage, diff --git a/pophory-iOS/Screen/Auth/ViewControllers/NameInputViewController.swift b/pophory-iOS/Screen/Auth/ViewControllers/NameInputViewController.swift index 11cff15f..949b1b40 100644 --- a/pophory-iOS/Screen/Auth/ViewControllers/NameInputViewController.swift +++ b/pophory-iOS/Screen/Auth/ViewControllers/NameInputViewController.swift @@ -18,6 +18,7 @@ final class NameInputViewController: BaseViewController { // MARK: - UI Properties private lazy var nameInputView = NameInputView() + private var textFieldManager = TextFieldManager() // MARK: - Life Cycle @@ -33,6 +34,9 @@ final class NameInputViewController: BaseViewController { showNavigationBar() handleNextButton() hideKeyboard() + textFieldManager.delegate = nameInputView + + textFieldManager.textFieldDidEndEditing(nameInputView.inputTextField) } override func viewDidLayoutSubviews() { diff --git a/pophory-iOS/Screen/Auth/Views/IDInputView.swift b/pophory-iOS/Screen/Auth/Views/IDInputView.swift index 6c0ac4b8..2e35d7b6 100644 --- a/pophory-iOS/Screen/Auth/Views/IDInputView.swift +++ b/pophory-iOS/Screen/Auth/Views/IDInputView.swift @@ -19,7 +19,6 @@ final class IDInputView: NameInputView { updateIndicatorViewBackgroundColor(at: 1, color: .pophoryPurple) updateIndicatorViewBackgroundColor(at: 0, color: .pophoryGray300) updateNameInputViewLabels() - maxCharCount = 12 } required init?(coder: NSCoder) { @@ -30,7 +29,7 @@ final class IDInputView: NameInputView { private func updateNameInputViewLabels() { headerLabel.text = "너만의 재치있는\n포포리 아이디를 만들어줘!" - headerLabel.applyColorAndBoldText(targetString: "포포리 아이디", color: .pophoryPurple, font: .head1Medium, boldFont: .head1Bold) +// headerLabel.applyColorAndBoldText(targetString: "포포리 아이디", color: .pophoryPurple, font: .head1Medium, boldFont: .head1Bold) bodyLabel.text = "영문, 숫자, 특수문자 조합 4-12자리 이내로\n작성해주세요 (특수문자는 . _ 만 가능해요)" inputTextField.placeholder = "아이디" charCountLabel.text = "(0/12)" @@ -40,27 +39,27 @@ final class IDInputView: NameInputView { charCountLabel.text = "(\(charCount)/12)" } - override func onValueChangedTextField() { - guard let text = inputTextField.text else { return } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - if text.isValidCharacters() { - self.inputTextField.layer.borderColor = UIColor.pophoryPurple.cgColor - self.warningLabel.isHidden = true - - if text.count >= 4 && text.count <= 12 { - self.nextButton.isEnabled = true - } else { - self.warningLabel.text = "4-12자 이내로 작성해주세요" - self.warningLabel.isHidden = false - self.nextButton.isEnabled = false - } - } else { - self.inputTextField.layer.borderColor = UIColor.pophoryRed.cgColor - self.warningLabel.text = "올바른 형식의 아이디가 아닙니다" - self.warningLabel.isHidden = false - self.nextButton.isEnabled = false - } - } - } +// override func onValueChangedTextField() { +// guard let text = inputTextField.text else { return } +// +// DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { +// if text.isValidCharacters() { +// self.inputTextField.layer.borderColor = UIColor.pophoryPurple.cgColor +// self.warningLabel.isHidden = true +// +// if text.count >= 4 && text.count <= 12 { +// self.nextButton.isEnabled = true +// } else { +// self.warningLabel.text = "4-12자 이내로 작성해주세요" +// self.warningLabel.isHidden = false +// self.nextButton.isEnabled = false +// } +// } else { +// self.inputTextField.layer.borderColor = UIColor.pophoryRed.cgColor +// self.warningLabel.text = "올바른 형식의 아이디가 아닙니다" +// self.warningLabel.isHidden = false +// self.nextButton.isEnabled = false +// } +// } +// } } diff --git a/pophory-iOS/Screen/Auth/Views/NameInputView.swift b/pophory-iOS/Screen/Auth/Views/NameInputView.swift index 4a601145..0f1812a9 100644 --- a/pophory-iOS/Screen/Auth/Views/NameInputView.swift +++ b/pophory-iOS/Screen/Auth/Views/NameInputView.swift @@ -11,12 +11,10 @@ import SnapKit class NameInputView: BaseSignUpView { - var maxCharCount: Int = 6 - - // MARK: - UI Properties - // TODO: Private -> Delegate 패턴 구현 + var textFieldManager = TextFieldManager() + lazy var bodyStackView: UIStackView = { let stackView = UIStackView() stackView.axis = .vertical @@ -31,38 +29,13 @@ class NameInputView: BaseSignUpView { label.textColor = .pophoryGray500 label.font = .title1 label.numberOfLines = 0 - label.setTextWithLineHeight(lineHeight: convertByHeightRatio(24)) + // label.setTextWithLineHeight(lineHeight: convertByHeightRatio(24)) return label }() - lazy var inputTextField: UITextField = { - let textField = UITextField() - textField.placeholder = "닉네임" - textField.textColor = .black - textField.backgroundColor = .pophoryGray100 - textField.font = .popupButton - textField.layer.borderColor = UIColor.pophoryGray400.cgColor - textField.layer.borderWidth = 1 - textField.makeRounded(radius: 18) - textField.addPadding(left: 15) - textField.addTarget(self, action: #selector(textFieldDidChangeSelection), for: .editingChanged) - - let clearTextFieldIcon: UIButton = { - let button = UIButton(type: .custom) - button.setImage(ImageLiterals.placeholderDeleteIcon, for: .normal) - button.addTarget(self, action: #selector(clearTextFieldTapped), for: .touchUpInside) - button.frame = CGRect(x: 0, y: 0, width: 18, height: 18) - button.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: convertByWidthRatio(15)) - return button - }() - - textField.rightView = clearTextFieldIcon - textField.rightViewMode = .whileEditing - - return textField - }() + lazy var inputTextField: UITextField = { createInputTextField(placeholder: "닉네임", textFieldManager: textFieldManager) }() - lazy var charCountLabel: UILabel = { + var charCountLabel: UILabel = { let label = UILabel() label.font = .popupButton label.textColor = .pophoryGray400 @@ -70,7 +43,7 @@ class NameInputView: BaseSignUpView { return label }() - lazy var warningLabel: UILabel = { + var warningLabel: UILabel = { let label = UILabel() label.font = .popupLine label.textColor = .pophoryRed @@ -79,22 +52,19 @@ class NameInputView: BaseSignUpView { return label }() + // MARK: - Life Cycle + override init(frame: CGRect) { super.init(frame: frame) - updateIndicatorViewBackgroundColor(at: 0, color: .pophoryPurple) - inputTextField.addTarget(self, action: #selector(textFieldDidChangeSelection), for: .editingChanged) setupDelegate() setupViews() + updateIndicatorViewBackgroundColor(at: 0, color: .pophoryPurple) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - override func updateCharCountLabel(charCount: Int) { - charCountLabel.text = "(\(charCount)/6)" - } } // MARK: - Extensions @@ -130,71 +100,32 @@ extension NameInputView { inputTextField.addTarget(self, action: #selector(onValueChangedTextField), for: .editingChanged) } - // MARK: - @objc - - @objc func clearTextFieldButtonOnClick() { - inputTextField.text = nil - } - - @objc private func clearTextFieldTapped() { - inputTextField.text = "" - } - - @objc func onValueChangedTextField() { - guard let text = inputTextField.text else { return } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - if text.isContainKoreanOnly() { - self.inputTextField.layer.borderColor = UIColor.pophoryPurple.cgColor - - if text.count >= 2 && text.count <= 6 { - self.warningLabel.isHidden = true - self.nextButton.isEnabled = true - } else { - self.warningLabel.text = "2-6글자 이내로 작성해주세요" - self.warningLabel.isHidden = false - self.nextButton.isEnabled = false - } - - } else { - self.inputTextField.layer.borderColor = UIColor.pophoryRed.cgColor - self.warningLabel.text = "현재 한국어만 지원하고 있어요" - self.warningLabel.isHidden = false - self.nextButton.isEnabled = false - } - } - } - // MARK: - Private Methods private func setupDelegate() { - inputTextField.delegate = self + inputTextField.delegate = textFieldManager + textFieldManager.delegate = self } } -// MARK: - UITextFieldDelegate - -extension NameInputView: UITextFieldDelegate { - func textFieldDidBeginEditing(_ textField: UITextField) { - if textField.text?.count == 0 { - textField.layer.borderColor = UIColor.pophoryPurple.cgColor - } else { - if !textField.text!.isContainKoreanOnly() { - textField.layer.borderColor = UIColor.pophoryRed.cgColor - } - } +extension NameInputView: TextFieldManagerDelegate { + func updateBorderColor(to color: UIColor) { + inputTextField.layer.borderColor = color.cgColor } - func textFieldDidEndEditing(_ textField: UITextField) { - textField.layer.borderColor = UIColor.pophoryGray400.cgColor + func setWarningLabelHidden(isHidden: Bool) { + warningLabel.isHidden = isHidden } - @objc func textFieldDidChangeSelection(_ textField: UITextField) { - let text = textField.text ?? "" - let charCount = text.count - updateCharCountLabel(charCount: charCount) - - let isTextFieldEmpty = text.isEmpty - textField.rightView?.isHidden = isTextFieldEmpty + func setCharCountLabelText(text: String) { + charCountLabel.text = text + } + + func setWarningLabelText(text: String) { + warningLabel.text = text + } + + func setNextButtonEnabled(isEnabled: Bool) { + nextButton.isEnabled = isEnabled } } diff --git a/pophory-iOS/Screen/Auth/Views/PickAlbumCoverView.swift b/pophory-iOS/Screen/Auth/Views/PickAlbumCoverView.swift index 7dea46ff..68649c7a 100644 --- a/pophory-iOS/Screen/Auth/Views/PickAlbumCoverView.swift +++ b/pophory-iOS/Screen/Auth/Views/PickAlbumCoverView.swift @@ -116,7 +116,7 @@ extension PickAlbumCoverView { private func updateNameInputViewLabels() { headerLabel.text = "마음에 쏙 드는\n앨범 테마를 선택해줘!" - headerLabel.applyColorAndBoldText(targetString: "앨범 테마", color: .pophoryPurple, font: .head1Medium, boldFont: .head1Bold) +// headerLabel.applyColorAndBoldText(targetString: "앨범 테마", color: .pophoryPurple, font: .head1Medium, boldFont: .head1Bold) } private func setupRegister() { @@ -192,6 +192,8 @@ extension PickAlbumCoverView: UICollectionViewDataSource { } } +// MARK: - UICollectionViewDelegate + extension PickAlbumCoverView: UICollectionViewDelegate { func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { let currentPage = Int(scrollView.contentOffset.x / scrollView.frame.size.width) diff --git a/pophory-iOS/Screen/Auth/Views/StartPophoryView.swift b/pophory-iOS/Screen/Auth/Views/StartPophoryView.swift index 39524e79..e405159b 100644 --- a/pophory-iOS/Screen/Auth/Views/StartPophoryView.swift +++ b/pophory-iOS/Screen/Auth/Views/StartPophoryView.swift @@ -23,7 +23,7 @@ final class StartPophoryView: UIView { label.font = .head1Medium label.textColor = .pophoryWhite label.numberOfLines = 0 - label.setTextWithLineHeight(lineHeight: 34) +// label.setTextWithLineHeight(lineHeight: 34) return label }()