์ƒ์„ธ ์ปจํ…์ธ 

๋ณธ๋ฌธ ์ œ๋ชฉ

[UIKit] UITableView ์ฝ”๋“œ๋ฒ ์ด์Šค ์‚ฌ์šฉ๋ฒ•

๐ŸŽ iOS/UIKit

by AHN.Jihyeon 2024. 7. 4. 00:09

๋ณธ๋ฌธ

โœ… TableView(ํ…Œ์ด๋ธ”๋ทฐ)

: ๋‚ด๋ถ€์— ์…€์ด ์กด์žฌํ•˜๊ณ  ์„ธ๋กœ๋กœ๋งŒ ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•œ ๋ทฐ

๊ทธ๋ฃน์„ ์ง€์„ ์ˆ˜๋„ ์žˆ์Œ

 

 

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ํŒจํ„ด๊ณผ ํ•จ๊ป˜ํ•˜๋Š” ๊ฐœ๋…

 

 

โœ… ํ…Œ์ด๋ธ”๋ทฐ์˜ ๊ตฌ์กฐ

 

ViewController(๊ฐ์ฒด)

tableView(๊ฐ์ฒด)

tableViewCell(๊ฐ์ฒด)

 

ViewController์™€ tableView ๊ฐ„์— ์„œ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ์˜์‚ฌ์†Œํ†ต ํ•„์š”ํ•œ๋ฐ

์ด๊ฒƒ์„ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ํŒจํ„ด์œผ๋กœ ๊ตฌ์„ฑํ•œ๋‹ค. 


UITableVivewController๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” UITableViewDatSource ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•ด์ค˜์•ผ ํ•œ๋‹ค.

์ด ํ”„๋กœํ† ์ฝœ์€ tableView์™€ ViewController๊ฐ€ ํ†ต์‹ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. 

์ปจํ…์ธ ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋“ค์„ ํ‘œ์‹œํ•  ๊ฑด์ง€์— ๋Œ€ํ•œ ์†Œํ†ต. ์…€์€ ๋ช‡๊ฐœ ํ•„์š”ํ•œ์ง€, ์…€์€ ์–ด๋–ป๊ฒŒ ํ‘œ์‹œํ•ด์•ผํ•˜๋Š”์ง€

 

 

โœ… UITableView ์„ค์ • ์ˆœ์„œ 

1. UITableView ์„ ์–ธ ๋ฐ ์ƒ์„ฑ

2. delegate์™€ dataSource ํ”„๋กœํ† ์ฝœ ์ฑ„ํƒ 

// UITableView ์ธ์Šคํ„ด์Šค ์„ ์–ธ
var tableView: UITableView = {

	// UITableView ์ธ์Šคํ„ด์Šค ์ดˆ๊ธฐํ™” ๋ฐ ์„ค์ •
	let tableView = UITableView()
    
	tableView.delegate = self   //tableView์˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์†์„ฑ ์„ธํŒ…์„ ํ•ด๋‹น ๋ทฐ์ปจ์—์„œ ๋Œ€์‹  ์„ธํŒ…
	tableView.dataSource = self //tableView ์•ˆ์— ์ง‘์–ด ๋„ฃ์„ ๋ฐ์ดํ„ฐ๋“ค์„ ํ•ด๋‹จ ๋ทฐ์ปจ์—์„œ ์„ธํŒ… 
    tableView.register(CustumTableViewCell.self, forCellReuseIdentifier: CustumTableViewCell.id)
	return tableView
}

 

 

3-1. UITableViewDataSource ํ”„๋กœํ† ์ฝœ์˜ ํ•„์ˆ˜ ๋ฉ”์„œ๋“œ ๊ตฌํ˜„

ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํด๋ž˜์Šค ํ•˜๋‹จ์— extension์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค. 

 

tableView(_:numberOfRowsInSection:)์™€ tableView(_:cellForRowAt:) 

