๐ŸŽ ์•„์ด-์˜ค-์—์Šค/๐Ÿ“ฑ iOS ๊ฐœ๋ฐœ

[iOS] UITableView + UITableViewCell (2): ์ปค์Šคํ…€ ํ…Œ์ด๋ธ” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์™€ Data Source์˜ ์ •์ฒด

๊ฐœ๋ฐœํ•˜๋Š” ํ›ˆ์ด 2021. 9. 26. 17:29

Dynamic UITableView

์ง€๋‚œ ํฌ์ŠคํŠธ์—์„œ๋Š” ์ฝ”๋“œ๋Š” ํ•œ ์ค„๋„ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  ๊ธฐ๋ณธ์ ์ธ ํ…Œ์ด๋ธ” ๋ทฐ์˜ ๊ตฌ์„ฑ์š”์†Œ๋“ค๊ณผ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ค ์‹์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์ •๋ฆฌํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ˆ˜์ž‘์—…์œผ๋กœ ์…€์„ ํ•˜๋‚˜ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์ฃผ๋‹ค ๋ณด๋‹ˆ ์‹œ๊ฐ„๋„ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ  ์‹ค์ œ ๊ฐœ๋ฐœ์—์„œ๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค๋Š” ๋Š๋‚Œ์„ ๋ฐ›์•˜๋Š”๋ฐ์š”, ๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ UITableViewController๋ฅผ ์ปค์Šคํ…€์œผ๋กœ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

Custom UITableViewController 

๋จผ์ € ์ปค์Šคํ…€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ UItableViewController ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค!

์ƒˆ๋กœ ๋งŒ๋“ค๊ธฐ์—์„œ Cocoa Touch Class๋ฅผ ์„ ํƒํ•œ ๋’ค์—,

ํ…Œ์ด๋ธ” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์„ ํƒํ•ด ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ์Šคํ† ๋ฆฌ๋ณด๋“œ๋กœ ๋Œ์•„๊ฐ€์„œ ์ƒ์„ฑํ•œ ํ…Œ์ด๋ธ” ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํด๋ž˜์Šค๋ฅผ ์—ฐ๊ฒฐํ•ด์ฃผ๋ฉด ์ถ”๊ฐ€๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค!

 

์ฝ”๋“œ๋ฅผ ํ•œ ๋ฒˆ ์—ด์–ด๋ด…์‹œ๋‹ค!

์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ์ ์ธ ์ฝ”๋“œ์™€ ์ฃผ์„์ฒ˜๋ฆฌ๋œ ์ฝ”๋“œ๋“ค์ด ๋ณด์ผ ํ…๋ฐ์š”, override ๋œ ๋‘ ๋ฉ”์„œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•˜๋Š” ์ฝ”๋“œ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ ‡๊ฒŒ ๋‚˜์™€์žˆ๊ฒ ์ฃ ? MARK๋ฅผ ์ฝ์–ด๋ณด๋ฉด ํ…Œ์ด๋ธ” ๋ทฐ์˜ data source์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์ธ ๊ฒƒ ๊ฐ™์œผ๋‹ˆ ๋จผ์ € data source๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ฐพ์•„๋ด…์‹œ๋‹ค!!

 

UITableViewDataSource: Data Source์˜ ์ •์ฒด

๐Ÿ’ก The methods that an object adopts to manage data and provide cells for a table view.

 

์• ํ”Œ ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ํ…Œ์ด๋ธ” ๋ทฐ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ์ด๋ ‡๊ฒŒ ์ •์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค! ๊ฐ์ฒด๊ฐ€ ํ…Œ์ด๋ธ” ๋ทฐ์˜ ๋ฐ์ดํ„ฐํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ํ…Œ์ด๋ธ” ๋ทฐ์— ์…€์„ ์ œ๊ณตํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์ฑ„ํƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋“ค์˜ ์ง‘ํ•ฉ์ด๋ผ๊ณ  ํ•˜๋„ค์š”. ์•„์ง ์ž˜ ์™€๋‹ฟ์ง€ ์•Š์•„์„œ ๋” ์ž์„ธํ•œ ์„ค๋ช…์„ ์ฝ์–ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

 

