diff --git a/README.md b/README.md
index 14895eae..793990a6 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,149 @@
-# ios-contact-manager-ui
\ No newline at end of file
+# ๐ README
+#### โจ iyeah & ๐ Jenna
+##### 2023๋
1์ 30์ผ โ 2023๋
2์ 10์ผ
+
+ใ
+
+# Step 1๏ธโฃ
+## \# ํธ๋ฌ๋ธ ์ํ
+### ๐ฟ ํธ๋ฌ๋ธ
+> ๊ธฐ์กด ํ๋ก์ ํธ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ค๋ฉด์, ํ๋ก์ ํธ ๋ฐ ํด๋๋ช
์ ๋ฐ๊ฟ์ผ ํ ์ง ๊ณ ๋ฏผ
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+> ํ๊ฒ `ContactManagerUI`์ ์๋ก ์ถ๊ฐํ๋ค๋ ๊ฒ์ ํ๋ก์ ํธ์ ๋ณ๋์ ์ ํ์ ๋ง๋ ๋ค๋ ๊ฒ์ด๋ฏ๋ก,
+์คํ๋ ค ์ด๋ฆ์ ๊ฐ๊ฒ ๋ง์ถ๋ ค ํ ํ์๊ฐ ์์์ ๊นจ๋ฌ์ ํ๋ก์ ํธ ๋ฐ ํด๋๋ช
์ ์ง
+
+
+## \# ํ์ต๋ด์ฉ ์์ฝ
+>`Target`
+ํ๋์ ํ๊ฒ์ ํ๋์ ํ๋ก๋ํธ์ด๋ฉฐ,
+ํ๋ก์ ํธ ๋ด์ ์ฌ๋ฌ ๊ฐ์(= ๋ณ๊ฐ์) ํ๊ฒ(ํ๋ก๋ํธ)์ด ์กด์ฌํ ์ ์๋ค.
+
+
+
+ใ
+ใใ
+
+# Step 2๏ธโฃ
+## \# ํธ๋ฌ๋ธ ์ํ
+### ๐ฟ ํธ๋ฌ๋ธ
+> ๋๋ฏธ ๋ฐ์ดํฐ๋ฅผ Model์ธ `ContactManageSystem`์ ๋ฃ์์ง, `ViewController`์ ๊ตฌํ ํด์ผํ ์ง์ ๋ํ ๊ณ ๋ฏผ
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+
+๋ฆฌ๋ทฐ์ด ์๊ฒฌ ๐ถ
+๋๋ฏธ ๋ฐ์ดํฐ ์์ฒด๋ฅผ ์์์ฒ๋ผ ๋ง๋ค์ด์ ViewController์์ ์ฒ๋ฆฌํ๋๊ฒ ์ ๋ ๋ ๊น๋ํ ๊ฒ ๊ฐ์์! ์์์ฒ๋ผ ์ฐ๊ณ ๋์ค์ ์ ๊ฑฐํ๋ฉด ๋๋๊น์!
+
+
+โ `ViewController`์ `dummyData`๋ฅผ ์์๋ก ์ ์ธํ์ฌ ํด๊ฒฐ
+
+ใ
+### ๐ฟ ํธ๋ฌ๋ธ
+> MVC ํด๋์ ๋ฃ๊ธฐ ์ ๋งคํ ํ์ผ๋ค ์ฒ๋ฆฌ
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+
+๋ฆฌ๋ทฐ์ด ์๊ฒฌ ๐ถ
+์ด ๋ถ๋ถ์ ์ ํด์ ธ์๋ ๋ต์ ์์ผ๋, ํ ๋ด์์ ์ฝ์ํด์ ํด๋๋ก ์ ๋ฆฌํด๋ ๊ด์ฐฎ์ต๋๋ค.
MVC ํด๋ ๋ฐ์ผ๋ก ๋นผ๋๋ ๊ฒ๋ ์ข์ต๋๋ค.
LaunchScreen์ ๋ฐ๋ก ํด๋๋ก ๋ง๋๋ ๊ฒฝ์ฐ๋ ์๊ณ , AppConfiguration๊ณผ ๊ฐ์ด ํด๋๋ฅผ ๋ง๋ค์ด์ AppDelegate, SceneDelegate ํ์ผ์ ๋ฃ๊ธฐ๋ํฉ๋๋ค.
์ ๊ฐ ๋ง์๋๋ฆฐ ๋ถ๋ถ์ ์ฐธ๊ณ ๋งํ์๊ณ ํ์๊ณผ ๊ฐ์ด ์ด์ผ๊ธฐํด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค:)
+
+
+โ ์๋์ ๊ฐ์ด ํด๋๋ง ์งํ
+
+
+
+
+ใ
+## \# ํ์ต๋ด์ฉ ์์ฝ
+> 1. ๋๋ฏธ๋ฐ์ดํฐ๊ฐ ํ์ํ๋ค๋ฉด ์ํ๋ ์์ ์ ์ญ์ ๊ฐ ํธํ๋๋ก ์์๋ก ๊ตฌํ
+> 2. ํด๋๋ง์ ์ ํด์ง ๊ฒ ์๊ธฐ ๋๋ฌธ์ ํ์๊ณผ ์์ํด์ ์ ํ๊ธฐ
+
+
+
+
+
+# Step 3๏ธโฃ
+## \# ํธ๋ฌ๋ธ ์ํ
+### ๐ฟ ํธ๋ฌ๋ธ
+> `.phonePad`๋ -์
๋ ฅ์ ์ง์ํ์ง ์์
+โ ์์ดํฐ ๊ธฐ๋ณธ์ฑ์ฒ๋ผ, ํ
์คํธํ๋์ ๊ฐ์ด ์
๋ ฅ๋ ๋๋ง๋ค `-`๊ฐ ์ ์ ํ ์์น์ ์ฝ์
๋๊ฒ๋ ์๋๋ณํ ํด์ฃผ๋ ๋ก์ง์ ์ถ๊ฐ๋ก ๊ตฌํํ ํ์๊ฐ ์๊น
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+> `textField(_:shouldChangeCharactersIn:replacementString:)`๋ฉ์๋
+
+- `AddProfileViewController๊ฐ` `UITextFieldDelegate`ํ๋กํ ์ฝ์ ์ฑํ
+- `textField(_:shouldChangeCharactersIn:replacementString:)`๋ฉ์๋๋ก ์๋ก์ด ์
๋ ฅ์ ์ข
๋ฅ์ ๋ฐ๋ผ
+ - ์ฐ๋ฝ์ฒ ํ
์คํธํ๋์์์ ์
๋ ฅ์ ํ์ฉํ๊ฑฐ๋,
+ - ๋๋ ์์์ ๋ง์ถ ๋ณํ๊ฐ์ผ๋ก ๋์ฒดํ์ฌ ์ง์ ํ ๋น(์
๋ ฅ ๊ฑฐ์ )
+- `PhoneNumberRegularExpressions`์ด๊ฑฐํ์ผ๋ก ์๋ฆฟ์๋ณ ๋ณํ ๋ฐฉ์์ ์ ์
+
+ใ
+## \# ํ์ต๋ด์ฉ ์์ฝ
+> Delegate์ ์ ์๋ `์์ํ๋ค`,
+๊ฐ์ธ์ ์ผ๋ก Delegate Pattern์ด๋ `์ฑ
์์-๋๋ฆฌ์ ํจํด` ์ด๋ผ๊ณ ์ดํด
+#### ํ๋ก์ ํธ์ ์ ์ฉํ ๋ถ๋ถ โผ
+- `AddProfileViewController`(์ดํ AddVC)๋ **์ฑ
์์**
+- `ListProfileViewController`(์ดํ ListVC)๋ `AddProfileViewControllerDelegate`(์ดํ AddVCDelegate) ์๊ฒฉ์ฆ์ ๊ฐ์ง
+- `ListVC`๋ ์๋ก์ด ๋ทฐ(`AddV`)๋ฅผ ์ฌ๋ฆด ๋ ๋ณธ์ธ(self)์ ๊ทธ ๋ทฐ์ปจํธ๋กค๋ฌ(`AddVC`)์ **๋๋ฆฌ์**(Delegate)๋ก ์ง์ ํ์ฌ ํจ๊ป ๋ณด๋ด์ง๊ณ ,
+ ```Swift
+ AddVC.delegate: AddVCDelegate = self //self: ListVC(AddVCDelegate๋ก์์ ListVC)
+ ```
+- ๋๋ฆฌ์๋ ์ฑ
์์(AddVC) ๋ด์ ๋จธ๋ฌผ๋ฉฐ(= .delegate๋ณ์์ ํ ๋น๋ ์ฑ)
+๋๋ฆฌ์๋ก์ ์๊ตฌ๋ฐ์ ๋์(ํ๋กํ ์ฝ ํ์๊ตฌํ ๋ฉ์๋)์ ์ ์ ํ ์์ ์ ์ํ
+ ```Swift
+ // ๊ทธ ๋์์ AddProfileViewController์์ 'Save๋ฒํผ์ด ๋๋ ์ ๋' ํธ์ถ๋์ด,
+ // (๊ฒ์ฆ ์๋ฃ๋) ์๋ก์ด ์ด๋ฆยท๋์ดยท์ฐ๋ฝ์ฒ ์ ๋ณด๋ฅผ ์กฐํฉํ์ฌ ํ๋กํ์ ์์ฑ(= ์๊ตฌ๋ฐ์ ๋์)
+ delegate?.updateProfile(name: name, age: age, tel: tel)
+ dismiss()
+ ```
+- `AddV`๊ฐ ๋ด๋ ค๊ฐ๋ฉด ๋๋ฆฌ์ ์ญํ ์ ๋ง๋ฌด๋ฆฌํ๊ณ ๋์์จ `ListVC`๋ ๊ทธ ๋ฐ์ดํฐ(์ ํ๋กํ)๋ฅผ ๋ฐ์ ํ์ํ ์์
(profiles์ ์ ํ๋กํ์ ๋ฑ๋ก)์ ์ด์ด์ ์ํ
+
+
+
+ใ
+ใ
+
+
+# Step ๐
ฑ๐
พ๐
ฝ๐๐
+## \# ํธ๋ฌ๋ธ ์ํ
+### ๐ฟ ํธ๋ฌ๋ธ
+> ๊ฒ์๊ฒฐ๊ณผ ํ๋ฉด์์๋ ์ฌ๋ฐ๋ฅธ ์
์ด ์ญ์ ๋๋๋ก ํ๊ธฐ
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+์ผํญ์ฐ์ฐ์๋ฅผ ํ์ฉํ์ฌ, `isSearching`์ ๋ฐ๋ผ
+index๋ก ์ ๊ทผํ ํ๋กํ ๋ฐฐ์ด์ด `profiles` / `filteredProfiles` ์ค ์ด๋์ชฝ์ธ์ง ๊ฒฐ์ ํ๋ ๋ก์ง์ ์ถ๊ฐ
+```Swift
+let profile = isSearching ? profileSearchResults[indexPath.row] : profiles[indexPath.row]
+```
+
+ใ
+### ๐ฟ ํธ๋ฌ๋ธ
+> ๋๋ช
์ด์ธ์ด ์์ด๋ ์ ํํ ์ญ์ ๋๋๋ก ํ๊ธฐ
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+1. ์ด๋ฐ์ profiles์์ `name`์ด ์ผ์นํ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ํ์ง๋ง, ๋๋ช
์ด์ธ์ด ๋์ ์ญ์ ๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์
+2. Model์ธ `Profile`์ด Hashableํ๋กํ ์ฝ(= ์ฆ Equatable๋ ์ฑํํจ)์ ์ฑํํ์ผ๋ฏ๋ก ์ปค์คํ
์ดํญ์ฐ์ฐ์๋ฅผ ๊ตฌํํด๋ณด๋ ค๋ค๊ฐ ์๋์ ๊ฐ์ ๋ฐ์์ด ๋ ์ฌ๋ผ ๋ณด๋ฅ
+3. `tableView(_:cellForRowAt:)`๋ฉ์๋์์ `indexPath.row`๋ก `profile`์ ๋ถ๋ฌ์์ผ๋ฏ๋ก, ์ญ์ผ๋ก ํด๋น index์ profile์ ๊บผ๋ด์ด ์ญ์ ํ๋ฉด ํด๋น ์
์ profile์ด ์ญ์ ๋ ๊ฑฐ๋ผ๊ณ ์๊ฐํ๊ณ ๊ตฌํ
+
+ใ
+### ๐ฟ ํธ๋ฌ๋ธ
+> ์ด๋ฆ์ ๋์๋ฌธ์๊ฐ ์์ฌ ์์ ๋ ์ค๋ฆ์ฐจ์์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ ๋ ฌ๋์ง ์๊ณ ๊ฒ์์ด ๋์ง ์์
+
+### ๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
+์ค๋ฆ์ฐจ์ ์ ๋ ฌ ์ `lowercased()` ๋ฉ์๋๋ฅผ ์ ์ฉํ์ฌ ๋์๋ฌธ์ ๊ตฌ๋ถ์์ด sort๋๋๋ก ํจ
+
+ใ
+ใ
+## \# ํ์ต๋ด์ฉ ์์ฝ
+### searchBar ๋ง๋ค๊ธฐ
+> **UISearchBar์ UISearchController์ ์ฐจ์ด**
+> - VC.navigationItem.searchController: `UISearchController`
+> - UISearchController().searchBar: `UISearchBar`
+### ์ฌ๋ผ์ด๋ํ์ฌ ํด๋น ์
delete ํ๊ธฐ
+> `UITableViewDataSource` ํ๋กํ ์ฝ ๋ด์ ์๋ `tableView(_:commit:forRowAt:)` ๋ฉ์๋ ์ฌ์ฉ
+
+
+
+
+
+ใ
+ ใ
diff --git a/ios-cantact-manager/ContactManagerUI/Controllers/ListProfileViewController.swift b/ios-cantact-manager/ContactManagerUI/Controllers/ListProfileViewController.swift
index 3151582a..144f8833 100644
--- a/ios-cantact-manager/ContactManagerUI/Controllers/ListProfileViewController.swift
+++ b/ios-cantact-manager/ContactManagerUI/Controllers/ListProfileViewController.swift
@@ -10,14 +10,27 @@ import UIKit
final class ListProfileViewController: UIViewController, AddProfileViewControllerDelegate {
private var contactManageSystem = ContactManageSystem()
private var profiles: [Profile] {
- contactManageSystem.profiles.sorted(by: { $0.name < $1.name })
+ contactManageSystem.sortProfiles()
}
+ private lazy var profileSearchResults = [Profile]()
+ private var isSearching: Bool {
+ let searchBarController = self.navigationItem.searchController
+ let isActive = searchBarController?.isActive ?? false
+ let isEmpty = searchBarController?.searchBar.text?.isEmpty ?? true
+ return isActive && !isEmpty
+ }
+
private let dummyData = [
- Profile(name: "james", age: "30", tel: "010-2222-2222"),
- Profile(name: "tom", age: "15", tel: "010-2222-3333"),
- Profile(name: "jams", age: "30", tel: "010-2222-2222"),
- Profile(name: "toem", age: "15", tel: "010-2222-3333"),
- Profile(name: "jamses", age: "30", tel: "010-2222-2222")
+ Profile(name: "iyeah", age: "1", tel: "010-2222-2222"),
+ Profile(name: "iyeah", age: "2", tel: "010-2222-3333"),
+ Profile(name: "iyeah", age: "3", tel: "010-2222-2222"),
+ Profile(name: "iyeah", age: "4", tel: "010-2222-3333"),
+ Profile(name: "Jenna", age: "5", tel: "010-2222-3333"),
+ Profile(name: "Jenna", age: "6", tel: "010-2222-3333"),
+ Profile(name: "Jenna", age: "7", tel: "010-2222-3333"),
+ Profile(name: "Jenna", age: "8", tel: "010-2222-3333"),
+ Profile(name: "iyeah", age: "9", tel: "010-2222-3333"),
+ Profile(name: "SeSaC", age: "30", tel: "010-2222-2222")
]
@IBOutlet private weak var tableView: UITableView!
@@ -28,6 +41,7 @@ final class ListProfileViewController: UIViewController, AddProfileViewControlle
contactManageSystem.add(profile: $0)
}
tableView.dataSource = self
+ makeSearchBar()
}
@IBAction private func addProfileButtonDidTap(_ sender: UIBarButtonItem) {
@@ -47,11 +61,11 @@ final class ListProfileViewController: UIViewController, AddProfileViewControlle
extension ListProfileViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- profiles.count
+ isSearching ? profileSearchResults.count : profiles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let profile = profiles[indexPath.row]
+ let profile = isSearching ? profileSearchResults[indexPath.row] : profiles[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileCell", for: indexPath)
var content = cell.defaultContentConfiguration()
@@ -63,5 +77,32 @@ extension ListProfileViewController: UITableViewDataSource {
return cell
}
+
+ func tableView(_ tableView: UITableView,
+ commit editingStyle: UITableViewCell.EditingStyle,
+ forRowAt indexPath: IndexPath) {
+ let profile = isSearching ? profileSearchResults[indexPath.row] : profiles[indexPath.row]
+ contactManageSystem.remove(profile: profile)
+ tableView.deleteRows(at: [indexPath], with: .fade)
+ }
}
+extension ListProfileViewController: UISearchResultsUpdating {
+ func updateSearchResults(for searchController: UISearchController) {
+ guard let text = searchController.searchBar.text else {
+ return
+ }
+ profileSearchResults = profiles.filter {
+ $0.name.lowercased() == text.lowercased()
+ }
+ tableView.reloadData()
+ }
+
+ private func makeSearchBar() {
+ let searchBar = UISearchController(searchResultsController: nil)
+ searchBar.searchResultsUpdater = self
+ searchBar.searchBar.autocapitalizationType = .none
+ navigationItem.searchController = searchBar
+ navigationItem.hidesSearchBarWhenScrolling = false
+ }
+}
diff --git a/ios-cantact-manager/ios-cantact-manager/ContactManageSystem.swift b/ios-cantact-manager/ios-cantact-manager/ContactManageSystem.swift
index 3361ad51..a2f56b86 100644
--- a/ios-cantact-manager/ios-cantact-manager/ContactManageSystem.swift
+++ b/ios-cantact-manager/ios-cantact-manager/ContactManageSystem.swift
@@ -49,7 +49,7 @@ struct ContactManageSystem {
case .listUpProfile:
listUpProfile()
case .searchProfile:
- searchProfile()
+ break
case .stop:
stop()
}
@@ -59,23 +59,19 @@ struct ContactManageSystem {
profiles.insert(profile)
}
+ mutating func sortProfiles() -> [Profile] {
+ profiles.sorted {
+ let (lhs, rhs) = ($0.name.lowercased(), $1.name.lowercased())
+ return lhs != rhs ? lhs < rhs : $0.age < $1.age
+ }
+ }
+
private func listUpProfile() {
OutputManager.print(profiles: profiles)
}
- private func searchProfile() {
- do {
- OutputManager.print(text: .inputProfileName)
- let targetName = try inputManager.targetInput()
- let filteredProfileData = profiles.filter { $0.name == targetName }
- guard !filteredProfileData.isEmpty else {
- OutputManager.printNoMatchingData(name: targetName)
- return
- }
- OutputManager.print(profiles: filteredProfileData)
- } catch {
- OutputManager.print(text: .invalidInput)
- }
+ mutating func remove(profile: Profile) {
+ profiles.remove(profile)
}
mutating func stop() {