swiftswiftui

ignoresSafeArea on views other than Color does not work


I got this code:

#if DEBUG
struct BottomViewPreview: View {
    var body: some View {
        Spacer()
        Text("S").ignoresSafeArea(edges: .bottom)
    }
}

#Preview {
    BottomViewPreview()
}
#endif

Which produces this view: $$$$

I would suspect the S Text would be at the bottom, ignoring the safe space but it does not work. What is the correct way of doing this? I don't want ANY padding at the bottom.


Solution

  • The main reason why the Text does not change its size or position when you add the modifier .ignoresSafeArea is because, unlike a Color or a Shape, a Text view is not greedy. It does not need more space than it already uses, so it does not expand into the safe area inset.

    If you add a background color to the Text using the modifier background(_:ignoresSafeAreaEdges:) then it will appear as if the frame of the text does extend to the screen edge:

    Text("S")
        .background(.yellow)
        .ignoresSafeArea(edges: .bottom)
    

    Screenshot

    This is misleading. What is actually happening, is that the background color is ignoring the safe area edge, not the Text. Adding a .border to the Text is a better way to confirm that the modifier .ignoresSafeArea is having no effect:

    Text("S")
        .border(.red)
        .ignoresSafeArea(edges: .bottom)
    }
    

    Screenshot

    To make the frame of the Text extend into the safe area inset, you can apply .frame(maxHeight: .infinity), before adding the modifier .ignoresSafeArea. However, this will cause it to use as much vertical space as possible, both above and below the actual text.

    To have the Text consume only the space in the safe area inset below it, you can give a higher .layoutPriority to the Spacer above it. Let's put the two views in a VStack too:

    VStack {
        Spacer()
            .layoutPriority(2)
        Text("S")
            .frame(maxHeight: .infinity)
            .layoutPriority(1)
            .border(.red)
            .ignoresSafeArea(edges: .bottom)
    }
    

    Screenshot

    By default, the .frame modifier uses alignment .center, which is why the text is now in the middle of the frame. Add an alignment parameter if you want a different alignment:

    Text("S")
        .frame(maxHeight: .infinity, alignment: .top)
        // ...other modifiers as before
    

    Screenshot

    If in fact you want the text to move completely into the safe area inset, you need to apply .ignoreSafeArea to the container instead:

    VStack {
        Spacer()
        Text("S")
            .border(.red)
    }
    .ignoresSafeArea(edges: .bottom)
    

    Screenshot

    However, this screenshot illustrates why it may be a bad idea to ignore the safe area edges for a container with Text at the bottom, because the text might end up being obscured by the home bar.