호댕의 iOS 개발

[iOS] 섹션 별로 inset을 주고 싶다면..? TableView의 header / footer 사용기 본문

Software Engineering/iOS

[iOS] 섹션 별로 inset을 주고 싶다면..? TableView의 header / footer 사용기

호르댕댕댕 2023. 6. 14. 22:42

설정 화면을 리뉴얼하면서 각 Section 별로 Content, 즉 셀을 넣을 때 Inset을 줘야 했다. 다만 각 셀 별로 ContentInset이 들어가는 것이 아니라 처음과 마지막 셀의 top과 bottom에 inset을 줘야 했다. 

 

셀 별로 Content Inset이 필요했다면 아주 간단하게 해결이 가능하다. 

각각의 Cell에서 아래처럼 해주면 된다. 

 

override func layoutSubviews() {
    super.layoutSubviews()

    contentView.frame = contentView.frame.inset(
        by: UIEdgeInsets(top: 10, left: .zero, bottom: 10, right: .zero)
    )
}

기존 셀의 ContentView.frame에서 inset을 준 것을 contentView.frame으로 다시 할당해 주는 것이다. 

 

그럼 이걸 왜 layoutSubViews() 메서드에서 수행하는 것일까?

공식 문서 상에선 뷰와 그 하위 뷰들의 정확한 레이아웃을 잡기 위해 이 메서드를 재정의해서 사용하라고 하고 있다. 

Subclasses can override this method as needed to perform more precise layout of their subviews.
You can use your implementation to set the frame rectangles of your subviews directly.

 

현재 내가 하고 싶었던 구현도 Cell의 하위 뷰인 contentView의 frame을 직접 지정해주고 싶었던 상황인 만큼 해당 메서드를 재정의해서 사용하게 된 것이다. 

 

이렇게 구현하게 되면 셀을 그릴 때 아래 / 위로 10만큼 띄워서 셀 내부의 Content를 보여주게 된다. 

하지만 이건 처음 목적과는 다르다. 

 

요런 셀을 통해 TableView를 그리게 되면 이런 식으로 그려지게 될 것이다. 

 

 

하지만 현재 내가 원하는 것은 

이런 식으로 하나의 Section에 위 아래로 한 번만 inset이 들어간 구조이다. 

 

TableView의 Header와 Footer를 통해 Section 별 inset 넣기 

이때 필요한 것이 바로 TableView의 Header와 Footer이다. 

 

가장 처음에 생각한 방법은

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 함수처럼 섹션의 높이를 지정할 수 있는 방법이 없을까 였다.

 

하지만 UITableViewDelegate / UITableViewDataSource에 이와 같은 함수는 존재하지 않았다.

 

그 다음으로 생각한 방식은 Section 내 가장 처음 셀과 마지막 셀만 Cell의 높이를 다르게 하고, 임의로 UIView를 추가하는 방법을 생각했다. 

하지만 이렇게 처리를 하는 경우 셀을 구현할 때 분기가 너무 많이 들어가고 복잡했다. 

 

마지막으로 생각한 방식이 Header와 Footer를 사용한 방식이었다. 

 

일단 나는 Header와 Footer가 Sticky하게 고정되어 있는 방식으로 구현을 하진 않아 TableView의 Style을 plain이 아닌 grouped로 변경하여 작업을 했다. 

private let tableView = UITableView(frame: .zero, style: .grouped)

 

간단하게 TableView의 Style에 대해 살펴보고 가면 다음과 같다. 

  •  plain : Section의 Header와 Footer가 inline seperators로 떠있게 되며 스크롤할 때도 플로팅 버튼처럼 고정되어 떠있게 된다. 
  • grouped : 스크롤할 때에도 Section의 Header와 Footer가 떠있지 않는다. 
  • insetGrouped : 기본적으로 group과 동일하나 Section이 둥근 모서리로 inset을 가지게 된다. 

 


👾 TableView를 grouped로 지정하니 알 수 없는 inset이 적용된 문제

하지만 grouped로 적용을 했더니 TableView가 시작할 때 지정하지도 않은 inset이 계속 적용되는 문제가 있었다... 🥲

역시 한 번에 척척 잘 되진 않는다 ㅎㅎㅎ

 

그래서 아래처럼 header와 footer의 높이를 0으로 줘봤다. 

tableView.sectionHeaderHeight = .zero
tableView.sectionFooterHeight = .zero

빨간색으로 표시하지 않은 다른 header와 footer는 사라졌지만 빨간색으로 표시한 부분은 사라지지 않았다. 

 

흠... 뭔가 기본적으로 HeaderView가 들어간 것 같았고 아래처럼 적용을 해봤다. 

tableView.tableHeaderView = UIView(frame: CGRect(
    origin: .zero,
    size: CGSize(width: CGFloat.leastNormalMagnitude, height: CGFloat.leastNormalMagnitude)
))
tableView.tableFooterView = UIView(frame: CGRect(
    origin: .zero,
    size: CGSize(width: CGFloat.leastNormalMagnitude, height: CGFloat.leastNormalMagnitude)
))

이렇게 해줬더니 TableView 최상단에 알 수 없는 공백이 사라졌다... 

 

정확히는 Apple이 grouped일 때 TableView를 어떻게 구현해놨는지 알 수 없어 원인을 찾을 수 없었지만 추측으론 기본적으로 grouped이면 상단에 inset을 주는 것이 아닐까 추측했다. 

Style이 plain일 때는 이런 inset이 없었기도 하고 말이다. 

 

이제 문제를 해결했으니 다시 본론으로 돌아가보자. 


그럼 이제 Header와 Footer를 적용해주면 된다. 

이는 UITableViewDelegate에 정의된 함수를 구현해서 사용하면 된다. 

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

    func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
        return UIView()
    }

이렇게 하면 섹션 별로 아래 위로 10만큼 인셋이 존재하도록 구현이 가능하다. 

 

 

처음 기획안을 봤을 때에는 Section마다 인셋을 넣는 것이 아주 간단하겠다고 생각했는데 생각하지 못한 문제도 겪었고, 마냥 쉽게 쉽게 구현되진  않았다. 

(물론 전부 구현하고 보면 이걸 왜 몰랐나 싶긴 하지만...)

 

더군다나 Header와 Footer를 많이 사용하지 않다보니 모르는 부분도 있었는데 이번 기회에 TableView의 Header와 Footer에 대해서도 좀 더 알게 된 좋은 경험이었다. 

 

레퍼런스

- https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews

 

layoutSubviews() | Apple Developer Documentation

Lays out subviews.

developer.apple.com

- https://developer.apple.com/documentation/uikit/uitableview/style/plain

 

UITableView.Style.plain | Apple Developer Documentation

A plain table view.

developer.apple.com

- https://developer.apple.com/documentation/uikit/uitableview/style/grouped

 

UITableView.Style.grouped | Apple Developer Documentation

A table view where sections have distinct groups of rows.

developer.apple.com

- https://developer.apple.com/documentation/uikit/uitableview/style/insetgrouped

 

UITableView.Style.insetGrouped | Apple Developer Documentation

A table view where the grouped sections are inset with rounded corners.

developer.apple.com

 

Comments