Intro

UITableView 를 별 다른 설정없이 인스턴스화해서 사용해보면 Style 기본값이 .plain 으로 설정되어 있어 Header 를 사용하는 경우 스크롤을 하더라도 바로 사라지는 것이 아니라 계속 남아있게 되는데요. 이것을 해결하려면 tableView 의 Style 을 .grouped 로 지정해주면 Header 가 다른 셀들과 같은 방식으로 스크롤되게 됩니다.

그런데 이렇게 사용하면 문제점이 하나 발생하는데 우리는 Footer 가 필요없고 Header 만 필요한 경우에도 자동적으로 Footer 까지 생성되는 상황이 발생하는거에요. 이것 때문에 골치를 여러번 썩으면서도 공부해서 해결하기보다 바쁘다는 핑계로 여태까지 우회하는 방법으로 해결을 했었거든요. 그런데 오늘 프로젝트를 진행하면서 같은 상황을 또 마주치게 되어 확실히 해결하는 방법을 공부해보았습니다.


Preconfigure

간단하게 연습해보기 위해 ViewController 에 Grouped Style 로 UITableView 를 하나 올려놓았어요. 그리고 tableView(_:viewForHeaderInSection:) 를 구현하고 회색 BackgroundColor 를 가진 뷰를 리턴했습니다.

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let view = UIView()
    view.backgroundColor = .gray
    return view
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 10
}

.plain Style 에서 이렇게 구현했다면 Header 만 생성되었을텐데요. Apple 이 기본세팅을 왜 이렇게 해놓았는지 모르겠지만 .grouped Style 에서는 자동적으로 Footer 뷰까지 생성이 됩니다.

Footer 뷰가 자동적으로 생성된 모습

우리는 분명히 Header 를 회색으로 지정해 놓았는데 빨간색 화살표로 표시된 부분에 우리는 생성한적 없는 다른 뷰가 하나 있잖아요? 저게 바로 Footer 입니다. 자동으로 생성되었어요. 처음에는 저게 Footer 라는 사실을 몰라서 더 많이 헤맸던 것 같아요. 알고나니 해결법이 정말 간단했습니다.


Solution

그럼 코드를 작성해볼게요.

tableView.tableFooterView = UIView(frame: .zero)
tableView.sectionFooterHeight = 0

직접 UIView 를 생성해 Footer 뷰로 지정해주고 높이를 0 으로 바꿔주었어요. 이유는 알 수 없지만 기본적으로 제공되는 Footer 의 높이를 0 으로 바꾸는 방법은 통하지 않더라고요. 이제 이렇게 세팅하고 Simulator 를 실행해 볼게요.

Footer 뷰가 사라진 모습

오!! 사라졌어요. 처음부터 기본값이 이렇게 세팅되어 있었다면 더 편했겠지만 굳이 그렇게하지 않은 이유가 분명 Apple 에게는 있을거에요. 그럴거에요.


Entire Code

import UIKit

class ViewController: UIViewController {

    // MARK: - Properties
    let tableView = UITableView(frame: .zero, style: .grouped)

    // MARK: - Lifecycle
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    // MARK: - UI
    private func configureUI() {
        setAttributes()
        setContraints()
    }

    private func setAttributes() {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.tableFooterView = UIView(frame: .zero)
        tableView.sectionFooterHeight = 0
    }

    private func setContraints() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

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

extension ViewController: UITableViewDelegate {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 10
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = UIView()
        view.backgroundColor = .gray
        return view
    }

    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 10
    }
}