Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] #243 - 온보딩 MVVM + Combine 적용 #250

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 78 additions & 72 deletions iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -64,27 +64,33 @@ extension ViewControllerFactoryImpl {
// onboarding
extension ViewControllerFactoryImpl {
func makeValueOnboardingViewController(coordinator: AuthCoordinator) -> ValueOnboardingViewController {
let viewController = ValueOnboardingViewController(coordinator: coordinator)
let viewModel = ValueOnboardingViewModelImpl(coordinator: coordinator)
let viewController = ValueOnboardingViewController(viewModel: viewModel)
return viewController
}
func makeLogoOnboardingViewController(coordinator: AuthCoordinator) -> LogoOnboardingViewController {
let viewController = LogoOnboardingViewController(coordinator: coordinator)
let viewModel = LogoOnboardingViewModelImpl(coodinator: coordinator)
let viewController = LogoOnboardingViewController(viewModel: viewModel)
return viewController
}
func makeSecondOnboardingViewController(coordinator: AuthCoordinator) -> SecondOnboardingViewController {
let viewController = SecondOnboardingViewController(coordinator: coordinator)
let viewModel = SecondOnboardingViewModelImpl(coordinator: coordinator)
let viewController = SecondOnboardingViewController(viewModel: viewModel)
return viewController
}
func makeThirdOnboardingViewController(coordinator: AuthCoordinator) -> ThirdOnboardingViewController {
let viewController = ThirdOnboardingViewController(coordinator: coordinator)
let viewModel = ThirdOnboardingViewModelImpl(coordinator: coordinator)
let viewController = ThirdOnboardingViewController(viewModel: viewModel)
return viewController
}
func makeFourthOnboardingViewController(coordinator: AuthCoordinator) -> FourthOnboardingViewController {
let viewController = FourthOnboardingViewController(coordinator: coordinator)
let viewModel = FourthOnboardingViewModelImpl(coordinator: coordinator)
let viewController = FourthOnboardingViewController(viewModel: viewModel)
return viewController
}
func makeFifthOnboardingViewController(coordinator: AuthCoordinator) -> FifthOnboardingViewController {
let viewController = FifthOnboardingViewController(coordinator: coordinator)
let viewModel = FifthOnboardingViewModelImpl(coordinator: coordinator)
let viewController = FifthOnboardingViewController(viewModel: viewModel)
return viewController
}
}
Expand Down
15 changes: 15 additions & 0 deletions iOS-NOTTODO/iOS-NOTTODO/Coordinator/Protocol/ViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// ViewModel.swift
// iOS-NOTTODO
//
// Created by 강윤서 on 3/13/24.
//

import Foundation

protocol ViewModel where Self: AnyObject {
associatedtype Input
associatedtype Output

func transform(input: Input) -> Output
}
78 changes: 78 additions & 0 deletions iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Combine+.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// Combine+.swift
// iOS-NOTTODO
//
// Created by 강윤서 on 3/20/24.
//

import Combine
import UIKit

extension UIControl {
func controlPublisher(for event: UIControl.Event) -> UIControl.EventPublisher {
return UIControl.EventPublisher(control: self, event: event)
}

// Publisher
struct EventPublisher: Publisher {
typealias Output = UIControl
typealias Failure = Never

let control: UIControl
let event: UIControl.Event

func receive<S>(subscriber: S)
where S: Subscriber, Never == S.Failure, UIControl == S.Input {
let subscription = EventSubscription(
control: control,
subscriber: subscriber,
event: event
)
subscriber.receive(subscription: subscription)
}
}

// Subscription
fileprivate class EventSubscription<EventSubscriber: Subscriber>: Subscription
where EventSubscriber.Input == UIControl, EventSubscriber.Failure == Never {

let control: UIControl
let event: UIControl.Event
var subscriber: EventSubscriber?

init(control: UIControl, subscriber: EventSubscriber, event: UIControl.Event) {
self.control = control
self.subscriber = subscriber
self.event = event
control.addTarget(self, action: #selector(eventDidOccur), for: event)
}

func request(_ demand: Subscribers.Demand) {}

func cancel() {
subscriber = nil
control.removeTarget(self, action: #selector(eventDidOccur), for: event)
}

@objc func eventDidOccur() {
_ = subscriber?.receive(control)
}
}
}

extension UITextField {
var textPublisher: AnyPublisher<String, Never> {
controlPublisher(for: .editingChanged)
.map { $0 as! UITextField }
.map { $0.text! }
.eraseToAnyPublisher()
}
}

extension UIButton {
var tapPublisher: AnyPublisher<Void, Never> {
controlPublisher(for: .touchUpInside)
.map { _ in }
.eraseToAnyPublisher()
}
}
13 changes: 13 additions & 0 deletions iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/UIButton+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,17 @@ extension UIButton {
)
setAttributedTitle(attributedString, for: .normal)
}

func setUnderlines(target: [String]) {
guard let title = titleLabel?.text else { return }
let attributedString = NSMutableAttributedString(string: title)
target.forEach {
let range = (title as NSString).range(of: $0)
attributedString.addAttribute(
.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: range)
}
setAttributedTitle(attributedString, for: .normal)
}
}
53 changes: 20 additions & 33 deletions iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthButtonView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import UIKit
import SnapKit
import Then

final class AuthButtonView: UIView {
final class AuthButton: UIButton {

// MARK: - UI Components
// MARK: - UI Properties

private var buttonView = UIView()
private var buttonIcon = UIImageView()
private var buttonLabel = UILabel()
private let image = UIImageView()

// MARK: - View Life Cycles

Expand All @@ -34,43 +32,32 @@ final class AuthButtonView: UIView {

// MARK: - Methods

extension AuthButtonView {
extension AuthButton {
private func setUI(title: String?, icon: UIImage?, color: UIColor?) {

buttonView.do {
$0.backgroundColor = color
$0.layer.cornerRadius = 5
}
image.image = icon

buttonIcon.image = icon
self.backgroundColor = color
self.layer.cornerRadius = 5

buttonLabel.do {
$0.text = title
$0.textColor = .systemBlack
$0.font = .Pretendard(.medium, size: 16)
}
var configuration = UIButton.Configuration.plain()

configuration.title = title
configuration.titleAlignment = .center
configuration.attributedTitle?.font = .Pretendard(.medium, size: 16)
configuration.baseBackgroundColor = color
configuration.baseForegroundColor = .systemBlack
configuration.contentInsets = NSDirectionalEdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 0)

self.configuration = configuration
}

private func setLayout() {
addSubviews(buttonView)
buttonView.addSubviews(buttonIcon, buttonLabel)

self.snp.makeConstraints {
$0.height.equalTo(53)
}

buttonView.snp.makeConstraints {
$0.leading.trailing.equalToSuperview().inset(17)
$0.height.equalToSuperview()
}
addSubviews(image)

buttonIcon.snp.makeConstraints {
$0.centerX.equalTo(buttonView.snp.leading).offset(27)
image.snp.makeConstraints {
$0.leading.equalToSuperview().inset(14)
$0.centerY.equalToSuperview()
}

buttonLabel.snp.makeConstraints {
$0.centerX.centerY.equalToSuperview()
}
}
}
88 changes: 22 additions & 66 deletions iOS-NOTTODO/iOS-NOTTODO/Presentation/Auth/AuthViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,10 @@ final class AuthViewController: UIViewController {
private var loginSubLabel = UILabel()

private var kakaoLoginImageView = UIImageView()
private var kakaoLoginButtonView = AuthButtonView(frame: .zero, title: I18N.kakaoLogin, icon: .kakaoLogo, color: .kakaoYellow)
private var appleLoginButtonView = AuthButtonView(frame: .zero, title: I18N.appleLogin, icon: .appleLogo, color: .white)
private var kakaoLoginButton = UIButton()
private var appleLoginButton = UIButton()
private var kakaoLoginButton = AuthButton(frame: .zero, title: I18N.kakaoLogin, icon: .kakaoLogo, color: .kakaoYellow)
private var appleLoginButton = AuthButton(frame: .zero, title: I18N.appleLogin, icon: .appleLogo, color: .white)

private var moreButton = UIButton()
private var conditionButton = UIButton()
private var personalInfoButton = UIButton()

// MARK: - init
init(coordinator: AuthCoordinator) {
Expand Down Expand Up @@ -66,25 +62,15 @@ extension AuthViewController {
$0.textColor = .white
$0.text = I18N.loginMain
$0.numberOfLines = 2

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = $0.font.lineHeight * 0.2

let attributedText = NSAttributedString(string: $0.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
$0.attributedText = attributedText
$0.setLineSpacing(lineSpacing: $0.font.lineHeight * 0.2)
}

loginSubLabel.do {
$0.font = .Pretendard(.medium, size: 16)
$0.textColor = .gray4
$0.text = I18N.loginSub
$0.numberOfLines = 3

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = $0.font.lineHeight * 0.2

let attributedText = NSAttributedString(string: $0.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
$0.attributedText = attributedText
$0.setLineSpacing(lineSpacing: $0.font.lineHeight * 0.2)
}

kakaoLoginImageView.image = .kakaoLoginLabel
Expand All @@ -96,78 +82,48 @@ extension AuthViewController {
$0.setTitle(I18N.moreAuth, for: .normal)
$0.setTitleColor(.gray4, for: .normal)
$0.titleLabel?.font = .Pretendard(.regular, size: 12)
}

conditionButton.do {
$0.setTitle(I18N.condition, for: .normal)
$0.setTitleColor(.gray4, for: .normal)
$0.titleLabel?.font = .Pretendard(.regular, size: 12)
$0.setUnderline()
$0.addTarget(self, action: #selector(moreButtonTapped), for: .touchUpInside)
}

personalInfoButton.do {
$0.setTitle(I18N.personalInfo, for: .normal)
$0.setTitleColor(.gray4, for: .normal)
$0.titleLabel?.font = .Pretendard(.regular, size: 12)
$0.setUnderline()
$0.setUnderlines(target: [I18N.condition, I18N.personalInfo])
$0.addTarget(self, action: #selector(moreButtonTapped), for: .touchUpInside)
}
}

private func setLayout() {

view.addSubviews(loginMainLabel, loginSubLabel, kakaoLoginImageView, kakaoLoginButtonView, appleLoginButtonView, kakaoLoginButton, appleLoginButton, moreButton)
moreButton.addSubviews(conditionButton, personalInfoButton)
view.addSubviews(loginMainLabel, loginSubLabel, kakaoLoginImageView, kakaoLoginButton, appleLoginButton, moreButton)

loginMainLabel.snp.makeConstraints {
$0.top.equalToSuperview().offset(155)
$0.leading.equalToSuperview().offset(29)
$0.top.equalToSuperview().offset(155.adjusted)
$0.leading.equalToSuperview().offset(29.adjusted)
}

loginSubLabel.snp.makeConstraints {
$0.top.equalTo(loginMainLabel.snp.bottom).offset(17)
$0.top.equalTo(loginMainLabel.snp.bottom).offset(17.adjusted)
$0.leading.equalTo(loginMainLabel.snp.leading)
}

moreButton.snp.makeConstraints {
$0.bottom.equalToSuperview().offset(-65)
$0.bottom.equalToSuperview().offset(-65.adjusted)
$0.centerX.equalToSuperview()
}

appleLoginButtonView.snp.makeConstraints {
$0.leading.trailing.equalToSuperview()
$0.bottom.equalTo(moreButton.snp.top).offset(-14)
}

kakaoLoginButtonView.snp.makeConstraints {
$0.leading.trailing.equalToSuperview()
$0.bottom.equalTo(appleLoginButtonView.snp.top).offset(-11)
}

kakaoLoginImageView.snp.makeConstraints {
$0.bottom.equalTo(kakaoLoginButtonView.snp.top).offset(-9)
$0.leading.equalToSuperview().offset(22)
$0.width.equalTo(189)
$0.height.equalTo(37)
}

kakaoLoginButton.snp.makeConstraints {
$0.top.bottom.leading.trailing.equalTo(kakaoLoginButtonView)
}

appleLoginButton.snp.makeConstraints {
$0.top.bottom.leading.trailing.equalTo(appleLoginButtonView)
$0.leading.trailing.equalToSuperview().inset(17.adjusted)
$0.bottom.equalTo(moreButton.snp.top).offset(-14.adjusted)
$0.height.equalTo(53.adjusted)
}

conditionButton.snp.makeConstraints {
$0.centerY.leading.equalToSuperview()
kakaoLoginButton.snp.makeConstraints {
$0.leading.trailing.equalToSuperview().inset(17.adjusted)
$0.bottom.equalTo(appleLoginButton.snp.top).offset(-11.adjusted)
$0.height.equalTo(53.adjusted)
}

personalInfoButton.snp.makeConstraints {
$0.centerY.trailing.equalToSuperview()
kakaoLoginImageView.snp.makeConstraints {
$0.bottom.equalTo(kakaoLoginButton.snp.top).offset(-9.adjusted)
$0.leading.equalToSuperview().offset(22.adjusted)
$0.width.equalTo(189.adjusted)
$0.height.equalTo(37.adjusted)
}

}

// MARK: - @objc Methods
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// AuthViewModel.swift
// iOS-NOTTODO
//
// Created by 강윤서 on 3/20/24.
//

import Foundation
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// AuthViewModelImpl.swift
// iOS-NOTTODO
//
// Created by 강윤서 on 3/20/24.
//

import Foundation
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ extension OnboardingCollectionViewCell {
titleLabel.text = model.title
titleLabel.snp.remakeConstraints {
$0.centerY.equalToSuperview()
$0.leading.equalToSuperview().offset(20)
$0.leading.equalToSuperview().offset(20.adjusted)
}
}
}
Loading