By applying .ignoresSafeArea() to the ScrollView, the ScrollView extends to the full size of the screen. The content inside the ScrollView adopts the same area, so safe area insets are ignored by the content too. This is why the content begins inside the safe area inset at the top of the screen in your screenshot.
Unfortunately, getting it to work as you want is not a simple matter of moving the modifier .ignoresSafeArea() to the ZStack, because when content is inside a ScrollView, the safe area insets that apply to the ScrollView do not get ignored. In other words, the modifier has no effect when used this way.
Here instead are two approaches that do work:
1. Add padding to the scrolled content
If the height of the top safe area inset is known then it can be added as padding to the content.
- By wrapping the
ScrollViewin aGeometryReader, the insets can be measured. - The padding can be added as simple
.paddingto theVStack, or as.safeAreaPaddingto theZStack. - If
.safeAreaPaddingis used, then.ignoresSafeArea()needs to be applied to the color.
Here is how the example can be updated to work this way:
var body: some View {
GeometryReader { geo in
ScrollView {
ZStack {
Color.blue
.ignoresSafeArea()
VStack {
// ...
}
}
.safeAreaPadding(.top, geo.safeAreaInsets.top)
VStack {
// ...
}
}
.ignoresSafeArea(edges: .top)
}
}
This approach works well when a gradient or image is used as the background, because the background can be scaled to fill the area precisely.
2. Use negative padding to cover the safe area inset
A simpler but slightly dirtier approach is not to ignore safe areas and just add negative padding to the background instead. The negative padding needs to be large enough to cover the safe area inset on all devices. 200 points is probably enough.
var body: some View {
ScrollView {
ZStack {
Color.blue
.padding(.top, -200)
VStack {
// ...
}
}
VStack {
// ...
}
}
}
This approach may be sufficient when a color is used for the background, because the color can overflow off-screen. In fact, the overflow helps to keep the background filled to the top of the screen even when the content is pulled down too:

Instead of using a ZStack, you could also consider applying the background using .background.
- The background adopts the size of the view it is applied to.
- Use a
framemodifier to expand the content to the full width of the screen, if necessary. - If the modifier
background(_:ignoresSafeAreaEdges:)is used, safe areas edges are ignored by default.
Here is how the first approach can be modified to work this way:
var body: some View {
GeometryReader { geo in
ScrollView {
VStack {
// ...
}
.frame(maxWidth: .infinity)
.safeAreaPadding(.top, geo.safeAreaInsets.top)
.background(.blue)
VStack {
// ...
}
}
.ignoresSafeArea(edges: .top)
}
}
If you want to show an image in the background and if the image should be scaled to fill then it works well to show the image as an overlay over Color.clear. See this answer for details.