iOS
Swift
ios11

[ios]iPhone Xでも、TableViewのheaderを画面上に残らないようにする

TableViewのヘッダーはスクロールしても画面内に止まるようになっています。
それを無理やり画面に残らないようにする方法は下記の地道な方法があります。
セクションヘッダが画面上に残らないようにする方法

safeAreaを考慮しない場合
private let sectionHeaderHeight: CGFloat = 8
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    /// sectionHeaderが上部に残らないようにする
    let offsetY = scrollView.contentOffset.y

    if offsetY <= sectionHeaderHeight && offsetY >= 0 {
        scrollView.contentInset = UIEdgeInsets(top: -offsetY, left: 0, bottom: 0, right: 0)
    } else if offsetY >= sectionHeaderHeight {
        scrollView.contentInset = UIEdgeInsets(top: -sectionHeaderHeight, left: 0, bottom: 0, right: 0)
    }
}

しかし、これだとiPhone Xの場合にsafeArea上で思った通りに動きません。

before2.gif

tableViewにはios11から safeAreaInsets というものが追加されおり、この safeAreaInsetscontentsInsets を合算した adjustedContentInset を使ってヘッダー位置の調整を行なっています。
よって、上記サンプルのcontentOffsetにsafeAreaの概念を入れてあげます。

safeAreaを考慮したサンプル
private let sectionHeaderHeight: CGFloat = 8
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    /// sectionHeaderが上部に残らないようにする
    let offsetY = scrollView.contentOffset.y

    let safeAreaInset: CGFloat
    if #available(iOS 11.0, *) {
        safeAreaInset = scrollView.safeAreaInsets.top
    } else {
        safeAreaInset = 0
    }

    let top: CGFloat
    if offsetY > sectionHeaderHeight{
        /// 一番上のheaderの最下部が画面外へ出ている状態
        top = -(safeAreaInset + sectionHeaderHeight)
    } else if offsetY < -safeAreaInset {
        /// 初期状態からメニューを下に引っ張っている状態
        top = 0
    } else {
        /// safeArea内を一番上のheaderが移動している状態
        top = -(safeAreaInset + offsetY)
    }
    scrollView.contentInset = UIEdgeInsets(top: top, left: 0, bottom: 0, right: 0)
}

after.gif