Search code examples
iosswiftswiftuikeyboardgesture

How to ignore safe area for container and respect safe area for content in swift UI


I'm trying to create a bottom sheet in swift ui that looks something like this

https://user-images.githubusercontent.com/33900517/172068237-4dd58374-b6e6-4340-a913-7085fb64b254.mp4

My issue is that I have an animated bottom sheet, but because it is ignoring the safe area, when I click into the textfield it does not expand with the keyboard.

How would I fix this so the view expands with the keyboard but the white at the bottom still goes beyond the safe area?

I.e. the containing view should ignore the safe area, and the content within should adhere to the safe area.

Here is the bottom sheet code snippet, full example can be found here https://gist.github.com/CTOverton/4fbfb8db2de31f3b5f5ef9ee88e8f744

   var body: some View {
    GeometryReader { geometry in
      VStack() {
        self.content
      }
      .padding(.vertical, 34)
      .padding(.horizontal, 16)
//      .frame(width: geometry.size.width, height: geometry.size.height * heightRatio, alignment: .top)
      .frame(width: geometry.size.width, height: self.maxHeight, alignment: .top)
      .background(Color(.white))
      .cornerRadius(Constants.radius)
      .frame(height: geometry.size.height, alignment: .bottom)
      .offset(y: max(self.offset + self.translation, 0))
      .animation(.interactiveSpring())
      .gesture(
          DragGesture().updating(self.$translation) { value, state, _ in
                state = value.translation.height
              }.onEnded { value in
                let snapDistance = self.maxHeight * Constants.snapRatio
                guard abs(value.translation.height) > snapDistance else {
                  return
                }
                self.isOpen = value.translation.height < 0
              }
      )
    }
    .edgesIgnoringSafeArea([.bottom, .horizontal])
    .shadow(color: Color(hue: 1.0, saturation: 0.0, brightness: 0.0, opacity: 0.08), radius: 12, y: -8)
  }

I've tried various configurations of .ignoreSafeArea() and .safeAreaInset() but I just can't seem to get it quite right.

Here are some pictures for reference as well

Bottom sheet closed Bottom sheet open Bottom sheet open and textfield is selected, keyboard is active


Solution

  • Actually instead of ignoring safe area for everything (that results in issue), we need it only in background, so the question is how to correctly construct background in this case.

    Note: the .cornerRadius is also not appropriate here, because it clips content

    demo

    Here is a main part of a fix. Tested with Xcode 13.4 / iOS 15.5

    .background(
        RoundedRectangle(cornerRadius: Constants.radius)  // corners !!
            .fill(.white)                                 // background !!
            .edgesIgnoringSafeArea([.bottom, .horizontal]) // << only here !!
    )
    

    Complete test module is here