A page-style TabView that extends under the bottom tab bar with .ignoresSafeArea(edges: .bottom) leaves its pages shifted upward after its height shrinks while it is on screen. The shift is half of the bottom safe area (41.67 pt with an 83.33 pt tab bar area), and the top of each page is hidden.
The internal UICollectionView (contentInsetAdjustmentBehavior == .automatic) seems to briefly add the bottom safe area to adjustedContentInset.bottom during the resize. The inset goes back to 0 afterwards, but the visible cell is left at frame.minY == -41.67.
- Xcode 27.0 (27A266a), iPhone Duo Simulator (iOS 27.1), deployment target iOS 18.0
- In a unit test it also reproduces when only the window height shrinks (386×678 → 386×600), so it does not depend on the foldable device
- Run the code below on the iPhone Duo Simulator while it is folded
- Unfold the device
The red "Top of page 0" area stays directly below the yellow header, as it does when the app is launched unfolded.
The top of the red area is hidden under the header. Log output:
folded (launch): height=628.00 adjustedContentInset.bottom=0.00 cell.minY=0.00
after unfolding: height=619.00 adjustedContentInset.bottom=0.00 cell.minY=-41.67
In this sample the page returns to its position when the device is folded again. In our app it stays shifted, and after the shift, tapping to switch pages sometimes stops working until the user swipes.
- Removing
.ignoresSafeArea(edges: .bottom)avoids the issue, but the content then no longer extends under the translucent tab bar - Setting
edgesForExtendedLayout = []on the hosting controller also avoids it, with the same visual change.safeAreaRegions = []andsetContentScrollView(nil, for:)do not help - A non-transparent background on the
TabViewor its pages, as suggested in https://developer.apple.com/forums/thread/762286, does not help. The trigger here is a size change, not a page switch, so this seems to be a different issue - Current workaround: find the internal
UICollectionViewthrough the superview chain and setcontentInsetAdjustmentBehavior = .never. This works, but it relies on the private view hierarchy. Is there a supported way to avoid this?
import SwiftUI
import UIKit
// A TabView(.page) extended under the tab bar with .ignoresSafeArea(edges: .bottom).
// Resizing the window while it is visible (e.g. unfolding) shifts the page up.
struct PagerView: View {
@State private var selection = 0
var body: some View {
VStack(spacing: 0) {
Color.yellow.frame(height: 50).overlay(Text("Header"))
TabView(selection: $selection) {
ForEach(0..<2) { index in
ScrollView {
Color.red.frame(height: 120).overlay(Text("Top of page \(index)"))
ForEach(0..<30) { Text("Row \($0)").frame(maxWidth: .infinity, minHeight: 44) }
}
.tag(index)
}
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
.ignoresSafeArea(edges: .bottom)
}
}
final class PagerViewController: UIHostingController<PagerView> {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in self?.logCollectionView() }
}
private func logCollectionView() {
func find(_ view: UIView) -> UICollectionView? {
view as? UICollectionView ?? view.subviews.lazy.compactMap(find).first
}
guard let collectionView = find(view) else { return }
let cellMinY = collectionView.visibleCells.first?.frame.minY ?? .nan
NSLog("FORUM height=%.2f adjustedContentInset.bottom=%.2f cell.minY=%.2f", collectionView.bounds.height, collectionView.adjustedContentInset.bottom, cellMinY)
}
}
@main
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, configurationForConnecting session: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: nil, sessionRole: session.role)
configuration.delegateClass = SceneDelegate.self
return configuration
}
}
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let pager = PagerViewController(rootView: PagerView())
pager.tabBarItem = UITabBarItem(title: "Pager", image: UIImage(systemName: "square"), tag: 0)
let other = UIViewController()
other.tabBarItem = UITabBarItem(title: "Other", image: UIImage(systemName: "circle"), tag: 1)
let tabBarController = UITabBarController()
tabBarController.viewControllers = [pager, other]
window = UIWindow(windowScene: scene as! UIWindowScene)
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
}
}