Skip to content

Commit

Permalink
feat/#154 온보딩 프로세스 구현완료
Browse files Browse the repository at this point in the history
  • Loading branch information
hooni0918 committed Jul 10, 2024
1 parent 8636c80 commit 2b04452
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 136 deletions.
24 changes: 12 additions & 12 deletions KkuMulKum.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
782B406F2C3DBF93008B0CA7 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B406E2C3DBF93008B0CA7 /* ProfileViewController.swift */; };
782B40722C3DBFA3008B0CA7 /* ProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B40712C3DBFA3008B0CA7 /* ProfileViewModel.swift */; };
782B40752C3DBFBA008B0CA7 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B40742C3DBFBA008B0CA7 /* ProfileView.swift */; };
782B407B2C3E395A008B0CA7 /* WelcomeVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407A2C3E395A008B0CA7 /* WelcomeVIew.swift */; };
782B407D2C3E3984008B0CA7 /* WelcomeViewcontroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407C2C3E3984008B0CA7 /* WelcomeViewcontroller.swift */; };
782B407F2C3E44B7008B0CA7 /* WelcomeVIewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407E2C3E44B7008B0CA7 /* WelcomeVIewModel.swift */; };
782B407B2C3E395A008B0CA7 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407A2C3E395A008B0CA7 /* WelcomeView.swift */; };
782B407D2C3E3984008B0CA7 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407C2C3E3984008B0CA7 /* WelcomeViewController.swift */; };
782B407F2C3E44B7008B0CA7 /* WelcomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B407E2C3E44B7008B0CA7 /* WelcomeViewModel.swift */; };
782B40822C3E4925008B0CA7 /* NicknameViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 782B40812C3E4925008B0CA7 /* NicknameViewModel.swift */; };
784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; };
784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; };
Expand Down Expand Up @@ -132,9 +132,9 @@
782B406E2C3DBF93008B0CA7 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
782B40712C3DBFA3008B0CA7 /* ProfileViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewModel.swift; sourceTree = "<group>"; };
782B40742C3DBFBA008B0CA7 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
782B407A2C3E395A008B0CA7 /* WelcomeVIew.swift */ = {isa = PBXFileReference; indentWidth = 5; lastKnownFileType = sourcecode.swift; path = WelcomeVIew.swift; sourceTree = "<group>"; };
782B407C2C3E3984008B0CA7 /* WelcomeViewcontroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewcontroller.swift; sourceTree = "<group>"; };
782B407E2C3E44B7008B0CA7 /* WelcomeVIewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeVIewModel.swift; sourceTree = "<group>"; };
782B407A2C3E395A008B0CA7 /* WelcomeView.swift */ = {isa = PBXFileReference; indentWidth = 5; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
782B407C2C3E3984008B0CA7 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = "<group>"; };
782B407E2C3E44B7008B0CA7 /* WelcomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewModel.swift; sourceTree = "<group>"; };
782B40812C3E4925008B0CA7 /* NicknameViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NicknameViewModel.swift; sourceTree = "<group>"; };
785AE1D02C3B07A600677CA0 /* PrivacyInfo.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = PrivacyInfo.plist; sourceTree = "<group>"; };
7898732F2C3D1A7B00435E96 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -311,23 +311,23 @@
782B40772C3E38B5008B0CA7 /* View */ = {
isa = PBXGroup;
children = (
782B407A2C3E395A008B0CA7 /* WelcomeVIew.swift */,
782B407A2C3E395A008B0CA7 /* WelcomeView.swift */,
);
path = View;
sourceTree = "<group>";
};
782B40782C3E38BC008B0CA7 /* ViewModel */ = {
isa = PBXGroup;
children = (
782B407E2C3E44B7008B0CA7 /* WelcomeVIewModel.swift */,
782B407E2C3E44B7008B0CA7 /* WelcomeViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
782B40792C3E38C3008B0CA7 /* ViewController */ = {
isa = PBXGroup;
children = (
782B407C2C3E3984008B0CA7 /* WelcomeViewcontroller.swift */,
782B407C2C3E3984008B0CA7 /* WelcomeViewController.swift */,
);
path = ViewController;
sourceTree = "<group>";
Expand Down Expand Up @@ -870,7 +870,7 @@
78AED1342C3D951F000AD80A /* NicknameViewController.swift in Sources */,
DE254AB42C31199B00A4015E /* UITextField+.swift in Sources */,
DDAF1C912C3D6E3D008A37D3 /* TardyViewController.swift in Sources */,
782B407D2C3E3984008B0CA7 /* WelcomeViewcontroller.swift in Sources */,
782B407D2C3E3984008B0CA7 /* WelcomeViewController.swift in Sources */,
DE8248002C36E857000601BC /* ObservablePattern.swift in Sources */,
DDAF1C902C3D6E3D008A37D3 /* PromiseViewModel.swift in Sources */,
DE254AAA2C31190E00A4015E /* UIStackView+.swift in Sources */,
Expand All @@ -881,9 +881,9 @@
DE254AA82C3118EA00A4015E /* UIView+.swift in Sources */,
DE254AAE2C31193600A4015E /* UIFont+.swift in Sources */,
DE9E189A2C3BCCBE00DB76B4 /* UtilsTemp.swift in Sources */,
782B407B2C3E395A008B0CA7 /* WelcomeVIew.swift in Sources */,
782B407B2C3E395A008B0CA7 /* WelcomeView.swift in Sources */,
DD3072142C3BF87A00416D9F /* NearestPromiseResponseModel.swift in Sources */,
782B407F2C3E44B7008B0CA7 /* WelcomeVIewModel.swift in Sources */,
782B407F2C3E44B7008B0CA7 /* WelcomeViewModel.swift in Sources */,
DD3072162C3BFE4E00416D9F /* UpcomingPromiseListResponseModel.swift in Sources */,
A3FB18572C3BF704001483E5 /* MeetingListResponseModel.swift in Sources */,
DE254AB22C31197B00A4015E /* UIButton+.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion KkuMulKum/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
) {
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: windowScene)
self.window?.rootViewController = ProfileSetupViewController()
self.window?.rootViewController = NicknameViewController()
self.window?.makeKeyAndVisible()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
"scale" : "1x"
},
{
"filename" : "Mask group 1.svg",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Mask group 2.svg",
"idiom" : "universal",
"scale" : "3x"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import UIKit

class NicknameViewController: BaseViewController, UITextFieldDelegate {
class NicknameViewController: BaseViewController {

private let nicknameView = NicknameView()
private let viewModel = NicknameViewModel()
Expand Down Expand Up @@ -73,18 +73,18 @@ class NicknameViewController: BaseViewController, UITextFieldDelegate {
}

@objc private func nextButtonTapped() {
let name = viewModel.enteredName.value
let welcomeVC = WelcomeViewController(name: name)
present(welcomeVC, animated: true, completion: nil)
let profileSetupVC = ProfileSetupViewController(viewModel: ProfileSetupViewModel(nickname: viewModel.nickname.value))
profileSetupVC.modalPresentationStyle = .fullScreen
present(profileSetupVC, animated: true, completion: nil)
}

@objc private func dismissKeyboard() {
view.endEditing(true)
nicknameView.nicknameTextField.layer.borderColor = UIColor.gray3.cgColor
}
// MARK: - UITextFieldDelegate
}

extension NicknameViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
nicknameView.nicknameTextField.layer.borderColor = UIColor.gray3.cgColor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@ enum NicknameState {
class NicknameViewModel {
let nickname = ObservablePattern<String>("")
let nicknameState = ObservablePattern<NicknameState>(.empty)
let errorMessage = ObservablePattern<String?>("")
let errorMessage = ObservablePattern<String?>(nil)
let isNextButtonEnabled = ObservablePattern<Bool>(false)
let characterCount = ObservablePattern<String>("0/5")
let enteredName = ObservablePattern<String>("") //데이터 전송


private let nicknameRegex = "^[가-힣a-zA-Z0-9]{1,5}$"

func validateNickname(_ name: String) {
nickname.value = name
characterCount.value = "\(name.count)/5"
enteredName.value = name

if name.isEmpty {
nicknameState.value = .empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@

import UIKit

class ProfileSetupViewController: BaseViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
class ProfileSetupViewController: BaseViewController {
private let rootView = ProfileSetupView()
private let viewModel = ProfileSetupViewModel()
private let viewModel: ProfileSetupViewModel

init(viewModel: ProfileSetupViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func loadView() {
view = rootView
}
Expand All @@ -22,13 +31,13 @@ class ProfileSetupViewController: BaseViewController, UIImagePickerControllerDel
}

private func setupBindings() {
viewModel.profileImage.bind(with: self) { (vc, image) in
vc.rootView.profileImageView.image = image
viewModel.profileImage.bind { [weak self] image in
self?.rootView.profileImageView.image = image
}

viewModel.isConfirmButtonEnabled.bind(with: self) { (vc, isEnabled) in
vc.rootView.confirmButton.isEnabled = isEnabled
vc.rootView.confirmButton.alpha = isEnabled ? 1.0 : 0.5
viewModel.isConfirmButtonEnabled.bind { [weak self] isEnabled in
self?.rootView.confirmButton.isEnabled = isEnabled
self?.rootView.confirmButton.alpha = isEnabled ? 1.0 : 0.5
}
}

Expand All @@ -39,13 +48,15 @@ class ProfileSetupViewController: BaseViewController, UIImagePickerControllerDel
}

@objc private func confirmButtonTapped() {
// TODO: 확인 버튼 탭 시 동작 구현
print("프로필 이미지 설정 완료")
let welcomeVC = WelcomeViewController(viewModel: WelcomeViewModel(nickname: viewModel.nickname))
welcomeVC.modalPresentationStyle = .fullScreen
present(welcomeVC, animated: true, completion: nil)
}

@objc private func skipButtonTapped() {
// TODO: 건너뛰기 버튼 탭 시 동작 구현
print("프로필 이미지 설정 건너뛰기")
let welcomeVC = WelcomeViewController(viewModel: WelcomeViewModel(nickname: viewModel.nickname))
welcomeVC.modalPresentationStyle = .fullScreen
present(welcomeVC, animated: true, completion: nil)
}

@objc private func cameraButtonTapped() {
Expand All @@ -56,19 +67,30 @@ class ProfileSetupViewController: BaseViewController, UIImagePickerControllerDel
present(imagePicker, animated: true)
}

// MARK: - UIImagePickerControllerDelegate

private func cropToCircle(image: UIImage) -> UIImage {
let shorterSide = min(image.size.width, image.size.height)
let imageBounds = CGRect(x: 0, y: 0, width: shorterSide, height: shorterSide)
UIGraphicsBeginImageContextWithOptions(imageBounds.size, false, UIScreen.main.scale)
let context = UIGraphicsGetCurrentContext()!
context.addEllipse(in: imageBounds)
context.clip()
image.draw(in: imageBounds)
let circleImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return circleImage
}
}

extension ProfileSetupViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let editedImage = info[.editedImage] as? UIImage {
viewModel.updateProfileImage(editedImage)
} else if let originalImage = info[.originalImage] as? UIImage {
viewModel.updateProfileImage(originalImage)
if let editedImage = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage {
let croppedImage = cropToCircle(image: editedImage)
viewModel.updateProfileImage(croppedImage)
}
dismiss(animated: true)
}

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true)
}

}
38 changes: 29 additions & 9 deletions KkuMulKum/Source/Onboarding/Profile/View/ProfileView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ import Then

class ProfileSetupView: BaseView {

let navigationBar = UIView().then {
$0.backgroundColor = .white
}

let titleLabel = UILabel().then {
$0.text = "프로필 설정"
$0.font = UIFont.pretendard(.body03)
$0.textAlignment = .center
}

let separatorLine = UIView().then {
$0.backgroundColor = .gray2
}

let subtitleLabel = UILabel().then {
$0.text = "프로필을 설정해 주세요"
$0.font = UIFont.pretendard(.head01)
Expand All @@ -27,9 +35,11 @@ class ProfileSetupView: BaseView {

let profileImageView = UIImageView().then {
$0.image = UIImage.imgProfile
$0.contentMode = .scaleAspectFit
$0.layer.cornerRadius = 50
$0.contentMode = .scaleAspectFill
$0.clipsToBounds = true
$0.layer.cornerRadius = 75
$0.layer.borderWidth = 1
$0.layer.borderColor = UIColor.gray3.cgColor
}

let cameraButton = UIButton().then {
Expand All @@ -42,31 +52,41 @@ class ProfileSetupView: BaseView {
$0.setTitle("지금은 넘어가기", for: .normal)
$0.setTitleColor(.gray5, for: .normal)
$0.titleLabel?.font = UIFont.pretendard(.body05)

}

let confirmButton = UIButton().then {
$0.setTitle("확인", for: .normal)
$0.backgroundColor = .maincolor
$0.layer.cornerRadius = 8
$0.titleLabel?.font = UIFont.pretendard(.body03)

}

override func setupView() {
backgroundColor = .white
[titleLabel, subtitleLabel, profileImageView, cameraButton, skipButton, confirmButton].forEach { addSubview($0) }
[navigationBar, separatorLine, subtitleLabel, profileImageView, cameraButton, skipButton, confirmButton].forEach { addSubview($0) }
navigationBar.addSubview(titleLabel)
}

override func setupAutoLayout() {
navigationBar.snp.makeConstraints {
$0.top.leading.trailing.equalToSuperview()
$0.height.equalTo(93)
}

titleLabel.snp.makeConstraints {
$0.top.equalTo(safeAreaLayoutGuide).offset(20)
$0.centerX.equalToSuperview()
$0.centerX.equalTo(navigationBar.snp.centerX)
$0.bottom.equalTo(navigationBar.snp.bottom).offset(-12)
}

separatorLine.snp.makeConstraints {
$0.top.equalTo(navigationBar.snp.bottom)
$0.leading.trailing.equalToSuperview()
$0.height.equalTo(1)
}

subtitleLabel.snp.makeConstraints {
$0.top.equalTo(titleLabel.snp.bottom).offset(20)
$0.leading.equalToSuperview().inset(20)
$0.top.equalTo(separatorLine.snp.bottom).offset(24)
$0.leading.trailing.equalToSuperview().inset(20)
}

profileImageView.snp.makeConstraints {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
// Created by 이지훈 on 7/10/24.
//


import UIKit

class ProfileSetupViewModel {
let profileImage = ObservablePattern<UIImage?>(UIImage.imgProfile)
let isConfirmButtonEnabled = ObservablePattern<Bool>(false)
let nickname: String

init(nickname: String) {
self.nickname = nickname
}

func updateProfileImage(_ image: UIImage?) {
profileImage.value = image
Expand Down
Loading

0 comments on commit 2b04452

Please sign in to comment.