2

I'm seeing a weird behavior that is affecting one of my views in SwiftUI after upgrading to iOS 16.

Just to give some context, here is the stack:

  • Xcode 14
  • Simulator or real device on iOS 15.5 and 16

Considering the minimum reproducible code below:

struct ContentView: View {
    @State private var isPresented: Bool = false

    var body: some View {
        GeometryReader { reader in
            VStack(spacing: 36) {
                Text("Screen frame:\n\(String(describing: reader.frame(in: .global)))")
                    .multilineTextAlignment(.center)

                Button {
                    isPresented.toggle()
                } label: {
                    Text("Open modal")
                }
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .padding()
            .onReceive(NotificationCenter.default.appDidBecomeActive()) { _ in
                print(reader.frame(in: .global))
            }
            .onReceive(NotificationCenter.default.appDidEnterBackground()) { _ in
                print(reader.frame(in: .global))
            }
        }
        .sheet(isPresented: $isPresented) {
            modalView
        }
    }

    private var modalView: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
        }
        .padding()
    }
}

extension NotificationCenter {
    func appDidBecomeActive() -> AnyPublisher<Notification, Never> {
        publisher(for: UIApplication.didBecomeActiveNotification).eraseToAnyPublisher()
    }

    func appDidEnterBackground() -> AnyPublisher<Notification, Never> {
        publisher(for: UIApplication.didEnterBackgroundNotification).eraseToAnyPublisher()
    }
}

As soon the view starts, it's possible to see the frame available due to the GeometryReader. Then following the steps:

  1. Open the modal view
  2. Send the app to the background
  3. Open the app again
  4. Close the modal

It's possible to see that the frame changed, and the values match with the 3D effect when a view is presenting another view, and it's never changing again to the right values unless you send the app again to the background or switch views (e.g. using a TabView).

I don't find anything on iOS release notes talking about it, so I supposed it must be a bug (I've filled out a bug report already).

On iOS 15, the frame value keeps stable at the same value.

I have a couple of views relying on the value of a GeometryReader, and it's causing my view to deform because of this issue. Does anyone know a way to force the recalculation for the GeometryReader for this case?

Any help is appreciated.

bguidolim
  • 367
  • 2
  • 9
  • Apple says in one of the WWDC sessions this year that `GeometryReader` is less desirable and the improved `Layout` should be the new norm. It is more stable. – lorem ipsum Sep 16 '22 at 14:56

1 Answers1

1

The issue won't occur if you control the display of the sheet with the new presentationDetents method, provided you do not request to cover the entire screen.

I modified your code as follows:

    .sheet(isPresented: $isPresented) {
        if #available(iOS 16, *) {
            modalView
                .presentationDetents([.fraction(0.99)])
        }
        else {
            modalView
        }
    }

The issue will remain if you request .fraction(1), i.e. covering the whole screen.

Alex
  • 91
  • 6
  • Thank you! I still think it's a bug on GeometryReader, but your answer gave me a good workaround to solve the issue easily without having to do any refactoring using the `Layout` protocol. – bguidolim Sep 21 '22 at 09:22
  • Yes totally agree this is a bug, and the workaround I suggested results in a less favorable UI. I filed an issue on Feedback Assistant but haven't got any interaction on it so far. – Alex Sep 28 '22 at 11:49