extension ViewController: UITableViewDataSource {  // ์ฑ…์ž„์„ ๋ทฐ์ปจ์— ์œ„์ž„
    // ํ…Œ์ด๋ธ”๋ทฐ ์„น์…˜ ๋‚ด ํ–‰์˜ ๊ฐœ์ˆ˜ ๋ฐ˜ํ™˜
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10  // ์˜ˆ์‹œ๋กœ 10๊ฐœ์˜ ํ–‰์„ ๋ฐ˜ํ™˜
    }

    // ๊ฐ ํ–‰์— ํ‘œ์‹œํ•  ์…€ ๋ฐ˜ํ™˜
    // indexPath = ํ…Œ์ด๋ธ”๋ทฐ์˜ ํ–‰๊ณผ ์„น์…˜์„ ์˜๋ฏธ
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: CustumTableViewCell.id) as? TableViewCell else { return }
        cell.configureCell()
        return cell
    }
}

 

identifier๋Š” ๊ฐ Cell์˜ ํด๋ž˜์Šค๋ช…๊ณผ ๋™์ผํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ์ž‘์—…ํ•  ๋•Œ ํŽธํ•˜๋‹ค!!

 

 

 

 

3-2. UITableViewDelegate ํ”„๋กœํ† ์ฝœ์˜ ๋ฉ”์„œ๋“œ๋Š” ์„ ํƒ ์‚ฌํ•ญ

์…€ ์„ ํƒ, ์…€ ๋†’์ด ์„ค์ •, ์„ค ํŽธ์ง‘, ์„น์…˜ ํ—ค๋” ๋ฐ ํ‘ธํ„ฐ์˜ ๋ทฐ ์ปจ์Šคํ„ฐ๋งˆ์ด์ง• ๋“ฑ ์ฃผ๋กœ ํ…Œ์ด๋ธ”๋ทฐ ๋™์ž‘์„ ์‚ฌ์šฉ์ž ์ •์˜ํ•œ๋‹ค. 

  • ์…€ ๋†’์ด ์„ค์ • ๋ฉ”์„œ๋“œ
extension ViewController: UITableViewDelegate {  // ์ฑ…์ž„์„ ๋ทฐ์ปจ์— ์œ„์ž„
    // ํ…Œ์ด๋ธ”๋ทฐ ์„น์…˜ ๋‚ด ํ–‰์˜ ๊ฐœ์ˆ˜ ๋ฐ˜ํ™˜// ๊ฐ ํ–‰์˜ ๋†’์ด๋ฅผ ์„ค์ •
	func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    	return 44.0 // ์›ํ•˜๋Š” ๋†’์ด
    }
}
  • ์…€ ์„ ํƒ ๊ด€๋ จ๋ฉ”์„œ๋“œ
// ์…€์ด ์„ ํƒ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // ์…€ ์„ ํƒ ์‹œ ์‹คํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์—ฌ๊ธฐ์— ์ž‘์„ฑ
}

// ์…€ ์„ ํƒ์ด ํ•ด์ œ๋˜์—ˆ์„ ๋•Œ ํ˜ธ์ถœ
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    // ์…€ ์„ ํƒ ํ•ด์ œ ์‹œ ์‹คํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์—ฌ๊ธฐ์— ์ž‘์„ฑ
}
  • ์„น์…˜ ํ—ค๋” ๋ฐ ํ‘ธํ„ฐ ๋ทฐ ์„ค์ • ๋ฉ”์„œ๋“œ
// ์„น์…˜ ํ—ค๋” ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerView = UIView()
    // ํ—ค๋” ๋ทฐ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
    return headerView
}

// ์„น์…˜ ํ‘ธํ„ฐ ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
    let footerView = UIView()
    // ํ‘ธํ„ฐ ๋ทฐ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
    return footerView
}

// ์„น์…˜ ํ—ค๋”์˜ ๋†’์ด๋ฅผ ์„ค์ •
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 20.0
}

// ์„น์…˜ ํ‘ธํ„ฐ์˜ ๋†’์ด๋ฅผ ์„ค์ •
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return 20.0
}
  • ์…€ ํŽธ์ง‘ ๊ด€๋ จ ๋ฉ”์„œ๋“œ 