๐Ÿ’กTable views manage only the presentation of their data; they do not manage the data itself. To manage the data, you provide the table with a data source object—that is, an object that implements the 
UITableViewDataSource protocol. A data source object responds to data-related requests from the table.

 

ํ…Œ์ด๋ธ” ๋ทฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ๋งŒ์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Data source ๊ฐ์ฒด๋ฅผ ํ…Œ์ด๋ธ” ๋ทฐ์— ์ œ๊ณตํ•ด์•ผ ํ•˜๊ณ  ์ด ๊ฐ์ฒด๋Š” UITableViewDataSource ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜์—ฌ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด๋ผ๊ณ  ํ•˜๋„ค์š”. ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ์™€ ๊ด€๋ จ๋œ ์ž‘์—…์˜ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ๊ทธ ์š”์ฒญ์— ์‘๋‹ตํ•˜๋Š” ๊ฒƒ ์—ญ์‹œ data source ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.

 

๐Ÿ’ก It also manages the table's data directly, or coordinates with other parts of your app to manage that data. Other responsibilities of the data source object include:
- Reporting the number of sections and rows in the table.
- Providing cells for each row of the table.
- Providing titles for section headers and footers.
- Configuring the table's index, if any.
- Responding to user- or table-initiated updates that require changes to the underlying data.

 

data source ๊ฐ์ฒด๊ฐ€ ํ•˜๋Š” ์ผ์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค!

  • ์„น์…˜์˜ ๊ฐœ์ˆ˜์™€ ํ–‰์˜ ๊ฐœ์ˆ˜๋ฅผ ํ…Œ์ด๋ธ”์— ์•Œ๋ ค์ฃผ๊ธฐ
  • ๊ฐ ํ–‰์— ๋Œ€ํ•œ ์…€์„ ํ…Œ์ด๋ธ”์—๊ฒŒ ์ œ๊ณตํ•˜๊ธฐ 
  • ์„น์…˜์˜ ํ—ค๋”์™€ ํ‘ธํ„ฐ๋ฅผ ์ œ๊ณตํ•˜๊ธฐ 
  • ํ…Œ์ด๋ธ”์˜ ์ธ๋ฑ์Šค๋ฅผ ์•Œ์•„๋‚ด๊ธฐ
  • ํ…Œ์ด๋ธ”์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ ๋ฐ˜์‘ํ•˜๊ธฐ

์ด์ œ ์ข€ ๊ฐ์ด ์˜ค์‹œ๋‚˜์š”? MVC ๋ชจ๋ธ์ด ์ต์ˆ™ํ•˜๋‹ค๋ฉด Data source๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” Model์˜ ์—ญํ• ์„ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋˜๊ฒ ๋„ค์š”!

 

ํ…Œ์ด๋ธ” ๋ทฐ์—๊ฒŒ ์ •๋ณด๋ฅผ ์•Œ๋ ค์ฃผ์ž

๊ทธ๋Ÿผ ๋‹ค์‹œ ์•„๊นŒ์˜ ์ฝ”๋“œ๋กœ ๋Œ์•„๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

Data Source ๊ฐ์ฒด๊ฐ€ ํ•˜๋Š” ์ผ ์ค‘์— ์„น์…˜์˜ ๊ฐœ์ˆ˜์™€ ํ–‰์˜ ๊ฐœ์ˆ˜๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์ผ์ด ์žˆ์—ˆ๋Š”๋ฐ์š”, ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ๋‘ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ฐ”๋กœ ๊ทธ ์—ญํ• ์„ ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค! ๊ทธ๋Ÿผ ๋™์ ์œผ๋กœ ํ–‰๊ณผ ์„น์…˜์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ์ž‘์„ฑํ•ด๋ณผ๊ฒŒ์š”.

 

struct CallInfo {
    let name: String
    let type: String
    let day: String
    
