훌이
후리스콜링개발
훌이

블로그 메뉴

  • 왈 (iOS APP)
  • Github
전체 방문자
오늘
어제
  • 전체 (171)
    • ⭐️ 개발 (140)
      • JAVA (4)
      • Web (5)
      • iOS & Swift (94)
      • iOS Concurrency (4)
      • Rx (18)
      • Git (6)
      • WWDC (1)
      • Code Refactor (3)
      • Server (1)
    • ⭐️ Computer Science (22)
      • 운영체제 (10)
      • 네트워크 (5)
      • PS (7)
    • 경제시사상식 (8)
    • 기타 등등 (0)

인기 글

최근 글

06-08 02:11

티스토리

hELLO · Designed By 정상우.
훌이
⭐️ 개발/iOS & Swift

[iOS] Expandable TableView Cell 만들기

[iOS] Expandable TableView Cell 만들기
⭐️ 개발/iOS & Swift

[iOS] Expandable TableView Cell 만들기

2021. 6. 16. 19:09
728x90
반응형

📦 Expandable TableView Cell 만들기

 

마켓컬리 클론코딩을 하다가 카테고리를 접었다가 펴주는 테이블 뷰 기능을 구현하게 되어

공부한 내용을 정리해보려고 합니다!

 

처음에는 어려울 것 같았는데

생각보다 엄청 간단하게 구현이 되어서 라이브러리를 쓰지 않아도 되겠다 싶었어요!

(맨 아래로 내려가면 full code 보실 수 있습니다!)

 


테이블 뷰를 위한 세팅을 해주세요~~

 

첫 번째로, 테이블 뷰에 들어갈 데이터를 위한 구조체를 하나 만들어 줍니다.

struct cellData {
    var opened = Bool()
    var title = String()
    var sectionData = [String]()
}

- opened는 테이블 뷰 셀이 접혔는지 펴졌는지를 확인해주기 위한 변수입니다.

- title은 카테고리에 해당하는 문자열 변수고,

- sectionData는 카테고리 내 아이템들에 해당하는문자열 리스트 변수입니다.

 

 

그리고 테이블 뷰에 들어갈 데이터를 위한 리스트를 하나 선언해주고

내부에 데이터를 입력해줍니다.

class ViewController : UIViewController {
    
    // MARK: - Data
    
    
    var tableViewData = [cellData]() // 리스트 선언 부분
    
    
    // MARK: - Property

    var tableView = UITableView()
    
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 여기 이 부분에 데이터 넣어줍니다
        tableViewData = [cellData(opened: false, title: "Section1", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section2", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section3", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section4", sectionData: ["Cell1", "Cell2", "Cell3"])]
        
        tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        
        tableView.delegate = self
        tableView.dataSource = self
        
        configureUI()
        
    }
    

 

 

 

두 번째로,  UITableViewDataSource 부분을 채워줍시다~~

 

1 . Section의 개수

func numberOfSections(in tableView: UITableView) -> Int {
     return tableViewData.count
}

 

2. Row의 개수

  • 섹션이 열렸을 때랑 닫혔을 때에 따라 분기 처리를 해줘야 합니다.
  • 섹션이 열린 경우에는 내부에 들어갈 데이터 개수에 카테고리 제목 셀까지 하나 추가해줘야 합니다. 그래서 + 1 을 해줍니다.
  • 섹션이 닫힌 경우에는 카테고리 제목 셀 하나만 보여주면 되기 때문에 return 1을 해줍니다.
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableViewData[section].opened == true {
            // tableView Section이 열려있으면 Section Cell 하나에 sectionData 개수만큼 추가해줘야 함
            return tableViewData[section].sectionData.count + 1
        } else {
            // tableView Section이 닫혀있을 경우에는 Section Cell 하나만 보여주면 됨
            return 1
        }
    }

 

 

3. Row에 들어갈 Cell 등록하기

