ohdair / ios-contact-manager-ui

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

iOS_contact-manager-ui


๐Ÿ’ป ์‹คํ–‰ ํ™”๋ฉด

์—ฐ๋ฝ์ฒ˜ ์ถ”๊ฐ€ ์—ฐ๋ฝ์ฒ˜ ์ˆ˜์ • ์—ฐ๋ฝ์ฒ˜ ์‚ญ์ œ ์—ฐ๋ฝ์ฒ˜ ๊ฒ€์ƒ‰

๐Ÿ”– ์—ญํ•  ๋ถ„๋ฐฐ

Controller ์—ญํ• 
ContactsViewController - ์šฐ์ธก ์ƒ๋‹จ โž• ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ContactHandlerViewController ํ™”๋ฉด์œผ๋กœ modal push๋กœ ์ด๋™
- NavigationItem์— searchController ์ถ”๊ฐ€
- tableView์˜ Cell ์„ ํƒ ์‹œ, ์—ฐ๋ฝ์ฒ˜๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ํ™”๋ฉด์œผ๋กœ ์ด๋™
- Cell์„ Swipingํ•˜๋ฉด ์‚ญ์ œ์˜ ๋ฒ„ํŠผ ํ™œ์„ฑํ™” ๋ฐ ์‚ญ์ œ ๊ตฌํ˜„
ContactHandlerViewController - ์—ฐ๋ฝ์ฒ˜๋ฅผ Handler ํƒ€์ž…์„ ์ด์šฉํ•˜์—ฌ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •์„ ๋ฐ˜์˜
  - ์ถ”๊ฐ€ : ์ƒˆ๋กœ์šด ์—ฐ๋ฝ์ฒ˜๊ฐ€ ์ถ”๊ฐ€
  - ์ˆ˜์ • : ๊ธฐ์กด ์—ฐ๋ฝ์ฒ˜๋ฅผ ์ˆ˜์ •
enum ์—ญํ• 
Handler ContactHandlerViewController์—์„œ add์™€ edit์˜ ์ƒํƒœ๋ฅผ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํƒ€์ž… ์ •์˜
struct ์—ญํ• 
Contact ์—ฐ๋ฝ์ฒ˜์— ํ•„์š”ํ•œ ์ •๋ณด(name, age์™€ phoneNumber)๋ฅผ ๊ฐ–๋Š” ๊ตฌ์กฐ์ฒด

Step์— ๋”ฐ๋ฅธ ๊ตฌํ˜„

๐Ÿš€ STEP 1 - iOS App Target ์ถ”๊ฐ€

  • ๊ธฐ์กด ContactManager ํƒ€๊นƒ์˜ ์ฃผ์š” ํŒŒ์ผ์„ ContactManagerUI์˜ ํƒ€๊นƒ ๋ฉค๋ฒ„์‹ญ ํŒŒ์ผ๋กœ ์ถ”๊ฐ€

๐Ÿš€ STEP 2 - ์—ฐ๋ฝ์ฒ˜ ๋ชฉ๋ก ๊ตฌํ˜„

  • tableView ๊ตฌํ˜„
  • ContactTableViewCell๋กœ tableView์—์„œ Cell๋กœ ์‚ฌ์šฉ

๐Ÿš€ STEP 3 - ์—ฐ๋ฝ์ฒ˜ ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ๊ตฌํ˜„

์ •์ƒ ๋™์ž‘ ์ทจ์†Œ ๊ธฐ๋Šฅ ์—๋Ÿฌ ๋ฐœ์ƒ์‹œ

์ˆซ์ž๋งŒ ์ž…๋ ฅ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„

์ „ํ™”๋ฒˆํ˜ธ ์ž…๋ ฅ์‹œ "-"๋ฅผ ์ปค์Šคํ…€ ํ‚ค๋ณด๋“œ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
์ „ํ™”๋ฒˆํ˜ธ ์ˆซ์ž์˜ ๊ฐœ์ˆ˜๊ฐ€

  • 9๊ฐœ ์ดํ•˜์ธ ๊ฒฝ์šฐ XX-XXX-XXXX
  • 10๊ฐœ์ธ ๊ฒฝ์šฐ XXX-XXX-XXXX
  • 11๊ฐœ ์ด์ƒ์ธ ๊ฒฝ์šฐ XXX-XXXX-XXXX

Formatter ํƒ€์ž…์œผ๋กœ PhoneFormatter ์ƒ์„ฑ

class PhoneFormatter: Formatter {
    override func string(for obj: Any?) -> String? {
        guard let numbers = obj as? String,
              Int(numbers) != nil else { return nil }
        var formatType: String
        var index = numbers.startIndex
        var formattedNumbers = ""

        switch numbers.count {
        case ...9:
            formatType = "XX-XXX-XXXX"
        case 10:
            formatType = "XXX-XXX-XXXX"
        default:
            formatType = "XXX-XXXX-XXXX"
        }

        for character in formatType where index < numbers.endIndex {
            if character == "X" {
                formattedNumbers.append(numbers[index])
                index = numbers.index(after: index)
            } else {
                formattedNumbers.append(character)
            }
        }

        while index < numbers.endIndex {
            formattedNumbers.append(numbers[index])
            index = numbers.index(after: index)
        }

        return formattedNumbers
    }
}

์—ฐ๋ฝ์ฒ˜๋ฅผ ์ถ”๊ฐ€

tableView์— ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•ด์„œ reloadData()๋Š” ์ „์ฒด์ ์œผ๋กœ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฑฐ๋ผ์„œ ๋น„์šฉ์ด ํฌ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  insertRows()๋ฅผ ์‚ฌ์šฉ

๐Ÿš€ STEP Bonus

์•„์ดํฐ ์—ฐ๋ฝ์ฒ˜์™€ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„

  • ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ
  • ์‚ญ์ œ ๊ธฐ๋Šฅ
  • ํŽธ์ง‘ ๊ธฐ๋Šฅ

์ถ”๊ฐ€๋œ ์—ฐ๋ฝ์ฒ˜์˜ ์ด๋ฆ„์— ๋งž๊ฒŒ Section์„ ๋ถ„๋ฆฌ

๊ธฐ์กด ์ถ”๊ฐ€ ๊ธฐ๋Šฅ์„ ํ•  ๋•Œ, insert๋˜๋Š” ๋ถ€๋ถ„์„ ์ด๋ฆ„์„ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ์•ŒํŒŒ๋ฒณ์— ๋งž๊ฒŒ Section์„ ๋ถ„๋ฆฌํ•˜์—ฌ Row๋ฅผ ์ถ”๊ฐ€

๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ

isSearching์ด๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ๋‘์–ด Bool๊ฐ’์— ๋”ฐ๋ผ tableView์˜ Cell์„ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ์„œ ๋ณ€๊ฒฝํ•˜๋„๋ก ์ฒ˜๋ฆฌ

  • section์˜ ์ˆ˜
func numberOfSections(in tableView: UITableView) -> Int {
    return isSearching ? 1 : sectionOfContacts().count
}
  • section๋งˆ๋‹ค row ์ˆ˜
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if isSearching {
        return searchedContacts.count
    }
    ...
}
  • section Header ๋ณ€๊ฒฝ
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if isSearching {
        return "Search Results"
    }
    ...
}

Cell์„ Swipingํ•˜๋ฉด delete ๊ตฌํ˜„

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        guard let selectedCell = tableView.cellForRow(at: indexPath) as? ContactTableViewCell,
              let selectedContact = selectedCell.getContact(),
              let selectedIndex = self.contacts.firstIndex(of: selectedContact) else {
            return
        }
        self.contacts.remove(at: selectedIndex)
        self.tableView.reloadData()
    }
}

Cell ๋ฒ„ํŠผ ํด๋ฆญ์‹œ ContactHandlerViewController์˜ edit ์ƒํƒœ๋กœ ํ™”๋ฉด ์ „ํ™˜

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if let viewController = storyboard?.instantiateViewController(withIdentifier: "ContactHandler") as? ContactHandlerViewController {
        viewController.handle = .edit
        viewController.delegate = self
        guard let selectedCell = tableView.cellForRow(at: indexPath) as? ContactTableViewCell,
              let contact = selectedCell.getContact() else {
            return
        }
        viewController.editContact = (contact, indexPath)
        navigationController?.pushViewController(viewController, animated: true)
    }
}

๐Ÿ““ ํ•™์Šต๋‚ด์šฉ ์š”์ 

  1. tableView์˜ reload()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์‚ฝ์ž… ๋ฐ ์‚ญ์ œ ๋‹จ ํ•œ๊ฐœ์˜ ํ–‰์„ ์ถ”๊ฐ€/์‚ญ์ œ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค๋Š” ๋น„์šฉ์ด ๋งŽ์ด ๋“ค๊ธฐ์— deleteRows(), deleteSections()์— ๋Œ€ํ•œ ์‚ฌ์šฉ์— ๋Œ€ํ•ด ๋ฐฐ์› ์–ด์š”
  2. ์—ฐ๋ฝ์ฒ˜๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ํ™”๋ฉด๊ณผ ์ˆ˜์ •ํ•˜๋Š” ํ™”๋ฉด์€ ๋™์ผํ•˜๊ฒŒ ๊ตฌ์„ฑ์„ ํ–ˆ๊ธฐ์— ์žฌํ™œ์šฉ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๊ณ ๋ฏผ์„ ํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ContactHandlerViewController๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” ํƒ€์ž…์„ ๋ฐ›์•„์„œ ๊ตฌํ˜„์„ ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ์–ด์š”
  3. ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์™€ table์˜ ์„น์…˜ ๊ทธ๋ฆฌ๊ณ  ํ–‰์— ๋Œ€ํ•ด์„œ ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋” ๊ตฌํ˜„์„ ํ•˜๋Š” ๊ฒŒ ์ข‹์„ ์ง€ ๊ณ ๋ฏผ์„ ํ•˜์˜€๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ tableView์˜ ์„น์…˜์— ๋งž์ถฐ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋ฐ์ดํ„ฐ์—์„œ tableView์—์„œ ์„น์…˜๋ณ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ๊ฒฐ๊ตญ ๊ฐ™์€ ๋กœ์ง์ด๋ผ๊ณ  ์ƒ๊ฐ๋˜์–ด ๋ฐ์ดํ„ฐ๋Š” ๋ฐฐ์—ด๋กœ ๊ตฌํ˜„์„ ํ–ˆ๊ณ  ์ข€ ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๊ณ ๋ฏผ์„ ํ•˜๊ฒŒ ๋˜์—ˆ์–ด์š”.

๐Ÿงจ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

  1. STEP์„ BONUS๊นŒ์ง€ ์ง„ํ–‰์„ ํ•˜๋ฉด์„œ tableView์—์„œ ์ด๋ฆ„๋ณ„๋กœ ์„น์…˜์„ ์ถ”๊ฐ€ํ•˜์˜€๊ณ  ํ–‰์„ ์‚ญ์ œํ•˜๋Š” ๋ฉ”์„œ๋“œ์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด ๊ฒƒ์„ ๋ฐœ๊ฒฌํ•˜์˜€๊ณ  ํ–‰์ด ํ•˜๋‚˜ ๋‚จ์•˜์„ ๋•Œ Cell์„ ์ง€์šฐ๊ฒŒ ๋˜๋ฉด ์„น์…˜์˜ ์ˆ˜๊ฐ€ ๋งž์ง€ ์•Š์•„์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒ์„ ํ•˜์˜€์Šต๋‹ˆ๋‹ค. Cell์„ ์‚ญ์ œ๋ฅผ ํ•  ๋•Œ ์„น์…˜๋„ ์‚ญ์ œ๋ฅผ ํ•ด์•ผํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜๊ณ  deleteSections()์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๊ฐœ๋ณ„์ ์ธ ์‚ญ์ œ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๊ธฐ์— beginUpdates(), endUpdates() ๋˜ํ•œ ์‚ฌ์šฉ์„ ํ•˜์—ฌ ํ•ด๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  2. ๋ฐ์ดํ„ฐ๋Š” 1์ฐจ์› ๋ฐฐ์—ด์ด๊ณ  TableView์˜ ์„น์…˜๊ณผ ํ–‰์€ 2์ฐจ์› ๋ฐฐ์—ด ํ˜•์‹์ด๋ผ์„œ ํ–‰์˜ ๊ฐฏ์ˆ˜๋ฅผ ๋งž์ถ”๋Š” ๊ณผ์ •์ด ์–ด๋ ค์› ์œผ๋ฉฐ, ์„น์…˜๋งˆ๋‹ค์˜ ๊ฐฏ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” lastIndex์™€ firstIndex๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐฏ์ˆ˜๋ฅผ ์„ธ์—ˆ์Šต๋‹ˆ๋‹ค

About


Languages

Language:Swift 100.0%