    let randomNames = ["๋ฏผ์ค€", "์„œ์ค€", "์˜ˆ์ค€", "๋„์œค", "์‹œ์šฐ", "์ฃผ์›", "ํ•˜์ค€", "์ง€ํ˜ธ", "์ง€ํ›„", "์ค€์„œ", "์ค€์šฐ", "ํ˜„์šฐ", "๋„ํ˜„", "์ง€ํ›ˆ", "๊ฑด์šฐ", "์šฐ์ง„", "์„ ์šฐ", "์„œ์ง„", "๋ฏผ์žฌ", "ํ˜„์ค€", "์—ฐ์šฐ", "์œ ์ค€", "์ •์šฐ", "์Šน์šฐ", "์Šนํ˜„", "์‹œ์œค", "์ค€ํ˜", "์€์šฐ", "์ง€ํ™˜", "์Šน๋ฏผ", "์ง€์šฐ", "์œ ์ฐฌ", "์œค์šฐ", "๋ฏผ์„ฑ", "์ค€์˜", "์‹œํ›„", "์ง„์šฐ", "์ง€์ˆ˜", "์„œ์—ฐ", "์„œ์œค", "์ง€์šฐ", "์„œํ˜„", "๋ฏผ์„œ", "ํ•˜์€", "ํ•˜์œค", "์œค์„œ", "์ง€์œ ", "์ง€๋ฏผ", "์ฑ„์›", "์ง€์œค", "์€์„œ", "์ˆ˜์•„", "๋‹ค์€", "์˜ˆ์€", "์ง€์•„", "์ˆ˜๋นˆ", "์†Œ์œจ", "์˜ˆ๋ฆฐ", "์˜ˆ์›", "์ง€์›", "์†Œ์œค", "์ง€์•ˆ", "ํ•˜๋ฆฐ", "์‹œ์€", "์œ ์ง„", "์ฑ„์€"]
    let randomTypes = ["ํœด๋Œ€์ „ํ™”", "๋Œ€ํ•œ๋ฏผ๊ตญ"]
    let randomDays = ["์˜ค๋Š˜", "์–ด์ œ", "๊ธˆ์š”์ผ", "๋ชฉ์š”์ผ", "์ˆ˜์š”์ผ", "ํ™”์š”์ผ", "์›”์š”์ผ"]
    
    init() {
        self.name = self.randomNames.shuffled().first!
        self.type = self.randomTypes.shuffled().first!
        self.day = self.randomDays.shuffled().first!
    }
}

class TableViewController: UITableViewController {
    var sectionA: [CallInfo] = []
    var sectionB: [CallInfo] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        (0..<100).forEach { _ in
            sectionA.append(CallInfo())
            sectionB.append(CallInfo())
        }
    }
.
.
.

์ผ๋‹จ ์ด์ „ ํฌ์ŠคํŠธ์—์„œ ๋งŒ๋“ค์—ˆ๋˜ ์ตœ๊ทผ ํ†ตํ™” ๋ชฉ๋ก์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ทฐ๊ฐ€ ์‹œ์ž‘๋  ๋•Œ 100๊ฐœ์˜ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‘ ๊ฐœ์˜ ์„น์…˜์— ๋„ฃ์–ด์ฃผ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. 

 

๊ทธ๋Ÿผ ์ด์ œ ์˜ค๋ฒ„๋ผ์ด๋“œ ๋˜์–ด ์žˆ๋Š” data source ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ?

class TableViewController: UITableViewController {
    var sectionA: [CallInfo] = []
    var sectionB: [CallInfo] = []
    var sections: [[CallInfo]] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        (0...100).forEach { _ in
            sectionA.append(CallInfo())
            sectionB.append(CallInfo())
        }
        sections = [sectionA, sectionB]
    }

    // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        switch section {
        case 0:
            return sectionA.count
        case 1:
            return sectionB.count
        default:
            return 0
        }
    }
.
.
.

numberOfSections์˜ ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ๋Š” ์„น์…˜์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•ด ์ฃผ๊ณ , numberOfRowsInSection๋Š” ๊ฐ ์„น์…˜์˜ ์•„์ดํ…œ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

์ด๋Œ€๋กœ ์‹คํ–‰ํ•ด๋ณผ๊นŒ์š”?

์—๋Ÿฌ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.. ์—๋Ÿฌ๋ฅผ ์ฝ์–ด๋ณด๋ฉด,

UITableView dataSource returned a nil cell for row at index path:~~~

 

