From 3139043c4073567d1f00fc29bc48701a67404530 Mon Sep 17 00:00:00 2001 From: Yoonseo Kim Date: Fri, 24 Sep 2021 13:15:33 +0900 Subject: [PATCH] =?UTF-8?q?[#4]=20dynamic=20=E1=84=8C=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=8B=E1=85=AD=E1=86=BC=E1=84=8C=E1=85=AE=E1=86=BC,=20?= =?UTF-8?q?=E1=84=8B=E1=85=B5=E1=86=B7=E1=84=89=E1=85=B5=20=E1=84=8F?= =?UTF-8?q?=E1=85=A5=E1=84=86=E1=85=B5=E1=86=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CheoMooRac.xcodeproj/project.pbxproj | 54 +++++++++++++--- .../Storyboards/Base.lproj/Main.storyboard | 7 +-- .../{Manager => Managers}/StringManager.swift | 0 .../CheoMooRac/Sources/Models/Person.swift | 12 ++++ ...troller.swift => MainViewController.swift} | 47 +++++++++----- .../Sources/ViewModels/Dynamic.swift | 32 ++++++++++ .../Sources/ViewModels/MainViewModel.swift | 62 +++++++++++++++++++ .../Views/MainTableViewController.swift | 23 +++++++ 8 files changed, 212 insertions(+), 25 deletions(-) rename CheoMooRac/CheoMooRac/Sources/{Manager => Managers}/StringManager.swift (100%) create mode 100644 CheoMooRac/CheoMooRac/Sources/Models/Person.swift rename CheoMooRac/CheoMooRac/Sources/ViewControllers/{ViewController.swift => MainViewController.swift} (82%) create mode 100644 CheoMooRac/CheoMooRac/Sources/ViewModels/Dynamic.swift create mode 100644 CheoMooRac/CheoMooRac/Sources/ViewModels/MainViewModel.swift create mode 100644 CheoMooRac/CheoMooRac/Sources/Views/MainTableViewController.swift diff --git a/CheoMooRac/CheoMooRac.xcodeproj/project.pbxproj b/CheoMooRac/CheoMooRac.xcodeproj/project.pbxproj index 74fd1fa..d0c057b 100644 --- a/CheoMooRac/CheoMooRac.xcodeproj/project.pbxproj +++ b/CheoMooRac/CheoMooRac.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ DD2844A626EF35C4004FC840 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2844A526EF35C4004FC840 /* AppDelegate.swift */; }; DD2844A826EF35C4004FC840 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2844A726EF35C4004FC840 /* SceneDelegate.swift */; }; - DD2844AA26EF35C4004FC840 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2844A926EF35C4004FC840 /* ViewController.swift */; }; + DD2844AA26EF35C4004FC840 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2844A926EF35C4004FC840 /* MainViewController.swift */; }; DD2844AD26EF35C4004FC840 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD2844AB26EF35C4004FC840 /* Main.storyboard */; }; DD2844B026EF35C5004FC840 /* CheoMooRac.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD2844AE26EF35C5004FC840 /* CheoMooRac.xcdatamodeld */; }; DD2844B226EF35C7004FC840 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD2844B126EF35C7004FC840 /* Assets.xcassets */; }; @@ -28,6 +28,10 @@ DD6CCCED26F081C600C7BA2C /* MyCardTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6CCCEC26F081C600C7BA2C /* MyCardTableViewCell.swift */; }; DD6CCCEF26F08EB200C7BA2C /* CreateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6CCCEE26F08EB200C7BA2C /* CreateViewController.swift */; }; DD868E7026FB56D700D5BE38 /* StringManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD868E6F26FB56D700D5BE38 /* StringManager.swift */; }; + DD8A91E226FD7AFB004DAF3B /* Dynamic.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8A91E126FD7AFB004DAF3B /* Dynamic.swift */; }; + DD8A91E526FD7B6C004DAF3B /* MainTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8A91E426FD7B6C004DAF3B /* MainTableViewController.swift */; }; + DD96AA7126FCC44A00C9AA43 /* Person.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD96AA7026FCC44A00C9AA43 /* Person.swift */; }; + DD96AA7426FCC61200C9AA43 /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD96AA7326FCC61200C9AA43 /* MainViewModel.swift */; }; DDC49F1C26F8A829001921C1 /* ReadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC49F1B26F8A829001921C1 /* ReadViewController.swift */; }; /* End PBXBuildFile section */ @@ -35,7 +39,7 @@ DD2844A226EF35C4004FC840 /* CheoMooRac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CheoMooRac.app; sourceTree = BUILT_PRODUCTS_DIR; }; DD2844A526EF35C4004FC840 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; DD2844A726EF35C4004FC840 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - DD2844A926EF35C4004FC840 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + DD2844A926EF35C4004FC840 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; DD2844AC26EF35C4004FC840 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; DD2844AF26EF35C5004FC840 /* CheoMooRac.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CheoMooRac.xcdatamodel; sourceTree = ""; }; DD2844B126EF35C7004FC840 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -50,6 +54,10 @@ DD6CCCEC26F081C600C7BA2C /* MyCardTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCardTableViewCell.swift; sourceTree = ""; }; DD6CCCEE26F08EB200C7BA2C /* CreateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateViewController.swift; sourceTree = ""; }; DD868E6F26FB56D700D5BE38 /* StringManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringManager.swift; sourceTree = ""; }; + DD8A91E126FD7AFB004DAF3B /* Dynamic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dynamic.swift; sourceTree = ""; }; + DD8A91E426FD7B6C004DAF3B /* MainTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTableViewController.swift; sourceTree = ""; }; + DD96AA7026FCC44A00C9AA43 /* Person.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Person.swift; sourceTree = ""; }; + DD96AA7326FCC61200C9AA43 /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; DDC49F1B26F8A829001921C1 /* ReadViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -108,7 +116,10 @@ DD2844BD26EF35EB004FC840 /* Sources */ = { isa = PBXGroup; children = ( - DD868E6E26FB56C900D5BE38 /* Manager */, + DD8A91E326FD7B3B004DAF3B /* Views */, + DD96AA7226FCC5EA00C9AA43 /* ViewModels */, + DD96AA6F26FCC43A00C9AA43 /* Models */, + DD868E6E26FB56C900D5BE38 /* Managers */, DD372A5B26F0EE2C0033D40A /* Headers */, DD6CCCEB26F081A700C7BA2C /* Cells */, DD6CCCE426F080A200C7BA2C /* Exensions */, @@ -148,7 +159,7 @@ isa = PBXGroup; children = ( DDC49F1B26F8A829001921C1 /* ReadViewController.swift */, - DD2844A926EF35C4004FC840 /* ViewController.swift */, + DD2844A926EF35C4004FC840 /* MainViewController.swift */, DD6CCCEE26F08EB200C7BA2C /* CreateViewController.swift */, ); path = ViewControllers; @@ -182,12 +193,37 @@ path = Cells; sourceTree = ""; }; - DD868E6E26FB56C900D5BE38 /* Manager */ = { + DD868E6E26FB56C900D5BE38 /* Managers */ = { isa = PBXGroup; children = ( DD868E6F26FB56D700D5BE38 /* StringManager.swift */, ); - path = Manager; + path = Managers; + sourceTree = ""; + }; + DD8A91E326FD7B3B004DAF3B /* Views */ = { + isa = PBXGroup; + children = ( + DD8A91E426FD7B6C004DAF3B /* MainTableViewController.swift */, + ); + path = Views; + sourceTree = ""; + }; + DD96AA6F26FCC43A00C9AA43 /* Models */ = { + isa = PBXGroup; + children = ( + DD96AA7026FCC44A00C9AA43 /* Person.swift */, + ); + path = Models; + sourceTree = ""; + }; + DD96AA7226FCC5EA00C9AA43 /* ViewModels */ = { + isa = PBXGroup; + children = ( + DD96AA7326FCC61200C9AA43 /* MainViewModel.swift */, + DD8A91E126FD7AFB004DAF3B /* Dynamic.swift */, + ); + path = ViewModels; sourceTree = ""; }; /* End PBXGroup section */ @@ -272,8 +308,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DD8A91E526FD7B6C004DAF3B /* MainTableViewController.swift in Sources */, + DD96AA7126FCC44A00C9AA43 /* Person.swift in Sources */, DD6CCCE826F0811600C7BA2C /* UIView+addSubviews.swift in Sources */, - DD2844AA26EF35C4004FC840 /* ViewController.swift in Sources */, + DD2844AA26EF35C4004FC840 /* MainViewController.swift in Sources */, DD372A5D26F0EEAF0033D40A /* ProfileHeaderView.swift in Sources */, DD6CCCEF26F08EB200C7BA2C /* CreateViewController.swift in Sources */, DD868E7026FB56D700D5BE38 /* StringManager.swift in Sources */, @@ -281,11 +319,13 @@ DD2844A626EF35C4004FC840 /* AppDelegate.swift in Sources */, DD6CCCEA26F0814300C7BA2C /* UIStackView+addArrangedSubviews.swift in Sources */, DDC49F1C26F8A829001921C1 /* ReadViewController.swift in Sources */, + DD8A91E226FD7AFB004DAF3B /* Dynamic.swift in Sources */, DD2844A826EF35C4004FC840 /* SceneDelegate.swift in Sources */, DD2844B026EF35C5004FC840 /* CheoMooRac.xcdatamodeld in Sources */, DD6CCCE626F080F300C7BA2C /* UITableView+Generic.swift in Sources */, DD6CCCED26F081C600C7BA2C /* MyCardTableViewCell.swift in Sources */, DD372A5A26F0EBC30033D40A /* TextFieldTableViewCell.swift in Sources */, + DD96AA7426FCC61200C9AA43 /* MainViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/CheoMooRac/CheoMooRac/Resources/Storyboards/Base.lproj/Main.storyboard b/CheoMooRac/CheoMooRac/Resources/Storyboards/Base.lproj/Main.storyboard index 1324588..8a8b88a 100644 --- a/CheoMooRac/CheoMooRac/Resources/Storyboards/Base.lproj/Main.storyboard +++ b/CheoMooRac/CheoMooRac/Resources/Storyboards/Base.lproj/Main.storyboard @@ -1,6 +1,5 @@ - @@ -8,12 +7,12 @@ - + - + - + diff --git a/CheoMooRac/CheoMooRac/Sources/Manager/StringManager.swift b/CheoMooRac/CheoMooRac/Sources/Managers/StringManager.swift similarity index 100% rename from CheoMooRac/CheoMooRac/Sources/Manager/StringManager.swift rename to CheoMooRac/CheoMooRac/Sources/Managers/StringManager.swift diff --git a/CheoMooRac/CheoMooRac/Sources/Models/Person.swift b/CheoMooRac/CheoMooRac/Sources/Models/Person.swift new file mode 100644 index 0000000..48314ad --- /dev/null +++ b/CheoMooRac/CheoMooRac/Sources/Models/Person.swift @@ -0,0 +1,12 @@ +// +// Person.swift +// CheoMooRac +// +// Created by 김윤서 on 2021/09/23. +// + +struct Person { + var firstName: String + var familyName: String + var phoneNumber: String +} diff --git a/CheoMooRac/CheoMooRac/Sources/ViewControllers/ViewController.swift b/CheoMooRac/CheoMooRac/Sources/ViewControllers/MainViewController.swift similarity index 82% rename from CheoMooRac/CheoMooRac/Sources/ViewControllers/ViewController.swift rename to CheoMooRac/CheoMooRac/Sources/ViewControllers/MainViewController.swift index 8ae6792..6d15c2e 100644 --- a/CheoMooRac/CheoMooRac/Sources/ViewControllers/ViewController.swift +++ b/CheoMooRac/CheoMooRac/Sources/ViewControllers/MainViewController.swift @@ -11,13 +11,15 @@ import UIKit import SnapKit import Then -class ViewController: UIViewController { +class MainViewController: UIViewController { private let tableView = UITableView() private var filteredList: [String] = [] private var arr = ["김윤서", "김루희", "윤예지", "김혜수", "코코", "민재", "잼권이", "리헤이", "노제", "몬익화", "립제이", "잘린이", "엠마", "모아나", "케이데이", "가비", "시미즈zz", "강호동", "이수근", "유재석", "리정" ] + private var viewModel: MainViewModelProtocol = MainViewModel() + private var isFiltering: Bool { let searchController = self.navigationItem.searchController let isActive = searchController?.isActive ?? false @@ -27,7 +29,7 @@ class ViewController: UIViewController { private var sectionHeaderList: [String] { var sectionHeaderList: [String] = [] - arr = arr.sorted() + arr.forEach { name in sectionHeaderList.append(StringManager.shared.chosungCheck(word: name)) } @@ -36,7 +38,16 @@ class ViewController: UIViewController { } private var filterdHeaderList: [String] = [] - + +// init(with viewModel: MainViewModel) { +// self.viewModel = viewModel +// super.init(nibName: nil, bundle: nil) +// } +// +// required init?(coder: NSCoder) { +// fatalError("init(coder:) has not been implemented") +// } +// override func viewDidLoad() { super.viewDidLoad() initViewController() @@ -50,6 +61,13 @@ class ViewController: UIViewController { super.viewWillAppear(animated) setSearchController() } + + private func bindViewModel() { + viewModel.list.bind { [weak self] _ in + guard let self = self else {return} + self.tableView.reloadData() + } + } private func initViewController() { title = "연락처" @@ -68,7 +86,7 @@ class ViewController: UIViewController { navigationItem.title = "연락처" navigationItem.hidesSearchBarWhenScrolling = false navigationController?.navigationBar.prefersLargeTitles = true - searchController.obscuresBackgroundDuringPresentation = true + searchController.obscuresBackgroundDuringPresentation = false navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addButtonDidTapped)) @@ -99,7 +117,7 @@ class ViewController: UIViewController { } -extension ViewController { +extension MainViewController { @objc private func addButtonDidTapped() { let createViewController = CreateViewController() @@ -114,9 +132,10 @@ extension ViewController { } } -extension ViewController: UITableViewDelegate { +extension MainViewController: UITableViewDelegate { func numberOfSections(in tableView: UITableView) -> Int { - return isFiltering ? filterdHeaderList.count + 1 : sectionHeaderList.count + 1 + return viewModel.sectionHeaderList.value.count +// return isFiltering ? filterdHeaderList.count + 1 : sectionHeaderList.count + 1 } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { @@ -134,13 +153,13 @@ extension ViewController: UITableViewDelegate { } } -extension ViewController: UITableViewDataSource { +extension MainViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch section { case 0 : return self.isFiltering ? 0 : 1 default: - return self.isFiltering ? getSectionArray(at: section, list: filteredList).count: getSectionArray(at: section, list: arr).count + return self.isFiltering ? getSectionArray(at: section, list: viewModel.list.value).count: getSectionArray(at: section, list: viewModel.list.value).count } } @@ -160,9 +179,9 @@ extension ViewController: UITableViewDataSource { let cell = UITableViewCell() if self.isFiltering { - cell.textLabel?.text = getSectionArray(at: indexPath.section,list: filteredList)[indexPath.row] + cell.textLabel?.text = getSectionArray(at: indexPath.section,list: viewModel.list.value)[indexPath.row] } else { - cell.textLabel?.text = getSectionArray(at: indexPath.section,list: arr)[indexPath.row] + cell.textLabel?.text = getSectionArray(at: indexPath.section,list: viewModel.list.value)[indexPath.row] } return cell @@ -174,19 +193,19 @@ extension ViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if section != 0 { - return isFiltering ? filterdHeaderList[section-1] : sectionHeaderList[section-1] + return isFiltering ? viewModel.sectionHeaderList.value[section-1] : viewModel.sectionHeaderList.value[section-1] } else { return nil } } func sectionIndexTitles(for tableView: UITableView) -> [String]? { - return sectionHeaderList + return viewModel.sectionHeaderList.value } } -extension ViewController: UISearchResultsUpdating{ +extension MainViewController: UISearchResultsUpdating{ func updateSearchResults(for searchController: UISearchController) { guard let text = searchController.searchBar.text else { return } filterdHeaderList.removeAll() diff --git a/CheoMooRac/CheoMooRac/Sources/ViewModels/Dynamic.swift b/CheoMooRac/CheoMooRac/Sources/ViewModels/Dynamic.swift new file mode 100644 index 0000000..02c7713 --- /dev/null +++ b/CheoMooRac/CheoMooRac/Sources/ViewModels/Dynamic.swift @@ -0,0 +1,32 @@ +// +// Dynamic.swift +// CheoMooRac +// +// Created by 김윤서 on 2021/09/24. +// + +import Foundation + +class Dynamic { + typealias Listener = (T) -> Void + var listener: Listener? + + func bind(_ listener: Listener?) { + self.listener = listener + } + + func bindAndFire(_ listener: Listener?) { + self.listener = listener + listener?(value) + } + + var value: T { + didSet { + listener?(value) + } + } + + init(_ v: T) { + value = v + } +} diff --git a/CheoMooRac/CheoMooRac/Sources/ViewModels/MainViewModel.swift b/CheoMooRac/CheoMooRac/Sources/ViewModels/MainViewModel.swift new file mode 100644 index 0000000..21ac60f --- /dev/null +++ b/CheoMooRac/CheoMooRac/Sources/ViewModels/MainViewModel.swift @@ -0,0 +1,62 @@ +// +// MainViewModel.swift +// CheoMooRac +// +// Created by 김윤서 on 2021/09/23. +// + +import Foundation + +protocol MainViewModelInput { + +} + +protocol MainViewModelOutput { + var list: Dynamic<[String]> {get} + var sectionHeaderList: Dynamic<[String]> {get} +} + +protocol MainViewModelProtocol : MainViewModelInput,MainViewModelOutput {} + +class MainViewModel: MainViewModelProtocol { + + // MARK: - OUTPUT + let list: Dynamic<[String]> = Dynamic([]) + let sectionHeaderList: Dynamic<[String]> = Dynamic([]) + +// private var filteredList: [String] = [] +// private var filterdHeaderList: [String] = [] + // private var list = ["김윤서", "김루희", "윤예지", "김혜수", "코코", "민재", "잼권이", "리헤이", "노제", "몬익화", "립제이", "잘린이", "엠마", "모아나", "케이데이", "가비", "시미즈zz", "강호동", "이수근", "유재석", "리정" ] +// +// private var filteredList: [String] = [] +// private var filterdHeaderList: [String] = [] +// +// private var sectionHeaderList: [String] { +// var sectionHeaderList: [String] = [] +// list.forEach { name in +// sectionHeaderList.append(StringManager.shared.chosungCheck(word: name)) +// } +// +// return Array(Set(sectionHeaderList)).sorted() +// } + +// var sectionHeaderList$: [String] { +// var sectionHeaderList: [String] = [] +// self.list?.forEach { name in +// sectionHeaderList.append(StringManager.shared.chosungCheck(word: name)) +// } +// +// return Array(Set(sectionHeaderList)).sorted() +// } + + init() { + self.list.value = ["김윤서", "김루희", "윤예지", "김혜수", "코코", "민재", "잼권이", "리헤이", "노제", "몬익화", "립제이", "잘린이", "엠마", "모아나", "케이데이", "가비", "시미즈zz", "강호동", "이수근", "유재석", "리정" ] + + var sectionHeaderList$: [String] = [] + self.list.value.forEach { name in + sectionHeaderList$.append(StringManager.shared.chosungCheck(word: name)) + } + + self.sectionHeaderList.value = Array(Set(sectionHeaderList$)).sorted() + } +} diff --git a/CheoMooRac/CheoMooRac/Sources/Views/MainTableViewController.swift b/CheoMooRac/CheoMooRac/Sources/Views/MainTableViewController.swift new file mode 100644 index 0000000..197e10b --- /dev/null +++ b/CheoMooRac/CheoMooRac/Sources/Views/MainTableViewController.swift @@ -0,0 +1,23 @@ +// +// MainTableViewController.swift +// CheoMooRac +// +// Created by 김윤서 on 2021/09/24. +// + +import UIKit + +class MainTableViewController: UITableViewController { + private var viewModel: MainViewModelProtocol + + init(with viewModel: MainViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + +}