ueunli / ios-contact-manager-ui

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

๐Ÿ—“ 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)๋กœ ์ง€์ •ํ•˜์—ฌ ํ•จ๊ป˜ ๋ณด๋‚ด์ง€๊ณ ,
    AddVC.delegate: AddVCDelegate = self    //self: ListVC(AddVCDelegate๋กœ์„œ์˜ ListVC)
  • ๋Œ€๋ฆฌ์ž๋Š” ์ฑ…์ž„์ž(AddVC) ๋‚ด์— ๋จธ๋ฌผ๋ฉฐ(= .delegate๋ณ€์ˆ˜์— ํ• ๋‹น๋œ ์ฑ„) ๋Œ€๋ฆฌ์ž๋กœ์„œ ์š”๊ตฌ๋ฐ›์€ ๋™์ž‘(ํ”„๋กœํ† ์ฝœ ํ•„์ˆ˜๊ตฌํ˜„ ๋ฉ”์„œ๋“œ)์„ ์ ์ ˆํ•œ ์‹œ์ ์— ์ˆ˜ํ–‰
    // ๊ทธ ๋™์ž‘์€ AddProfileViewController์—์„œ 'Save๋ฒ„ํŠผ์ด ๋ˆŒ๋ €์„ ๋•Œ' ํ˜ธ์ถœ๋˜์–ด,
    // (๊ฒ€์ฆ ์™„๋ฃŒ๋œ) ์ƒˆ๋กœ์šด ์ด๋ฆ„ยท๋‚˜์ดยท์—ฐ๋ฝ์ฒ˜ ์ •๋ณด๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ํ”„๋กœํ•„์„ ์ƒ์„ฑ(= ์š”๊ตฌ๋ฐ›์€ ๋™์ž‘)
    delegate?.updateProfile(name: name, age: age, tel: tel)
    dismiss()
  • AddV๊ฐ€ ๋‚ด๋ ค๊ฐ€๋ฉด ๋Œ€๋ฆฌ์ž ์—ญํ• ์„ ๋งˆ๋ฌด๋ฆฌํ•˜๊ณ  ๋Œ์•„์˜จ ListVC๋Š” ๊ทธ ๋ฐ์ดํ„ฐ(์ƒˆ ํ”„๋กœํ•„)๋ฅผ ๋ฐ›์•„ ํ•„์š”ํ•œ ์ž‘์—…(profiles์— ์ƒˆ ํ”„๋กœํ•„์„ ๋“ฑ๋ก)์„ ์ด์–ด์„œ ์ˆ˜ํ–‰


Step ๐Ÿ…ฑ๐Ÿ…พ๐Ÿ…ฝ๐Ÿ†„๐Ÿ†‚

# ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๐Ÿ‘ฟ ํŠธ๋Ÿฌ๋ธ”

๊ฒ€์ƒ‰๊ฒฐ๊ณผ ํ™”๋ฉด์—์„œ๋„ ์˜ฌ๋ฐ”๋ฅธ ์…€์ด ์‚ญ์ œ๋˜๋„๋ก ํ•˜๊ธฐ

๐Ÿ˜ˆ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ํ™œ์šฉํ•˜์—ฌ, isSearching์— ๋”ฐ๋ผ index๋กœ ์ ‘๊ทผํ•  ํ”„๋กœํ•„ ๋ฐฐ์—ด์ด profiles / filteredProfiles ์ค‘ ์–ด๋Š์ชฝ์ธ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€

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:) ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ

About


Languages

Language:Swift 100.0%