๋ผ๊ณ  ํ•˜๋Š”๋ฐ์š”, ์•„๊นŒ Data Source๊ฐ€ ํ•˜๋Š” ์ผ ์ค‘์— ํ…Œ์ด๋ธ”์— ๊ฐ ํ–‰์— ํ•ด๋‹นํ•˜๋Š” ์…€์„ ์ œ๊ณตํ•œ๋‹ค๋Š” ๋‚ด์šฉ์ด ์žˆ์—ˆ๋˜ ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์‹œ๋‚˜์š”? ํ…Œ์ด๋ธ” ๋ทฐ๊ฐ€ UITableDataSource์—๊ฒŒ ์„น์…˜์˜ ๊ฐœ์ˆ˜์™€ ํ–‰์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฌผ์–ด๋ดค๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ์ •๋ณด๋ฅผ ์•Œ๋ ค์ฃผ์—ˆ์ง€๋งŒ ๊ฐ ํ–‰์— ์–ด๋–ค ๋‚ด์šฉ์„ ํ‘œ์‹œํ• ์ง€ ์•Œ๋ ค์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค! ํ…Œ์ด๋ธ” ๋ทฐ๋Š” ์ „๋‹ฌ๋ฐ›์€ ๊ฐœ์ˆ˜๋Œ€๋กœ ํ…Œ์ด๋ธ”์— ์…€์„ ํ‘œ์‹œํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ํ‘œ์‹œํ•˜๋ ค๋Š” index path์— ๋Œ€ํ•œ ์…€์„ data source์—๊ฒŒ ๋ฐ›์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. 

 

์…€์„ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ธฐ ์ „์— ์ƒ์†Œํ•œ ๋‹จ์–ด์ธ index path๋ถ€ํ„ฐ ์ •๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค! index path๋Š” ์ •์ˆ˜๊ฐ€ ๋‘ ๊ฐœ ๋“ค์–ด๊ฐ€ ์žˆ๋Š” ๋ฐฐ์—ด์„ ๊ฐ€์ง€๋Š” 2์ฐจ์› ๋ฐฐ์—ด์ธ๋ฐ์š”, ๊ฐ ํ–‰์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ [์„น์…˜ ์ธ๋ฑ์Šค, ํ–‰ ์ธ๋ฑ์Šค]๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋ง์ด์ฃ .

 

๊ทธ๋Ÿผ ์ด์ œ ์…€์„ ๋งŒ๋“ค์–ด์„œ ํ…Œ์ด๋ธ” ๋ทฐ์— ์ „๋‹ฌํ•ด๋ด…์‹œ๋‹ค!

 

์…€์„ ํ…Œ์ด๋ธ” ๋ทฐ์— ๋„˜๊ฒจ์ฃผ๊ธฐ ์œ„ํ•ด์„œ๋Š” UITableViewDataSource์— ์žˆ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ•˜๋‚˜ ๋” ๊ตฌํ˜„ํ•˜๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค! 

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

}

 

tableView ๋ฉ”์„œ๋“œ๋“ค ์ค‘์—์„œ ์ธ์ž๋กœ cellForRowAt์„ ๊ฐ€์ง€๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•˜๊ณ  ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ UITableCell์„ ๊ฐ€์ง€๊ณ  ์žˆ๋„ค์š”. ์ด๋•Œ ์…€์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด์ด dequeueResusableCell ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ธ๋ฐ์š”. ํ•œ๋ฒˆ ์‚ฌ์šฉํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋จผ์ € ์Šคํ† ๋ฆฌ๋ณด๋“œ๋กœ ์ด๋™ํ•ด์„œ ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ์„ ํƒํ•œ ํ›„์— ์šฐ์ธก ๋‚ด๋น„๊ฒŒ์ดํ„ฐ์˜ Content ์˜ต์…˜์„ Dynamic Prototype์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ค๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  prototype cell์„ ํ•˜๋‚˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์„ค์ •ํ• ๊ฒŒ์š”.

 

์ด์ œ ๋งŒ๋“ค์–ด์ง„ ํ…Œ์ด๋ธ” ๋ทฐ ์…€์„ ์„ ํƒํ•˜๊ณ  identifier๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ "cell"์ด๋ผ๊ณ  ์ €๋Š” ์„ค์ •ํ• ๊ฒŒ์š”.

 

