본문 바로가기
iOS/Swift

Swift) TableView

by Jiseong 2022. 2. 13.

TableView

TableView.. 직접 해보니 재밌다. 

 

좀 이상해 보이는 것도 막 고치고 싶어서 삽질도 좀 해보고

테이블 뷰는 여러 아이템을 List형태로 보여주기 위한 뷰이다.

 

데이터는 테이블 내부의 을 위치시켜 표현할 수 있다.

 

테이블 내부의 셀의 갯수가 많아지면 자동으로 스크롤 기능이 생겨서 뭐지 했는데 테이블 뷰는 스크롤뷰를 상속받고 있는 것을 확인 할 수 있었다.

 

난 스토리보드를 사용하여 생성해줬다.

이런식으로 뷰에 테이블 뷰 넣어주고, 테이블 뷰에 셀 넣어주고 해당 셀에 레이블 하나 넣어줬다.

 

셀은 커스텀셀을 만들어 스토리보드의 셀에 연결해주었다.

그리고 해당 셀을 사용하기위해선 identifer가 필요하다.

 

어떤 셀을 사용할건지 인식자의 역할을 한다. 

 

화면을 전환할 때 쓰는 identifier랑 똑같은 역할

커스텀 셀 안에 레이블도 연결해주었고..

 

테이블 뷰의 기능들을 사용하기 위해 해야될 필수적인 일의 목록들은UITableViewDataSource, UITableViewDelegate 내부에 위치하고 있다.

 

물론 Optional로 선언되어 쓸지말지 자유인 함수들도 많다.

TableView Delegate, DataSource

View는 스스로 작업을 처리하지 못한다. 

 

그래서 View는 Controller에게 자신에게 일어나는 이벤트에 대한 작업(CallBack)의 권한을 위임한다.

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    var firstName: [String] = ["임", "김", "이", "박", "최", "곽"]
    
    var sections: [String] = ["경기", "서울", "경상"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self // 대리자 : self
        tableView.dataSource = self // data 갖고 있는 자: self
//        tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 20))
    }
    
//    func numberOfSections(in tableView: UITableView) -> Int {
//        return sections.count
//    }
}

아래가 핵심이다.

tableView.delegate = self // 대리자 : self
tableView.dataSource = self // data 갖고 있는 자: self

다시 말하는거지만 MV(X) 구조에서 View는 주도적으로 작업을 처리할 권한이 없다.

 

하지만 이벤트가 발생하는 부분은 분명 View 부분이기 때문에 이를 처리해줄 놈이 필요하다.

 

즉, 위 코드는 self(VC)는 테이블 뷰에 데이터를 공급해주고, 뷰에서 발생하는 이벤트를 self(VC)가 처리하고 관리한 후 다시 뷰에 뿌려줄 것임을 선언한 것이다.

 

두 프로토콜을 채택한후 필수적으로 정의해야하는 메소드는 UITableViewDataSource에 2가지가 있다.

extension ViewController: UITableViewDataSource {
//    var firstName: [String] = ["임", "김", "이", "박", "최", "곽"]
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return firstName.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        
        cell.label.text = firstName[indexPath.row]
        
        return cell
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return sections[section]
    }
    
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 10.0
    }
}

DataSource를 채택한 코드다.

 

밑에 2개는 무시하고 우선 numberOfRowsInSection부터 보자

 

numberOfRowsInSection 함수는 한 섹션 안에 있는 row(열, 세로)의 갯수를 리턴해주는 함수이다.

 

미리 정의한 firstName이라는 배열엔 6개의 요소가 있다.

 

테이블 뷰가 생성되기 전 자동으로 호출되어 한 섹션안에 6개의 열을 생성해준다.

두번째로 cellForRowAt은 테이블 뷰의 특정 인덱스에 삽입할 셀에 대한 데이터 소스를 요청하는 메소드...는 그냥 셀을 만들어주는 메소드라고 생각하면 될 것 같다.

 

리턴타입이 이잖아요

 

근데 내부에 dequeueReusableCell(withIdentifier:for:) 라는 메소드가 보인다. 

 

identifier에는 위쪽에서 설명한 cell에 설정한 identifier를 적어주면된다. 

 

dequeueReusableCell 메소드는 메모리를 줄이기위해 사용된다.

 

만약 테이블뷰에 천개, 만개의 셀이 있다고 생각해보면 그 많은 셀에 대한 메모리 할당이 이루어지게 된다.

 

하지만 dequeueReusableCell을 사용하면 현재 사용자에게 보이는 테이블 뷰의 셀 갯수만을 메모리에 할당한다.

 

만일 사용자가 스크롤을 1개의 셀만큼 내리면 이전에 보였던 List의 첫번째 cell은 사용자 눈에 보이지 않게 되어 queue에서 pop되고 새로운 cell이 queue에 push되며 화면에 보일 셀의 일정한 갯수들만 메모리에 할당하는 것이다.

 

그래서 한번 테스트해봤다.

호출되는 시점은 눈에 보이는 리스트의 가장 첫번째 셀이 사라질 때 함수가 호출되는 모습을 볼 수 있다.

 

마지막에 함수 여러번 걸리는 건 스크롤 확 올려서 여러개가 한번에 사라져서 그런 것이다. 

 

근데 dequeueReusableCell 메소드는 반환 타입이 UITableViewCell 타입이라서 개발자의 커스텀 cell의 타입과 맞지 않아 타입 캐스팅을 해줘야한다.

 

난 일단 공부 차원이라 강제로 해줬는데 안전하게 하려면

guard let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableViewCell else { return .init() }

이렇게 해주면 되겠지?

 

다음은 Delegate 메소드 구현

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let alert = UIAlertController(title: "선택", message: "난 선택받았다!!!", preferredStyle: .alert)
        
        let action = UIAlertAction(title: "OK", style: .default)
        
        alert.addAction(action)
        
        present(alert, animated: false, completion: nil)
        
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

Delegate 내부 메소드는 전부 Optioanl 이다. 

 

Delegate는 테이블뷰의 시각적인 부분 수정, 행의 선택 관리, 행 편집을 도와준다

 

난 셀이 선택되었을 때 구현 해주고싶은 것이 있어 didSelectRowAt 메소드를 구현하였다.

 

내부 구현은.. 얼럿밖에 없어서리 설명할 것이 없다.

 

아 deselectRow 메소드는 사용자가 셀을 선택하였을 때 회색(default)으로 진해지는데 한번 선택하면 이게 바뀌질 않았고 마음에 들지 않았다.

 

그래서 구현해주었다.

 

밑에는 deselectRow 적용 결과이다.

좌측이 deselectRow  적용 전, 우측이 deselectRow  적용 후

 

적용하면 순간적으로 회색이 되었다가 사라지는 것을 볼 수 있다.




어우 머리아퍼.. 맥주..?

'iOS > Swift' 카테고리의 다른 글

Swift) 문자열의 특정 범위의 속성을 변경해보자 NSMutableAttributedString  (0) 2022.02.13
Swift) Codable, Decode, Encode  (0) 2022.02.13
Swift) Delegate  (0) 2022.02.13
SOLID  (0) 2022.02.13
Swift) App LifeCycle  (0) 2022.02.13

댓글