Skip to content

Commit

Permalink
✨ Feat: #27 - 이용약관 페이지 UI구성 및 action바인딩 (#30)
Browse files Browse the repository at this point in the history
* ✨ Feat: #4 - touchArea의 style을 외부에서도 조절 가능하게...

* ✨ Feat: #24 - progressPager의 contentOffset을 0을 기준으로 조정

* ✨ Feat: #27 - 이용약관 페이지 UI구성 및 action바인딩

- init할 때, pager자체의 view의 레이아웃 warning뜨는 부분 잡아야함....
  • Loading branch information
usa4060 committed Oct 28, 2023
1 parent 4c187af commit 05acfc0
Show file tree
Hide file tree
Showing 7 changed files with 491 additions and 41 deletions.
12 changes: 6 additions & 6 deletions CMC/Sources/Presenter/Auth/Coordinators/AuthCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ class AuthCoordinator: CoordinatorType {
}
case .signUp:
let signUpViewController = SignUpViewController(
// viewModel: EmailSignUpViewModel(
// coordinator: self,
// userSignUpUsecase: DefaultUserSignUpUsecase(
// userRepository: DefaultUserRepository()
// )
// )
viewModel: SignUpViewModel(
coordinator: self,
authUsecase: DefaultAuthUsecase(
authRepository: DefaultAuthRepository()
)
)
)
if self.navigationController.viewControllers.contains(where: {$0 is SignUpViewController}) {
self.navigationController.popViewController(animated: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,266 @@
// Created by Siri on 10/28/23.
// Copyright © 2023 com.centralMakeusChallenge. All rights reserved.
//

import Foundation

import RxCocoa
import RxGesture
import RxSwift

import DesignSystem
import SnapKit

import UIKit

final class TermsAndConditionsView: BaseView {
// MARK: - UI

private lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = "약관동의"
label.font = DesignSystemFontFamily.Pretendard.bold.font(size: 26)
label.textColor = DesignSystemAsset.gray50.color
return label
}()

private lazy var buttonStackViews: [UIStackView] = {
var stackViews: [UIStackView] = []
buttons.enumerated().forEach { index, button in
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .center
stackView.addArrangedSubview(button)
stackView.addArrangedSubview(buttonLabels[index])
stackViews.append(stackView)
}
return stackViews
}()

private lazy var buttons: [CMCTouchArea] = {
let selectedImage = CMCAsset._24x24abled.image
let normalImage = CMCAsset._24x24disabled.image
let buttons = [
CMCTouchArea(image: normalImage),
CMCTouchArea(image: normalImage),
CMCTouchArea(image: normalImage),
CMCTouchArea(image: normalImage),
CMCTouchArea(image: normalImage)
]
buttons.forEach{ $0.setImage(selectedImage, for: .selected)}
return buttons
}()

private lazy var accessoryDetailButton : [CMCTouchArea] = {
let image = CMCAsset._16x16arrowLeft.image
let buttons = [
CMCTouchArea(image: image),
CMCTouchArea(image: image),
CMCTouchArea(image: image),
CMCTouchArea(image: image)
]
return buttons
}()

private lazy var buttonLabels: [UILabel] = {
var labels: [UILabel] = []
let texts = [
"전체 동의",
"서비스 이용약관(필수)",
"개인정보 수집/이용 (필수)",
"위치정보 이용 동의(선택)",
"마케팅 정보 수신 동의(선택)"
]
texts.enumerated().forEach { index, text in
let label = UILabel()
if index == 0 {
label.font = CMCFontFamily.Pretendard.bold.font(size: 18)
} else {
label.font = CMCFontFamily.Pretendard.bold.font(size: 14)
}
label.textColor = CMCAsset.gray50.color
label.text = text
labels.append(label)
}
return labels
}()

private lazy var separeteBar: UIView = {
let view = UIView()
view.backgroundColor = DesignSystemAsset.gray800.color
return view
}()

private lazy var rowStackViews: [UIStackView] = {
var stackViews: [UIStackView] = []
accessoryDetailButton.enumerated().forEach { index, button in
let stackView = UIStackView()
stackView.addArrangedSubview(buttonStackViews[index+1])
stackView.addArrangedSubview(button)
stackView.axis = .horizontal
stackViews.append(stackView)
}
return stackViews
}()

private lazy var nextButton: CMCButton = {
let button = CMCButton(
isRound: false,
iconTitle: nil,
type: .login(.disabled),
title: "다음")
return button
}()

// MARK: - Properties
private var viewModel: TermsAndConditionsViewModel

// MARK: - Initializers
init(
viewModel: TermsAndConditionsViewModel
) {
self.viewModel = viewModel
super.init(frame: .zero)
self.backgroundColor = CMCAsset.background.color
}


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

// MARK: - LifeCycle
// MARK: - Methods

override func setAddSubView() {
self.addSubview(titleLabel)
self.addSubview(buttonStackViews[0])
self.addSubview(separeteBar)
self.addSubview(rowStackViews[0])
self.addSubview(rowStackViews[1])
self.addSubview(rowStackViews[2])
self.addSubview(rowStackViews[3])
self.addSubview(nextButton)
}

override func setConstraint() {
titleLabel.snp.makeConstraints{ make in
make.top.equalToSuperview().offset(30)
make.leading.equalToSuperview().offset(24)
}

buttons.forEach { button in
button.snp.makeConstraints { make in
make.width.height.equalTo(44)
}
}

accessoryDetailButton.forEach { button in
button.snp.makeConstraints { make in
make.width.height.equalTo(44)
}
}

buttonStackViews[0].snp.makeConstraints{ make in
make.top.equalTo(titleLabel.snp.bottom).offset(30)
make.leading.equalToSuperview().offset(14)
}

separeteBar.snp.makeConstraints{ make in
make.top.equalTo(buttonStackViews[0].snp.bottom).offset(10)
make.leading.trailing.equalToSuperview()
make.height.equalTo(1)
}

rowStackViews[0].snp.makeConstraints { make in
make.top.equalTo(separeteBar.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(14)
make.trailing.equalToSuperview().offset(-5)
}

rowStackViews[1].snp.makeConstraints { make in
make.top.equalTo(rowStackViews[0].snp.bottom).offset(2)
make.leading.trailing.equalTo(rowStackViews[0])
}

rowStackViews[2].snp.makeConstraints { make in
make.top.equalTo(rowStackViews[1].snp.bottom).offset(2)
make.leading.trailing.equalTo(rowStackViews[0])
}

rowStackViews[3].snp.makeConstraints { make in
make.top.equalTo(rowStackViews[2].snp.bottom).offset(2)
make.leading.trailing.equalTo(rowStackViews[0])
}

nextButton.snp.makeConstraints { make in
make.bottom.equalTo(self.safeAreaLayoutGuide.snp.bottom).offset(-20)
make.leading.equalToSuperview().offset(14)
make.trailing.equalToSuperview().offset(-14)
make.height.equalTo(50)
}

}

override func bind() {

let input = TermsAndConditionsViewModel.Input(
allAgreeBtnTapped: buttons[0].rx.tapped().asObservable(),
termsBtnTapped: buttons[1].rx.tapped().asObservable(),
conditionBtnTapped: buttons[2].rx.tapped().asObservable(),
locateBtnTapped: buttons[3].rx.tapped().asObservable(),
eventBtnTapped: buttons[4].rx.tapped().asObservable(),

nextBtnTapped: nextButton.rx.tap.asObservable()
)

let output = viewModel.transform(input: input)

output.allAgreeBtnState
.observe(on: MainScheduler.instance)
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.buttons[0].makeCustomState(type: state ? .selected : .normal)
})
.disposed(by: disposeBag)

output.termsBtnState
.observe(on: MainScheduler.instance)
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.buttons[1].makeCustomState(type: state ? .selected : .normal)
})
.disposed(by: disposeBag)