// ์…€์„ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋ฐ˜ํ™˜
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    return true
}

// ์…€์ด ํŽธ์ง‘ ๋ชจ๋“œ๋กœ ๋“ค์–ด๊ฐ”์„ ๋•Œ ํ˜ธ์ถœ
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {
        // ์…€ ์‚ญ์ œ ์‹œ ์‹คํ–‰ํ•  ์ฝ”๋“œ
    }
}

 

 

4. TableViewCell ํด๋ž˜์Šค ์ •์˜

  • ๊ธฐ๋ณธ ์…€ ์ ์šฉ ๋ฐฉ๋ฒ•
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

 

  • ์ปค์Šคํ…€ ์…€ ์ ์šฉ  ๋ฐฉ๋ฒ•
import UIKit
//ํ…Œ์ด๋ธ”๋ทฐ ์…€ ํด๋ž˜์Šค ์ •์˜ 
class CustomTableViewCell: UITableViewCell {
	static let id  = "cellID"
    
    let customLabel: UILabel = {
        let label = UILabel()
        label.backgroundColor = .black
        label.textColor = .white
        return label
    }()

	//ํ…Œ์ด๋ธ”๋ทฐ์˜ Style๊ณผ id๋กœ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ 
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
		configureUI()
    }

	//์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”๋ฅผ ํ†ตํ•ด ์…€์„ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ
    //fatalError๋ฅผ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”๋กœ ์ดˆ๊ธฐํ™”ํ•˜์ง€ ์•Š์Œ์„ ๋ช…์‹œ 
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configureUI(){
    //UIViewController๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ view๋ฅผ, TableViewCell๋„ contentView๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ–๊ณ  ์žˆ๋‹ค. 
    contentView.backgroundColor = .black
    contentView.addSubview(customLabel)
    //์ƒ์„ธ ์˜คํ† ๋ ˆ์ด์•„์›ƒ ์ž‘์„ฑ 
    }
    
    //์…€์„ ์„ค์ •ํ•ด์ฃผ๋Š” ์™ธ๋ถ€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ
    public func configureCell(){
    }
    
}

 

 

 

 

 

 

 


 

import UIKit
import SnapKit


//MakeCell ํด๋ž˜์Šค: ํ…Œ์ด๋ธ”๋ทฐ์…€ ํด๋ž˜์Šค
class MakeCell: UITableViewCell {
    let contentLabel = UILabel()
    
    //์…€์˜ ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        contentView.addSubview(contentLabel)
        contentLabel.snp.makeConstraints {
            $0.edges.equalToSuperview().inset(10)
        }
    }
    
    /*
     UITableViewCell์„ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ๋Š”
     ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๋กœ, ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ๊ฐ€ ์žˆ๋‹ค.
     
     1. ์ฝ”๋“œ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒฝ์šฐ (init(style:reuseIdentifier:))
     2. ์Šคํ† ๋ฆฌ๋ณด๋“œ ๋˜๋Š” XIB ํŒŒ์ผ๋กœ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๊ฒฝ์šฐ (init(coder:))
     */
    
    //init(coder:)๋Š” ์Šคํ† ๋ฆฌ๋ณด๋“œ๋‚˜ XIB ํŒŒ์ผ๋กœ ์…€์„ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์—
    //์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ fatalError๋ฅผ ๋ฐœ์ƒ์‹œ์ผœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ์„ ๋ช…์‹œ
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //์…€์— ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ
    func configure(){
        contentLabel.text = "Sample Text"
    }
}




//ViewController ํด๋ž˜์Šค
class ViewController: UIViewController, UITableViewDataSource {
    