์ด์ œ ๋‹ค์‹œ ์ฝ”๋“œ๋กœ ๋Œ์•„์™€์„œ dequeueResusableCell ๋ฉ”์„œ๋“œ๋กœ ์…€์„ ๋งŒ๋“ค์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. 

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    return cell
}

 

์ด๋ ‡๊ฒŒ withIndentifier ์ธ์ž์— ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ์„ค์ •ํ•œ identifier๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ์…€์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. cellForRowAt ๋ฉ”์„œ๋“œ๋Š” ์šฐ๋ฆฌ๊ฐ€ Data Source๋ฅผ ํ†ตํ•ด ํ…Œ์ด๋ธ” ๋ทฐ์— ์•Œ๋ ค์ฃผ์—ˆ๋˜ ํ–‰์˜ ๊ฐœ์ˆ˜๋งŒํผ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ˜„์žฌ ์–ด๋–ค ํ–‰์— ๋Œ€ํ•œ ์…€์„ ๋ฐ›๊ธฐ๋ฅผ ์›ํ•˜๋Š”์ง€ indexPath ์ธ์ž๋ฅผ ํ†ตํ•ด ์•Œ๋ ค์ค๋‹ˆ๋‹ค. indexPath๋ฅผ ์ถœ๋ ฅํ•ด๋ณด๋ฉด ํ™•์‹คํ•˜๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์ฃ .

 

๊ทธ๋Ÿฐ๋ฐ ๋ญ”๊ฐ€ ์ด์ƒํ•ฉ๋‹ˆ๋‹ค.. ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ชจ๋“  ์…€์˜ index path๊ฐ€ ๋ถˆ๋ ค์ง€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์Šคํฌ๋กค์„ ํ•  ๋•Œ๋งˆ๋‹ค index path๊ฐ€ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค. ์™œ ์ด๋Ÿด๊นŒ์š”? ์•„๋งˆ๋„ dequeueResusableCell ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์„ ๋ณด๋ฉด์„œ ์™œ dequeue๊ฐ€ ์žˆ์„๊นŒ.. ์™œ resusable ์ผ๊นŒ..๋ผ๋Š” ๊ถ๊ธˆ์ฆ์ด ๋“ค์ง€ ์•Š์œผ์…จ๋‚˜์š”?? dequeueResusableCell์˜ ์ •์ฒด์— ๋Œ€ํ•ด์„œ๋Š” ๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ ์•Œ์•„๋ณด๊ธฐ๋กœ ํ•˜๊ณ  ์ผ๋‹จ์€ cellForRowAt ๋ฉ”์„œ๋“œ๋กœ ๋งŒ๋“ค์–ด์ง„ ์…€์— ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ •๋ณด๋ฅผ ๋„ฃ์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

ContentConfiguration: ์…€์„ ๊พธ๋ฉฐ๋ณด์ž

์ปค์Šคํ…€ ์…€์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๋ทฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์…€์— ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ๊ฒƒ์œผ๋กœ ์…€์˜ ๋‚ด์šฉ์„ ์ฑ„์šธ ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ContentConfiguration์„ ํ†ตํ•ด์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ์Šคํƒ€์ผ๋กœ ์ž‘์—…์„ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ContentConfiguration์€ iOS 14๋ถ€ํ„ฐ ์„ค์ •ํ•˜๋Š” ๋ฐฉ์‹์ด ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๊ณ  ํ•˜๋„ค์š”.

 

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    
    var config = cell.defaultContentConfiguration()
    config.text = sections[indexPath.section][indexPath.item].name
    config.secondaryText = sections[indexPath.section][indexPath.item].type
    config.secondaryTextProperties.color = UIColor.darkGray
    cell.accessoryType = .detailButton
    cell.contentConfiguration = config

    return cell
}

 

์ฝ”๋“œ๋Š” ์ƒ๊ฐ๋ณด๋‹ค ๋‹จ์ˆœํ•ฉ๋‹ˆ๋‹ค! ๋จผ์ € dequeueReusableCell๋กœ ๋งŒ๋“  ์…€์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” defaultContentConfiguration์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. 

var config = cell.defaultContentConfiguration()

 

