📘

【SwiftUI】listRowInsetsで上下の余白を消せない?

に公開

Summary

環境変数defaultMinListRowHeight0ポイントに設定することで、上下の余白を消すことができます。

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<10) { _ in
                Text("Hello, world.")
            }
            .listRowInsets(.init(top: .zero, leading: .zero, bottom: .zero, trailing: .zero))
        }
        .environment(\.defaultMinListRowHeight, 0)
    }
}

Description

本記事に辿り着かれた方の多くは、下記のようなコードを書かれていると思います。

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<10) { _ in
                Text("Hello, world.")
            }
            .listRowInsets(.init(top: .zero, leading: .zero, bottom: .zero, trailing: .zero))
        }
    }
}

一見、上記のコードで上下左右の余白がなくなりそうですが、実際は上下の余白は消えずに下の画像のようになります。

これは環境変数defaultMinListRowHeightがデフォルトで44ポイントに設定されているためです。

つまりは、列の高さは最低でも44ポイントは必要です。

ビューの高さだけでは44ポイントに満たないため、上下に余白が付与されます。

environmentモディファイアを用いて、環境変数defaultMinListRowHeight0ポイントに設定することで、上下の余白を消すことができます。

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<10) { _ in
                Text("Hello, world.")
            }
            .listRowInsets(.init(top: .zero, leading: .zero, bottom: .zero, trailing: .zero))
        }
        .environment(\.defaultMinListRowHeight, 0)
    }
}

上記のコードでアプリを立ち上げると、下の画像の通り上下の余白がなくなります。

Bonus

列の高さが環境変数defaultMinListRowHeightで設定された値に満たない場合、
(環境変数defaultMinListRowHeight - ビューの高さ + トップインセット + ボトムインセット) / 2」が上下の余白に追加されます。

下記のコードでは、環境変数defaultMinListRowHeightが300ポイント、ビューの高さが100ポイント、トップインセットが100ポイント、ボトムインセットが0ポイントとなっています。

列の高さは最低でも300ポイントは必要ですが、ビューの高さとトップインセットとボトムインセットの合計は200ポイントで100ポイント足りません。不足分の100ポイントは、上下の余白に50ポイントずつ追加されることになります。

今回の例では、上部の余白が元々の100ポイントに50ポイントが追加され150ポイントとなり、ビューの高さは変わらず100ポイントとなり、下部の余白が元々の0ポイントに50ポイントが追加され50ポイントとなります。

検証に使用したコードはこちら
struct ContentView: View {
    var body: some View {
        List {
            Rectangle()
                .frame(width: 100, height: 100)
                .frame(maxWidth: .infinity)
                .listRowInsets(.init(top: 100, leading: .zero, bottom: .zero, trailing: .zero))
        }
        .environment(\.defaultMinListRowHeight, 300)
        .overlay(content: DraggableRectangle.init)
    }
}

struct DraggableRectangle: View {
    @State private var currentOffset: CGSize = .zero
    @State private var dragOffset: CGSize = .zero

    var body: some View {
        Rectangle()
            .fill(.pink.opacity(0.3))
            .frame(width: 50, height: 50)
//            .frame(width: 150, height: 150)
            .offset(x: currentOffset.width + dragOffset.width,
                    y: currentOffset.height + dragOffset.height)
            .gesture(dragGesture)
    }

    var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                dragOffset = value.translation
            }
            .onEnded { _ in
                currentOffset.width  += dragOffset.width
                currentOffset.height += dragOffset.height
                dragOffset = .zero
            }
    }
}

Discussion