Intro

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

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


Preconfigure

간단하게 연습해보기 위해 ViewController 에 Grouped StyleUITableView 를 하나 올려놓았어요. 그리고 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
    }
}