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

QuestView 작성, 버튼 클릭시, AddQuestsView present #25

Merged
merged 12 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 23 additions & 2 deletions DailyQuest/DailyQuest.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
3449AD5B2922164B00B87619 /* Quest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5A2922164B00B87619 /* Quest.swift */; };
3449AD5D2922197000B87619 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5C2922197000B87619 /* User.swift */; };
3449AD6029222B3900B87619 /* UserInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3449AD5F29222B3900B87619 /* UserInfoCell.swift */; };
34874AA0292509A4000570DF /* QuestViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34874A9F292509A4000570DF /* QuestViewHeader.swift */; };
34874AA229250C43000570DF /* UIButton+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34874AA129250C43000570DF /* UIButton+.swift */; };
34874AA629252088000570DF /* AddQuestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34874AA529252088000570DF /* AddQuestsViewController.swift */; };
349955122923220E007AB99E /* SwiftUIPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349955112923220E007AB99E /* SwiftUIPreview.swift */; };
3499551529232533007AB99E /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3499551429232533007AB99E /* UIColor+.swift */; };
3499551E29233DEF007AB99E /* QuestCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3499551D29233DEF007AB99E /* QuestCell.swift */; };
Expand Down Expand Up @@ -45,6 +48,8 @@
34ACC364291DEF6100741371 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 34ACC363291DEF6100741371 /* FirebaseFirestore */; };
34ACC366291DEF6100741371 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 34ACC365291DEF6100741371 /* FirebaseStorage */; };
34ACC36C291DF0DD00741371 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 34ACC36B291DF0DD00741371 /* GoogleService-Info.plist */; };
34EE6EB72924C674005AF583 /* QuestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EE6EB62924C674005AF583 /* QuestView.swift */; };
34EE6EB92924CAA1005AF583 /* QuestViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */; };
A51189C329226E66008A9D33 /* UserQuestEntity+Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51189C229226E66008A9D33 /* UserQuestEntity+Mapping.swift */; };
A51F01C82923392F0031ECA2 /* UserInfoStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51F01C72923392F0031ECA2 /* UserInfoStorage.swift */; };
A51F01CA2923397E0031ECA2 /* UserInfoEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A51F01C92923397E0031ECA2 /* UserInfoEntity.swift */; };
Expand Down Expand Up @@ -85,6 +90,9 @@
3449AD5A2922164B00B87619 /* Quest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quest.swift; sourceTree = "<group>"; };
3449AD5C2922197000B87619 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = "<group>"; };
3449AD5F29222B3900B87619 /* UserInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoCell.swift; sourceTree = "<group>"; };
34874A9F292509A4000570DF /* QuestViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestViewHeader.swift; sourceTree = "<group>"; };
34874AA129250C43000570DF /* UIButton+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+.swift"; sourceTree = "<group>"; };
34874AA529252088000570DF /* AddQuestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddQuestsViewController.swift; sourceTree = "<group>"; };
349955112923220E007AB99E /* SwiftUIPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPreview.swift; sourceTree = "<group>"; };
3499551429232533007AB99E /* UIColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = "<group>"; };
3499551D29233DEF007AB99E /* QuestCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestCell.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -117,6 +125,8 @@
34ACC34D291DE9C100741371 /* DailyQuestUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyQuestUITests.swift; sourceTree = "<group>"; };
34ACC34F291DE9C100741371 /* DailyQuestUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyQuestUITestsLaunchTests.swift; sourceTree = "<group>"; };
34ACC36B291DF0DD00741371 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
34EE6EB62924C674005AF583 /* QuestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestView.swift; sourceTree = "<group>"; };
34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuestViewModel.swift; sourceTree = "<group>"; };
A51189C229226E66008A9D33 /* UserQuestEntity+Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserQuestEntity+Mapping.swift"; sourceTree = "<group>"; };
A51F01C72923392F0031ECA2 /* UserInfoStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoStorage.swift; sourceTree = "<group>"; };
A51F01C92923397E0031ECA2 /* UserInfoEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoEntity.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -192,6 +202,7 @@
children = (
349955112923220E007AB99E /* SwiftUIPreview.swift */,
3499551429232533007AB99E /* UIColor+.swift */,
34874AA129250C43000570DF /* UIButton+.swift */,
);
path = Utils;
sourceTree = "<group>";
Expand Down Expand Up @@ -232,6 +243,7 @@
34995519292332A9007AB99E /* Home */ = {
isa = PBXGroup;
children = (
34EE6EB52924C657005AF583 /* View */,
34A529DE29247F13001BAD34 /* ViewModel */,
34A529DD29247F0C001BAD34 /* ViewController */,
34A529D629247A52001BAD34 /* Flow */,
Expand Down Expand Up @@ -306,13 +318,15 @@
isa = PBXGroup;
children = (
34A529DF29247F1F001BAD34 /* HomeViewController.swift */,
34874AA529252088000570DF /* AddQuestsViewController.swift */,
);
path = ViewController;
sourceTree = "<group>";
};
34A529DE29247F13001BAD34 /* ViewModel */ = {
isa = PBXGroup;
children = (
34EE6EB82924CAA1005AF583 /* QuestViewModel.swift */,
);
path = ViewModel;
sourceTree = "<group>";
Expand Down Expand Up @@ -421,10 +435,12 @@
path = Data;
sourceTree = "<group>";
};
34EE6EB12924B3CF005AF583 /* View */ = {
34EE6EB52924C657005AF583 /* View */ = {
isa = PBXGroup;
children = (
34A529EA2924B077001BAD34 /* UserInfoView.swift */,
34EE6EB62924C674005AF583 /* QuestView.swift */,
34874A9F292509A4000570DF /* QuestViewHeader.swift */,
34A529EA2924B077001BAD34 /* UserInfoView.swift */,
);
path = View;
sourceTree = "<group>";
Expand Down Expand Up @@ -707,8 +723,10 @@
3499552329234D5F007AB99E /* BrowseCell.swift in Sources */,
3499551E29233DEF007AB99E /* QuestCell.swift in Sources */,
34A529DC29247BB6001BAD34 /* HomeSceneDIContainer.swift in Sources */,
34874AA229250C43000570DF /* UIButton+.swift in Sources */,
34A529E7292481E1001BAD34 /* BrowseCoordinator.swift in Sources */,
34A529D329247903001BAD34 /* TabCoordinator.swift in Sources */,
34EE6EB92924CAA1005AF583 /* QuestViewModel.swift in Sources */,
3449AD5D2922197000B87619 /* User.swift in Sources */,
A51F01CD29233ABB0031ECA2 /* RealmUserInfoStorage.swift in Sources */,
A51F01DA292345990031ECA2 /* BrowseQuest.swift in Sources */,
Expand All @@ -720,14 +738,17 @@
34A529D529247932001BAD34 /* AppCoordinator.swift in Sources */,
B50078D629222F3F0070AFC4 /* CircleCheckView.swift in Sources */,
3499552729235D1E007AB99E /* BrowseItemViewModel.swift in Sources */,
34874AA629252088000570DF /* AddQuestsViewController.swift in Sources */,
349955292923600A007AB99E /* BrowseViewController.swift in Sources */,
34EE6EB72924C674005AF583 /* QuestView.swift in Sources */,
B58DFC0A29227DA800C68A4B /* CalendarCell.swift in Sources */,
3499551529232533007AB99E /* UIColor+.swift in Sources */,
34A529DA29247B9C001BAD34 /* AppDIContainer.swift in Sources */,
34ACC32D291DE9C000741371 /* AppDelegate.swift in Sources */,
A5AC96D929223648003B7637 /* RealmStorage.swift in Sources */,
A51189C329226E66008A9D33 /* UserQuestEntity+Mapping.swift in Sources */,
A51F01D3292340360031ECA2 /* BrowseQuestsStorage.swift in Sources */,
34874AA0292509A4000570DF /* QuestViewHeader.swift in Sources */,
34A529EB2924B077001BAD34 /* UserInfoView.swift in Sources */,
A51F01CA2923397E0031ECA2 /* UserInfoEntity.swift in Sources */,
A51F01D8292343A80031ECA2 /* RealmBrowseQuestsStorage.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@

import UIKit

import RxSwift

protocol HomeCoordinator: Coordinator {
func showProfileFlow()
func showAddQuestFlow()
func showAddFriendsFlow()
}

final class DefaultHomeCoordinator: HomeCoordinator {
private var disposableBag = DisposeBag()

var finishDelegate: CoordinatorFinishDelegate?
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
Expand All @@ -28,14 +32,25 @@ final class DefaultHomeCoordinator: HomeCoordinator {
func start() {
let homeViewController = homeSceneDIContainer.makeHomeViewController()
navigationController.pushViewController(homeViewController, animated: false)

homeViewController
.coordinatorPublisher
.bind(onNext: { [weak self] event in
switch event {
case .showAddQuestsFlow:
self?.showAddQuestFlow()
}
})
.disposed(by: disposableBag)
}

func showProfileFlow() {

}

func showAddQuestFlow() {

let addQuestsViewController = AddQuestsViewController()
navigationController.present(addQuestsViewController, animated: true)
}

func showAddFriendsFlow() {
Expand Down
69 changes: 69 additions & 0 deletions DailyQuest/DailyQuest/Presentation/Home/View/QuestView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//
// QuestView.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/11/16.
//

import UIKit

import RxSwift
import RxCocoa

final class QuestView: UITableView {
private var viewModel: QuestViewModel!
private var disposableBag = DisposeBag()

override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)

rowHeight = 75

register(QuestCell.self, forCellReuseIdentifier: QuestCell.reuseIdentifier)
}

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

func setup(with viewModel: QuestViewModel) {
self.viewModel = viewModel

bind()
}

private func bind() {
viewModel
.data
.bind(to: rx.items(cellIdentifier: QuestCell.reuseIdentifier, cellType: QuestCell.self)) { row, item, cell in
cell.setup(with: item)
cell.backgroundColor = .white
}
.disposed(by: disposableBag)
}
}

/**
`QuestView`의 델리게이트 역할을 수행할 클래스입니다.
생성자를 통해 header 정보를 받는 이유는, `func tableView(_:viewForHeaderInSection)`메서드에서 헤더를 지정해주기 때문입니다.
*/
final class QuestViewDelegate: NSObject, UITableViewDelegate {
private let header: QuestViewHeader

init(header: QuestViewHeader) {
self.header = header
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 75
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return header
}

deinit {
print("deinit")
}
}

65 changes: 65 additions & 0 deletions DailyQuest/DailyQuest/Presentation/Home/View/QuestViewHeader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// QuestViewHeader.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/11/16.
//

import UIKit

import RxSwift
import RxCocoa
import SnapKit

final class QuestViewHeader: UIStackView {
private var disposableBag = DisposeBag()
var buttonDidClick = PublishSubject<Void>()

// MARK: - Components
private lazy var titleLabel: UILabel = {
let titleLabel = UILabel()
titleLabel.text = "Today Quest"
titleLabel.textColor = .maxViolet
titleLabel.font = UIFont.boldSystemFont(ofSize: 32)

return titleLabel
}()

private lazy var plusButton: UIButton = {
var config = UIButton.Configuration.maxStyle()
let plusButton = UIButton(configuration: config)

return plusButton
}()

// MARK: - Methods
convenience init() {
self.init(frame: .zero)

axis = .horizontal
alignment = .center
distribution = .equalSpacing
isLayoutMarginsRelativeArrangement = true
layoutMargins = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)

configureUI()
bind()
}

private func configureUI() {
addArrangedSubview(titleLabel)
addArrangedSubview(plusButton)
}

/**
버튼이 클릭되면 `buttonDidClick` subject가 이벤트를 방출합니다.
눌려졌다는 정보만 있으면 되므로, output type은 Void입니다.
*/
private func bind() {
plusButton.rx.tap
.subscribe(onNext: { [weak self] _ in
self?.buttonDidClick.onNext(())
})
.disposed(by: disposableBag)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// AddQuestsViewController.swift
// DailyQuest
//
// Created by jinwoong Kim on 2022/11/16.
//

import UIKit

import SnapKit

final class AddQuestsViewController: UIViewController {

private lazy var indicateMessage: UILabel = {
let indicateMessage = UILabel()
indicateMessage.text = "여기서 새로운 퀘스트를 추가합니다."

return indicateMessage
}()

override func viewDidLoad() {
super.viewDidLoad()

view.backgroundColor = .systemGray

configureUI()
}

private func configureUI() {
view.addSubview(indicateMessage)

indicateMessage.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,30 @@

import UIKit

import RxSwift
import RxCocoa
import SnapKit

final class HomeViewController: UIViewController {
enum Event {
case showAddQuestsFlow
}

var coordinatorPublisher = PublishSubject<Event>()

private var disposableBag = DisposeBag()
private var questViewDelegate: QuestViewDelegate?

private lazy var questView: QuestView = {
let questView = QuestView()
questView.setup(with: QuestViewModel())

return questView
}()

private lazy var questViewHeader: QuestViewHeader = {
return QuestViewHeader()
}()

// MARK: - Life Cycle
// static func create(with viewModel: HomeViewModel)
Expand All @@ -18,6 +40,33 @@ final class HomeViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
questViewDelegate = QuestViewDelegate(header: questViewHeader)

view.backgroundColor = .white

view.addSubview(questView)

questView.delegate = questViewDelegate
questView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}

bind()
}

private func bind() {
/**
Header에서 버튼이 눌려졌는지를 수신하고 있습니다.
버튼이 눌러지면 bind내의 클로저가 실행되고, 이는 다시 coordinatorPublisher가 이벤트를 방출하게 합니다.
이 버튼이 눌러졌을 때에는 퀘스트를 추가하는 화면이 띄워져야 하므로, 이벤트의 종류는 .`showAddQuestsFlow`입니다.
Note. Combine에서는 `assign(to:)` 오퍼레이터 메서드를 사용하면 이 위치에서 cancellable을 반환하지 않고(rx에서는 disposable)
이벤트를 연계해줄 수 있지만, rx에서는 동일한 역할을 해주는 오퍼레이터가 없어서 disposableBag에 `Disposable`을 담아주는 것에 유념해주세요.
*/
questViewHeader
.buttonDidClick
.bind(onNext: { [weak self] _ in
self?.coordinatorPublisher.onNext(.showAddQuestsFlow)
})
.disposed(by: disposableBag)
}
}
Loading