๊ทธ๋ฆฌ๊ณ  cellForRowAt์˜ ์ธ์ž๋กœ ์ „๋‹ฌ๋˜๋Š” indexPath๋ฅผ ์ด์šฉํ•ด์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. indexPath.section์œผ๋กœ ์„น์…˜ ์ธ๋ฑ์Šค๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ณ , indexPath.item์œผ๋กœ ํ–‰์˜ ์ธ๋ฑ์Šค๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Data Source์— ์ฒ˜์Œ ์ „๋‹ฌํ–ˆ๋˜ ์„น์…˜์˜ ๊ฐœ์ˆ˜์™€ ์„น์…˜๋‹น ํ–‰์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ indexPath๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ ํ–‰์— ์ผ์น˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฒ ์ฃ ?

 

callInfo ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋˜ ๋ฐ์ดํ„ฐ์ค‘ ์ด๋ฆ„์„ text๋กœ ์„ค์ •ํ•˜๊ณ , secondary text๋กœ๋Š” type ์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  secondary text๋Š” ์—ฐ๋ฝ์ฒ˜ ์•ฑ์— ๋‚˜์™€์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํšŒ์ƒ‰ ์ƒ‰์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค!

config.text = sections[indexPath.section][indexPath.item].name
config.secondaryText = sections[indexPath.section][indexPath.item].type
config.secondaryTextProperties.color = UIColor.darkGray

 

์ •๋ณด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ฒ„ํŠผ์ธ AccessoryView๋„ ๋„ฃ์–ด์•ผ๊ฒ ์ฃ ? ์ด์ „ ํฌ์ŠคํŠธ์—์„œ๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ Detail๋กœ ์„ค์ •ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

cell.accessoryType = .detailButton

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ด ์ธ์Šคํ„ด์Šค๋ฅผ ์ด ํ–‰์— ๋„ฃ์„ ์…€์˜ ContentConfiguration์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์…€์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์…€์ด ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค!

cell.contentConfiguration = config
return cell

 

์‹คํ–‰!

 

์›ํ•˜๋Š” ๋Œ€๋กœ ํ…Œ์ด๋ธ” ๋ทฐ๊ฐ€ ์™„์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค..!

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์„น์…˜์ด๋ฆ„์„ ํ‘œ์‹œํ•ด์ค„๊ฒŒ์š”. ์ด์ „ ํฌ์ŠคํŠธ์—์„œ๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ์—์„œ ์„น์…˜์˜ header๋ฅผ ์„ค์ •ํ•ด์ฃผ์—ˆ๋Š”๋ฐ์š”, ์ด๋ฒˆ์—๋Š” ํ…Œ์ด๋ธ” ๋ทฐ์˜ ๋ฉ”์„œ๋“œ๋“ค ์ค‘ titleForHeaderInSection๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•ด์„œ ์„น์…˜ ์ธ๋ฑ์Šค์— ๋งž๋Š” ์ œ๋ชฉ์„ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        switch section {
        case 0:
            return "A"
        case 1:
            return "B"
        default:
            return nil
        }
    }

 

์‹คํ–‰ํ• ๊ฒŒ์š”~!

 

์ด๋ฒˆ์—๋Š” ์„น์…˜ ์ด๋ฆ„๊นŒ์ง€ ์ž˜ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค~!

 

์ด๋ ‡๊ฒŒ ํ•ด์„œ UITableViewController๋ฅผ ์ƒ์†ํ•˜๋Š” ์ปค์Šคํ…€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ…Œ์ด๋ธ” ๋ทฐ๋ฅผ ๋งŒ๋“ค์–ด๋ดค์Šต๋‹ˆ๋‹ค! ์•„์ง ์ปค์Šคํ…€ ์…€์„ ๋งŒ๋“ค์–ด๋ณด์ง€ ์•Š์•˜๋Š”๋ฐ์š”, ๋‹ค์Œ ํฌ์ŠคํŠธ์—์„œ๋Š” dequeueResusableCell์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ๋จผ์ € ํ•˜๊ณ  ์ปค์Šคํ…€ ์…€์„ ๋งŒ๋“ค์–ด ํ…Œ์ด๋ธ” ๋ทฐ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~!