    //ํ…Œ์ด๋ธ”๋ทฐ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    private let tableView = UITableView()
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        //ํ…Œ์ด๋ธ”๋ทฐ์˜ ์†์„ฑ์— dataSource๊ฐ€ ์žˆ์Œ
        //์—ฌ๊ธฐ์„œ self๋Š” ViewController์˜ ์ธ์Šคํ„ด์Šค -> ์ด ํ…Œ์ด๋ธ”๋ทฐ์˜ ๋Œ€๋ฆฌ์ž๊ฐ€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋œ๋‹ค๋Š” ์˜๋ฏธ
        tableView.dataSource = self //UITableViewDataSource๋ฅผ ์ฑ„ํƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— dataSource์— self๋ฅผ ํ• ๋‹น(๋ธ๋ฆฌ๊ฒŒ์ดํŠธ์™€ ๋น„์Šท) -> ํ…Œ์ด๋ธ”๋ทฐ ์…€์„ ๋ช‡๊ฐœ์˜ ์…€ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ• ๊ฑด์ง€์— ๋Œ€ํ•ด ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
        tableView.delegate = self  //UITableViewDelegate ํ”„๋กœํ† ์ฝœ์„ ์ฑ„ํƒ ->ํ…Œ์ด๋ธ”๋ทฐ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กค/ํ„ฐ์น˜ ๋“ฑ์˜ ์ „๋‹ฌ ๋ฐ›์€ ํ™œ๋™๋“ค์„ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌ์—๊ฒŒ ์ „๋‹ฌ
        
        tableView.rowHeight = 70 //์…€ ํ•˜๋‚˜ํ•˜๋‚˜์˜ ๋†’์ด
        
        //cell์„ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์žฌ์‚ฌ์šฉ ์‹๋ณ„์ž๋ฅผ ํ…Œ์ด๋ธ”๋ทฐ์— ๋“ฑ๋ก
        tableView.register(MakeCell.self, forCellReuseIdentifier: "cellID")
        
        //ํ…Œ์ด๋ธ”๋ทฐ ์˜คํ† ๋ ˆ์ด์•„์›ƒ ์„ค์ • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        setupTableviewConstraints()
    }
    
    //ํ…Œ์ด๋ธ”๋ทฐ ์˜คํ† ๋ ˆ์ด์•„์›ƒ ์„ค์ •
    func setupTableviewConstraints(){
        view.addSubview(tableView)
        tableView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }
    }
    
    
    //UITableViewDataSource ํ”„๋กœํ† ์ฝœ ๋ฉ”์„œ๋“œ : ํ…Œ์ด๋ธ”๋ทฐ๊ฐ€ ๋ทฐ์ปจํŠธ๋กค๋Ÿฌํ•œํ…Œ ๋ช‡ ๊ฐœ์˜ ํ–‰์˜ ๊ฐœ์ˆ˜๊ฐ€ ํ•„์š”ํ•œ์ง€ ๋ฌผ์–ด ๋ณด๋Š” ์—ญํ• 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //return cartArray.count  //์•„์ง ๊ฐ’์„ ๋ฐ›์•„ ์˜ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋‹จ ์ฃผ์„ -> ๋ฆฌํ„ด๊ฐ’์€ ํ…Œ์ด๋ธ”๋ทฐ์— ์ „๋‹ฌ
        return 5
    }
    
    // UITableViewDataSource ํ”„๋กœํ† ์ฝœ ๋ฉ”์„œ๋“œ: register์‹œ ๋„˜๊ฒจ ์ฃผ์—ˆ๋˜ cellID๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํŠน์ • ํ–‰์˜ cell์„ ๋ถˆ๋Ÿฌ ์˜ค๊ธฐ(cellForRowAt) ์œ„ํ•œ ์ž‘์—…
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // ์…€์„ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ์—์„œ ๊บผ๋‚ด์„œ ์บ์ŠคํŒ…
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath) as? MakeCell else {   //MakeCell์„ ํƒ€์ž… ์บ์ŠคํŒ…
            return UITableViewCell()
        }
        
        cell.configure() // ์…€์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
        cell.selectionStyle = .none // ์…€ ์„ ํƒ ์‹œ ์ƒ‰์ƒ ๋ณ€ํ•˜์ง€ ์•Š๊ฒŒ ์„ค์ •
        
        return cell
    }
    
}

 

 

 

 

https://odinios.tistory.com/2

๊ด€๋ จ๊ธ€ ๋”๋ณด๊ธฐ