  • indexPath.row 가 0인 경우.. 즉, 카테고리 제목인 경우에는 title을 뿌려줍니다.
  • else 그 외에는 sectionData를 뿌려줘야 합니다.

 

📍여기서 중요한 점은 sectionData[indexPath.row - 1] 인데요.

- 1 을 해주는 이유는 카테고리 제목 부분은 빼야 하기 때문입니다.

카테고리 제목 부분을 제외하고 그 아래부터 데이터를 뿌려줘야 하기 때문에 -1을 해줍니다!

 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     // section 부분 코드
        if indexPath.row == 0 {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
                    as? TableViewCell else { return UITableViewCell() }
            cell.configureUI()
            cell.tableLabel.text = tableViewData[indexPath.section].title
            return cell
            
        // sectionData 부분 코드
        } else {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
                    as? TableViewCell else { return UITableViewCell() }
            cell.configureUI()
            cell.tableLabel.text = tableViewData[indexPath.section].sectionData[indexPath.row - 1]
            return cell
        }
        
}

 

 

 

세 번째로,  셀 선택 시 접었다 펴지는 부분을 구현해줍니다~~

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
    // 셀 선택 시 회색에서 다시 변하게 해주는 것
    tableView.deselectRow(at: indexPath, animated: true)
    
    // section 부분 선택하면 열리게 설정
    if indexPath.row == 0 {
        // section이 열려있다면 다시 닫힐 수 있게 해주는 코드
        tableViewData[indexPath.section].opened = !tableViewData[indexPath.section].opened
        
        // 모든 데이터를 새로고침하는 것이 아닌 해당하는 섹션 부분만 새로고침
        tableView.reloadSections([indexPath.section], with: .none)
    
    // sectionData 부분을 선택하면 아무 작동하지 않게 설정
    } else {
        print("이건 sectionData 선택한 거야")
    }
    
    print([indexPath.section], [indexPath.row])

    
}

 

didSelectRowAt 함수를 사용하면 셀 선택에 따른 동작 부분을 구현할 수 있죠?

 

카테고리 제목 부분을 눌렀을 때 셀이 펼쳐지는 걸 원하기 때문에 분기처리를 꼭 해줘야 합니다!

(안하면 카테고리 내부 아이템을 눌렀을 때도 펼쳐진다는 점..!)

 

 

1️⃣ tableViewData[indexPath.section].opened = !tableViewData[indexPath.section].opened

이 부분 코드는 아래 코드와 같은 의미입니다.

 

     if tableViewData[indexPath.section].opened == true {

                // 그리고 펼쳐져 있을 때 셀을 누르면 다시 닫혀줘야 하기 때문에 false로 바꿔줘야 함..

                tableViewData[indexPath.section].opened = false

                

            } else {

                // 처음 기본값이 false니까 얘를 펼쳐주려면 true로 바꿔줘야 함..

                tableViewData[indexPath.section].opened = true

            }

 

 

 

 

2️⃣ tableView.reloadSections([indexPath.section], with: .none)

이 부분은 변경하고 나서 해당하는 섹션 부분만 데이터를 새로고침 해주는 코드입니다.

 

 

 

끝입니다! 간단하져?

생각보다 엄청 간단해요~!~!

 


 

결과 화면입니다~

 

 

 

 

 

 

 

 


 

full code

- 저는 스냅킷을 사용했고 스토리보드를 쓰지 않아서 따로 테이블 뷰 셀 코드도 추가해두겠습니다 ~ 

- 깃허브 링크도 걸어두겠습니다 : 🔗🔗🔗

 

< ViewController >

import UIKit

import SnapKit

struct cellData {
    var opened = Bool()
    var title = String()
    var sectionData = [String]()
}

class ViewController : UIViewController {
    
    // MARK: - Data
    
    var tableViewData = [cellData]()
    
    
    // MARK: - Property

    var tableView = UITableView()
    
    
    // MARK: - Lifecycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableViewData = [cellData(opened: false, title: "Section1", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section2", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section3", sectionData: ["Cell1", "Cell2", "Cell3"]),
                         cellData(opened: false, title: "Section4", sectionData: ["Cell1", "Cell2", "Cell3"])]
        
        tableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        
        tableView.delegate = self
        tableView.dataSource = self
        
        configureUI()
        
    }
    
    
    // MARK: - UI
    
    func configureUI() {
        
        view.addSubview(tableView)
        
        tableView.snp.makeConstraints { (make) in
            make.top.leading.bottom.trailing.equalToSuperview()
        }
    }
}