output.conditionBtnState
.observe(on: MainScheduler.instance)
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.buttons[2].makeCustomState(type: state ? .selected : .normal)
})
.disposed(by: disposeBag)

output.locateBtnState
.observe(on: MainScheduler.instance)
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.buttons[3].makeCustomState(type: state ? .selected : .normal)
})
.disposed(by: disposeBag)

output.eventBtnState
.observe(on: MainScheduler.instance)
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.buttons[4].makeCustomState(type: state ? .selected : .normal)
})
.disposed(by: disposeBag)

output.nextButtonState
.withUnretained(self)
.subscribe(onNext: { owner, state in
owner.nextButton.rxType.accept(state ? .login(.inactive) : .login(.disabled))
})
.disposed(by: disposeBag)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,106 @@
//

import Foundation

import RxCocoa
import RxSwift

import UIKit

final class TermsAndConditionsViewModel: ViewModelType {

struct Input {
let allAgreeBtnTapped: Observable<Void>
let termsBtnTapped: Observable<Void>
let conditionBtnTapped: Observable<Void>
let locateBtnTapped: Observable<Void>
let eventBtnTapped: Observable<Void>

let nextBtnTapped: Observable<Void>
}

struct Output {
let allAgreeBtnState: Observable<Bool>
let termsBtnState: Observable<Bool>
let conditionBtnState: Observable<Bool>
let locateBtnState: Observable<Bool>
let eventBtnState: Observable<Bool>

let nextButtonState: Observable<Bool>
}

var disposeBag: DisposeBag = DisposeBag()
var parentViewModel: SignUpViewModel

init(
parentViewModel: SignUpViewModel
) {
self.parentViewModel = parentViewModel
}

private var allAgreeBtnRelay = BehaviorRelay<Bool>(value: false)
private var termsBtnRelay = BehaviorRelay<Bool>(value: false)
private var conditionBtnRelay = BehaviorRelay<Bool>(value: false)
private var locateBtnRelay = BehaviorRelay<Bool>(value: false)
private var eventBtnRelay = BehaviorRelay<Bool>(value: false)

func transform(input: Input) -> Output {
input.termsBtnTapped.bind { [unowned self] in
termsBtnRelay.accept(!termsBtnRelay.value)
updateAllAgreeState()
}.disposed(by: disposeBag)

input.conditionBtnTapped.bind { [unowned self] in
conditionBtnRelay.accept(!conditionBtnRelay.value)
updateAllAgreeState()
}.disposed(by: disposeBag)

input.locateBtnTapped.bind { [unowned self] in
locateBtnRelay.accept(!locateBtnRelay.value)
updateAllAgreeState()
}.disposed(by: disposeBag)

input.eventBtnTapped.bind { [unowned self] in
eventBtnRelay.accept(!eventBtnRelay.value)
updateAllAgreeState()
}.disposed(by: disposeBag)

input.allAgreeBtnTapped.bind { [unowned self] in
let newState = !allAgreeBtnRelay.value
allAgreeBtnRelay.accept(newState)
termsBtnRelay.accept(newState)
conditionBtnRelay.accept(newState)
locateBtnRelay.accept(newState)
eventBtnRelay.accept(newState)
}.disposed(by: disposeBag)

input.nextBtnTapped
.withUnretained(self)
.subscribe(onNext: { owner, _ in
owner.parentViewModel.nextBtnTapped.accept(())
})
.disposed(by: disposeBag)

let moveToNext = Observable.combineLatest(termsBtnRelay, conditionBtnRelay)
.map { $0 && $1 }

return Output(
allAgreeBtnState: allAgreeBtnRelay.asObservable(),
termsBtnState: termsBtnRelay.asObservable(),
conditionBtnState: conditionBtnRelay.asObservable(),
locateBtnState: locateBtnRelay.asObservable(),
eventBtnState: eventBtnRelay.asObservable(),
nextButtonState: moveToNext.asObservable()
)
}

}

// MARK: - Private Methods
extension TermsAndConditionsViewModel {

private func updateAllAgreeState() {
let allSelected = termsBtnRelay.value && conditionBtnRelay.value && locateBtnRelay.value && eventBtnRelay.value
allAgreeBtnRelay.accept(allSelected)
}
}
Loading

0 comments on commit 05acfc0

Please sign in to comment.