Navigation Bar Elements Disappear When Using UIPageViewController in SwiftUI Under Low Power Mode

Problem Description:

In a SwiftUI application, I've wrapped UIKit's UIPageViewController using UIViewControllerRepresentable, naming the wrapped class PagedInfiniteScrollView. This component causes navigation bar elements (title and buttons) to disappear.

This issue only occurs in Low Power Mode on a physical device.

Steps to Reproduce:

  1. Enable Low Power Mode on a physical device and open the app's home page.

  2. From the home page, open a detail sheet containing PagedInfiniteScrollView. This detail page include a navigation title and a toolbar button in the top-right corner. PagedInfiniteScrollView supports horizontal swiping to switch pages.

  3. Tap the toolbar button in the top-right corner of the detail page to open an edit sheet.

  4. Without making any changes, close the edit sheet and return to the detail page. On the detail page, swipe left and right on the PagedInfiniteScrollView.

Expected Result:

When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page should remain visible.

Actual Result:

When swiping the PagedInfiniteScrollView, the navigation title and top-right toolbar button of the detail page disappear.

import SwiftUI

@main
struct CalendarApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
import SwiftUI

struct ContentView: View {
    @State private var showDetailSheet = false
    @State private var currentPage: Int = 0

    var body: some View {
        NavigationStack {
            Button {
                showDetailSheet = true
            } label: {
                Text("show Calendar sheet")
            }
            .sheet(isPresented: $showDetailSheet) {
                DetailSheet(currentPage: $currentPage)
            }
        }
    }
}

struct DetailSheet: View {
    @Binding var currentPage: Int
    @State private var showEditSheet = false

    var body: some View {
        NavigationStack {
            PagedInfiniteScrollView(content: { pageIndex in
                                        Text("\(pageIndex)")
                                            .frame(width: 200, height: 200)
                                            .background(Color.blue)
                                    },
                                    currentPage: $currentPage)
                .sheet(isPresented: $showEditSheet, content: {
                    Text("edit")
                })

                .navigationTitle("Detail")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItemGroup(placement: .topBarTrailing) {
                        Button {
                            showEditSheet = true
                        } label: {
                            Text("Edit")
                        }
                    }
                }
        }
    }
}
import SwiftUI
import UIKit

struct PagedInfiniteScrollView<Content: View>: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIPageViewController

    let content: (Int) -> Content
    @Binding var currentPage: Int

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIPageViewController {
        let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
        pageViewController.dataSource = context.coordinator
        pageViewController.delegate = context.coordinator

        let initialViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) }))
        pageViewController.setViewControllers([initialViewController], direction: .forward, animated: false, completion: nil)

        return pageViewController
    }

    func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
        let currentViewController = uiViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>
        let currentIndex = currentViewController?.rootView.index ?? 0

        if currentPage != currentIndex {
            let direction: UIPageViewController.NavigationDirection = currentPage > currentIndex ? .forward : .reverse
            let newViewController = UIHostingController(rootView: IdentifiableContent(index: currentPage, content: { content(currentPage) }))
            uiViewController.setViewControllers([newViewController], direction: direction, animated: true, completion: nil)
        }
    }

    class Coordinator: NSObject, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
        var parent: PagedInfiniteScrollView

        init(_ parent: PagedInfiniteScrollView) {
            self.parent = parent
        }

        func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
            guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else {
                return nil
            }

            let previousIndex = currentIndex - 1

            return UIHostingController(rootView: IdentifiableContent(index: previousIndex, content: { parent.content(previousIndex) }))
        }

        func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
            guard let currentView = viewController as? UIHostingController<IdentifiableContent<Content>>, let currentIndex = currentView.rootView.index as Int? else {
                return nil
            }

            let nextIndex = currentIndex + 1

            return UIHostingController(rootView: IdentifiableContent(index: nextIndex, content: { parent.content(nextIndex) }))
        }

        func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
            if completed,
               let currentView = pageViewController.viewControllers?.first as? UIHostingController<IdentifiableContent<Content>>,
               let currentIndex = currentView.rootView.index as Int? {
                parent.currentPage = currentIndex
            }
        }
    }
}

extension PagedInfiniteScrollView {
    struct IdentifiableContent<Content: View>: View {
        let index: Int
        let content: Content

        init(index: Int, @ViewBuilder content: () -> Content) {
            self.index = index
            self.content = content()
        }

        var body: some View {
            content
        }
    }
}

I add a gif to demonstrate navigation bar elements disappearing when swiping UIPageViewController in SwiftUI under Low Power Mode"

Please use the Feedback Assistant to file a bug about this issue.

filed feedback with Apple: FB15402866

Navigation Bar Elements Disappear When Using UIPageViewController in SwiftUI Under Low Power Mode
 
 
Q