// MARK: - UITableViewDelegate

extension ViewController : UITableViewDelegate {
    
}


// MARK: - UITableViewDataSource

extension ViewController : UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return tableViewData.count
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableViewData[section].opened == true {
            // tableView Section이 열려있으면 Section Cell 하나에 sectionData 개수만큼 추가해줘야 함
            return tableViewData[section].sectionData.count + 1
        } else {
            // tableView Section이 닫혀있을 경우에는 Section Cell 하나만 보여주면 됨
            return 1
        }
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // section 부분 코드
        if indexPath.row == 0 {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
                    as? TableViewCell else { return UITableViewCell() }
            cell.configureUI()
            cell.tableLabel.text = tableViewData[indexPath.section].title
            return cell
            
        // sectionData 부분 코드
        } else {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
                    as? TableViewCell else { return UITableViewCell() }
            cell.configureUI()
            cell.tableLabel.text = tableViewData[indexPath.section].sectionData[indexPath.row - 1]
            return cell
        }
        
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        // 셀 선택 시 회색에서 다시 변하게 해주는 것
        tableView.deselectRow(at: indexPath, animated: true)
        
        // section 부분 선택하면 열리게 설정
        if indexPath.row == 0 {
            // section이 열려있다면 다시 닫힐 수 있게 해주는 코드
            tableViewData[indexPath.section].opened = !tableViewData[indexPath.section].opened
            
            // 모든 데이터를 새로고침하는 것이 아닌 해당하는 섹션 부분만 새로고침
            tableView.reloadSections([indexPath.section], with: .none)
        
        // sectionData 부분을 선택하면 아무 작동하지 않게 설정
        } else {
            print("이건 sectionData 선택한 거야")
        }
        
        print([indexPath.section], [indexPath.row])

        
    }
    
}

 

 

< TableViewCell >

import UIKit

import SnapKit

class TableViewCell: UITableViewCell {

    static let identifier = "TableViewCell"
    
    // MARK: - Property
    
    let tableLabel : UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 15, weight: .regular)
        return label
    }()
    
    
    // MARK: - configureUI
    
    func configureUI() {
        addSubview(tableLabel)
        
        tableLabel.snp.makeConstraints { (make) in
            make.centerY.equalToSuperview()
            make.leading.equalTo(20)
        }
        
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

    }

}
728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'⭐️ 개발 > iOS & Swift' 카테고리의 다른 글

[iOS] Alamofire Multipart/formdata를 통한 이미지 서버 통신  (3) 2021.07.19
[iOS] DateFormatter  (2) 2021.06.24
[iOS] UIColor Extension 파일 만드는 법 (feat.Zeplin)  (1) 2021.06.07
[iOS] NSMutableAttributedString : 문자열 특정 부분 색 바꿔주고 싶을 때  (0) 2021.06.07
[iOS] Then / DuctTape 라이브러리 사용기  (0) 2021.06.07
  • 📦 Expandable TableView Cell 만들기
  • 첫 번째로, 테이블 뷰에 들어갈 데이터를 위한 구조체를 하나 만들어 줍니다.
  • 두 번째로,  UITableViewDataSource 부분을 채워줍시다~~
  • 세 번째로,  셀 선택 시 접었다 펴지는 부분을 구현해줍니다~~
  • 결과 화면입니다~
  •  
  • full code
'⭐️ 개발/iOS & Swift' 카테고리의 다른 글
  • [iOS] Alamofire Multipart/formdata를 통한 이미지 서버 통신
  • [iOS] DateFormatter
  • [iOS] UIColor Extension 파일 만드는 법 (feat.Zeplin)
  • [iOS] NSMutableAttributedString : 문자열 특정 부분 색 바꿔주고 싶을 때
훌이
훌이

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.