ScrollViewの要素間の隙間を埋める


モチベーション

ScrollView(VStackも同様)で複数のViewを並べる時に、以下のように隙間ができる。
「この隙間を埋めたい」(映画の名言みたいな発言になった)

環境

PC: Catalina (10.15)
Xcode: 11.0 (11A420a)
iOS: 13.0
実行環境: iPhone11 シミュレーター

ScrollViewの隙間の調査

各要素は200ptでそれが3個並んでいるので高さは600ptとなるはずだが、
Debug View Hierarchy で確認すると 616ptとなっていることが確認できる。
つまりSwiftUIにより要素の間に8ptの隙間が挿入されたことがわかる。
加えて、要素間に余分なViewが挿入されているわけではないことも確認できる。

○ 隙間が作られるソースコード

struct ContentView: View {
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.yellow)
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.green)
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.blue)
        }
    }
}

VStackの隙間の調査

VStackでも同様に隙間が作られるので確認してみる。

struct ContentView: View {
    var body: some View {
        VStack {
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.yellow)
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.green)
            Rectangle()
                .frame(width: UIScreen.main.bounds.width, height: 200)
                .foregroundColor(.blue)
            Spacer()
        }
    }
}

Debug View Hierarchy での確認
View自体の縦幅が818ptであり、Spacerの縦幅が202ptなので、3つのViewの合計は616ptとなり、各要素間は8ptでScrollViewと同様の隙間が作られていることが確認できる。

隙間を埋める

VStackでは、spacingを0にすることで隙間を埋めることができるため、ScrollViewの中にVStackをいれることで隙間を埋める。

実際のコードは以下のようになる。

struct ContentView: View {
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack(spacing: 0) {
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.yellow)
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.green)
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.blue)
            }
        }
    }
}

○ Debug View Hierarchy での確認

期待通り高さは600ptで表示されている。

応用

2つ目と3つ目の要素間にだけ隙間を50pt開けたい場合は以下のようなコードで実現できる。

struct ContentView: View {
    var body: some View {
        ScrollView(.vertical, showsIndicators: false) {
            VStack(spacing: 0) {
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.yellow)
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.green)
                Spacer().frame(height: 50)
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width, height: 200)
                    .foregroundColor(.blue)
            }
        }
    }
}

考察

ScrollViewの要素間に隙間が作られるのはAppleとしては余白のないデザインさせたくない意思の現れなのかと感じました。

今回この隙間を削ることは、iOSアプリを作っていて、TableViewのセパレータの左右の余白を消すためにコードを書かないといけないのと少し似ている気がしております。

隙間を埋めるためにVStackでネストさせるのはスマートな方法ではないので、特殊な場合を除き8ptの隙間を生かしたデザインでアプリを作っていきたいと思います。

参考

SwiftUI: How to remove margin between views in VStack?