Mobile Dev

UIKit iOS: Fix .pageSheet Swipe Freeze on Pro Max iOS 26

iOS 26 .pageSheet freezes on Pro Max devices when UIDesignRequiresCompatibility=YES. Reproducer, Instruments/sysdiagnose debugging steps, root-cause hypotheses, and safe workarounds.

1 answer 1 view

iOS 26.x: Why does interactive .pageSheet swipe-to-dismiss freeze when UIDesignRequiresCompatibility = YES on some iPhones (iPhone 14 Pro Max / iPhone 17 / iPhone 17 Pro Max) and how can I fix or work around it?

Description:
Interactive dismissal of a modal presented with .pageSheet freezes mid-gesture (~20% progress) on specific iPhone models when UIDesignRequiresCompatibility = YES. UIKit lifecycle callbacks (e.g., viewDidDisappear) are never called and the app becomes unresponsive. There is no crash or console error.

Steps to reproduce

  1. Create an iOS app with deployment target iOS 13.0.
  2. In Info.plist set:
UIDesignRequiresCompatibility = YES
  1. Present a modal view controller using .pageSheet.
  2. Swipe down to dismiss the modal.
  3. Observe freeze mid-gesture.

Minimal code sample

swift
class TestModalViewController: UIViewController {
 override func viewDidLoad() {
 super.viewDidLoad()
 view.backgroundColor = .systemBackground
 }
}

let modal = TestModalViewController()
modal.modalPresentationStyle = .pageSheet
present(modal, animated: true)

Expected results

  • Interactive dismissal completes normally.
  • viewDidDisappear is called.
  • App remains responsive.

Actual results

  • App freezes mid-dismissal gesture; UI becomes unresponsive.
  • No crash or console error.
  • viewDidDisappear is never called.
  • Programmatic dismissal still works.

Devices tested

Affected: iPhone 14 Pro Max, iPhone 17, iPhone 17 Pro Max.
Not affected: iPhone 11, iPhone 16e.

iOS versions

  • iOS 26.1
  • iOS 26.2

Regression notes

  • ❌ Does NOT occur when UIDesignRequiresCompatibility = NO.
  • ❌ Does NOT occur with .fullScreen.
  • ❌ Does NOT occur in a fresh test project with the same code.
  • ✅ Occurs consistently in the existing project.

Additional notes

  • Issue occurs during UIKit’s interactive dismissal animation.
  • No custom transitions or animations in use.
  • Appears related to compatibility layout / gesture handling on large-screen devices.

Questions

  • What can cause a device- and project-specific interactive-dismissal freeze like this in iOS 26.x (UIKit bug, gesture/layout compatibility issue, or project configuration)?
  • How should I debug this (recommended logs, instruments, sysdiagnose, view hierarchy inspection, main-thread backtrace) to pinpoint the root cause?
  • Are there known iOS 26 regressions or Apple advisories related to UIDesignRequiresCompatibility and .pageSheet interactive dismissal?
  • What practical workarounds exist that do not require disabling UIDesignRequiresCompatibility globally (e.g., presentation style change, gesture interception, layout fixes, temporary code changes)?
  • What information and artifacts should I include when filing a bug report with Apple to ensure reproducibility and a faster fix?

In uikit ios on iOS 26.x, an interactive .pageSheet swipe-to-dismiss can hang on certain Pro‑Max iPhones when UIDesignRequiresCompatibility = YES because the compatibility path changes layout/gesture handling and exposes a UIKit gesture/state‑machine regression or a project‑specific gesture/layout interaction. The freeze most often comes from a gesture recognizer conflict or a main‑thread stall during the sheet’s interactive animation (backgrounding/foregrounding or programmatic dismiss may “unstick” it). Short‑term fixes: present as .fullScreen for affected devices, disable swipe dismissal for that sheet (isModalInPresentation), or temporarily disable conflicting gestures; long term you should collect a minimal repro, sysdiagnose/Instruments traces and file a Feedback Assistant report with Apple.


Contents


Why the .pageSheet swipe-to-dismiss can freeze (UIKit iOS / UIDesignRequiresCompatibility)

Short answer: the compatibility flag flips UIKit into a legacy/layout/gesture compatibility path on some large devices, and that different code path can expose a state machine bug or a gesture interaction that leaves the interactive dismissal half‑complete and the main runloop effectively blocked.

Why that matches your symptoms:

  • The freeze happens only when UIDesignRequiresCompatibility = YES and only on certain large devices (iPhone 14 Pro Max, 17, 17 Pro Max). That points to the alternate compatibility path interacting badly with large-screen scaling/gesture thresholds. See Apple’s developer forum reports of similar gesture-ordering freezes: Sheet dismiss gesture with swipe back causes app to freeze.
  • Programmatic dismissal still works and background/foreground can “unstick” the app — both are signs the UI state machine or an animation completion callback is in a bad state rather than a crash.
  • The issue being project‑specific (doesn’t reproduce in a fresh project) usually means a combination of your app’s global configuration, third‑party code or custom gesture/presentation plumbing is exposing the problem.

Put another way: this is most commonly a mix of three things — a UIKit regression exposed by the compatibility flag, a gesture recognizer interaction (system vs app/3rd-party), or synchronous work on the main thread during the interactive transition.


Minimal reproducible example & project differences

You already have a tiny reproducer:

swift
class TestModalViewController: UIViewController {
 override func viewDidLoad() {
 super.viewDidLoad()
 view.backgroundColor = .systemBackground
 }
}

let modal = TestModalViewController()
modal.modalPresentationStyle = .pageSheet
present(modal, animated: true)

That snippet can freeze when your Info.plist contains:

xml
<key>UIDesignRequiresCompatibility</key>
<true/>

If the problem doesn’t reproduce with a plain new app, build a progressive minimal repro:

  1. Start from the fresh project and add your app’s Info.plist, then test.
  2. Add your app’s Launch arguments, entitlements, and build settings. Test again.
  3. Add frameworks one-by-one (or group-by-group). Test.
  4. Add app-level gesture code, root container customizations, or other global changes last.

Quick helper: read the compatibility flag at runtime so you can gate workarounds:

swift
extension Bundle {
 var designRequiresCompatibility: Bool {
 return (object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool) ?? false
 }
}

Use that to test alternate presentation styles without editing Info.plist every run.


Likely causes: gesture conflicts, compatibility layout, and main‑thread stalls

Here are the hypotheses to test, ordered by likelihood and ease to verify.

  • Gesture recognizer conflict (high likelihood)
    Global pan gestures (on UIWindow), a UINavigationController’s interactivePopGestureRecognizer, or a third‑party gesture may be colliding with the sheet’s internal pan. That conflict can leave the sheet’s internal state machine waiting for a gesture end callback that never arrives. Quick check: enumerate gesture recognizers at runtime (see debugging section).

  • UIKit compatibility-path regression (medium–high)
    UIDesignRequiresCompatibility can change how UIKit composes containers, safe areas and hit testing on large devices; an internal bug exposed by that path can deadlock interactive transitions. Apple Developer Forums contains similar reports that point at UIKit behavior differences under complex gesture ordering: https://developer.apple.com/forums/thread/703791.

  • Synchronous work/observers on main thread during transitions (medium)
    Heavy work or synchronous layout in viewWillDisappear/viewDidLayoutSubviews, KVO callbacks, or locks taken on main thread during animation can block transition completion. The freeze looks like this because lifecycle callbacks don’t fire.

  • Presentation/container differences (medium)
    Custom containers, view controller containment, or modified UIWindow/scene behavior can alter when/where UIKit dispatches animation completions. If your app modifies the root window hierarchy or uses nonstandard window/scene setups, that’s worth checking.

  • Swizzling / 3rd‑party instrumentation (medium)
    Libraries that swizzle UIViewController lifecycle methods or UIGestureRecognizer internals can create race conditions only visible in the compatibility path.

  • Historically related patterns (low/medium)
    Community issues (Hero, React Native) show that pageSheet + navigation controllers or cross-framework handling of sheet dismissal can produce weird display/gesture problems — see related threads below.


How to debug: logs, Instruments, main‑thread backtraces and view hierarchy

Goal: capture the app state at the exact moment it freezes and collect artifacts Apple and engineers can use to root‑cause.

  1. Build a debug-symbol (not stripped) build and reproduce on a physical affected device (not simulator). Include the app’s exact Info.plist.

  2. Capture a video/GIF of the freeze — this is invaluable.

  3. Attach the debugger and pause while the app is frozen

  • In Xcode: Debug → Attach to Process (your app). When the UI hangs, click the Pause button.
  • Copy the main thread backtrace. In lldb (Xcode console) you can run:
(lldb) process interrupt
(lldb) thread backtrace all

Save the full thread dump (all threads). Symbolicated traces are best — keep the dSYM for the exact build.

  1. Time Profiler trace (Instruments)
  • Open Instruments → Time Profiler, choose the physical device process, Start Recording, reproduce the freeze, Stop.
  • Look at “Main Thread” call tree for stalls or repetitive CPU use. In many freezes you’ll see long time spent in runloop-related calls or animations waiting on callbacks.
  1. Device logs / Console
  • Xcode → Window → Devices and Simulators → select device → Open Console (or use Console.app). Start streaming logs, reproduce the issue and save the log. Filter for your app process and for tags like “presentation”, “sheet”, “UIPresentationController”.
  1. sysdiagnose
  • Collect a sysdiagnose while reproducing (attach through Feedback Assistant or use device developer settings if available). The sysdiagnose package contains kernel traces, process samples and other system-wide context useful to Apple. (If you need help collecting sysdiagnose, Feedback Assistant can guide you or use Xcode Device Console to gather logs.)
  1. Dump view hierarchy and gestures at freeze time
  • Use Xcode → Debug View Hierarchy while attached to capture the full view tree and inspect whether presentation controllers or wrapper views are mid‑transition.
  • Runtime dump to find gesture recognizers:
swift
for scene in UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }) {
 for window in scene.windows {
 for gr in window.gestureRecognizers ?? [] {
 print("window: (window), gesture: (gr), delegate: (String(describing: gr.delegate))")
 }
 }
}
  1. Lightweight in‑app instrumentation
  • Add os_signpost / OSLog around presentation/dismissal points and in viewWillAppear/Disappear to get timestamps in Console. Example:
swift
import os
let sheetLog = OSLog(subsystem: "com.example", category: "sheet")
os_signpost(.begin, log: sheetLog, name: "presentSheet")
  1. Check for third‑party swizzling and global gesture hooks
  • Temporarily remove or disable libraries that hook gesture/lifecycle behavior. Run the progressive minimal repro approach described earlier.
  1. Compare working vs non‑working project snapshots
  • Diff build settings, Info.plist, Scene/Window code, and linked frameworks. The difference that introduces the bug is often subtle.

Collect: the video/GIF, Xcode pause backtraces (thread backtrace all), Instruments .trace, Console logs, view-hierarchy snapshot, Info.plist, and the minimal repro project.


Workarounds and fixes (keep UIDesignRequiresCompatibility enabled)

Pick the least intrusive workaround that solves the problem for your users:

  1. Present .fullScreen on affected devices (non-invasive, reliable)
  • Detect the compatibility flag and device size, then pick .fullScreen for those devices:
swift
let modal = TestModalViewController()
if Bundle.main.designRequiresCompatibility && isLargeDisplayDevice() {
 modal.modalPresentationStyle = .fullScreen
} else {
 modal.modalPresentationStyle = .pageSheet
}
present(modal, animated: true)
  • Pros: simple, avoids pageSheet path. Cons: UX differs from pageSheet.
  1. Disable interactive swipe-to-dismiss for that sheet (isModalInPresentation)
swift
modal.isModalInPresentation = true // disables swipe-to-dismiss
present(modal, animated: true)
  • Pros: very simple and safe. Cons: removes expected swipe-to-dismiss behavior; add a visible Done button.
  1. Temporarily disable conflicting gestures on the presenting controller
  • If your app uses a UINavigationController, try disabling interactivePopGestureRecognizer while the sheet is present and re-enable on dismissal:
swift
presentingVC.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
modal.presentationController?.delegate = self // implement presentationControllerDidDismiss to re-enable
  • Or require the pop gesture to fail until the sheet pan ends:
swift
if let popGR = presentingVC.navigationController?.interactivePopGestureRecognizer,
 let sheetPan = modal.view.gestureRecognizers?.first(where: { $0 is UIPanGestureRecognizer }) {
 popGR.require(toFail: sheetPan)
}
  • Pros: preserves pageSheet visuals. Cons: fragile — internal gesture implementation can change across iOS versions.
  1. Use presentationController delegate hooks
  • Implement UIAdaptivePresentationControllerDelegate methods to control dismissal attempts and to guard re-enabling gestures or state cleanup.
  1. Add an explicit dismiss control (Done button) and set isModalInPresentation to true
  • This avoids user swipe paths entirely while keeping control in your code.
  1. Remove or update 3rd-party libraries that hook gestures or view controller lifecycle
  • If a particular library triggers the bug, updating or removing it may eliminate the issue.

Note: backgrounding/unbackgrounding “fixes” the UI but is not a viable user-facing solution. Use it only to verify the problem’s symptom.


What to include when filing a bug with Apple (attachments & template)

File via Feedback Assistant and attach everything below. The more precise and self-contained the package, the faster Apple can reproduce and fix it.

Essential items to attach:

  • Title: concise, include UIDesignRequiresCompatibility and devices (e.g., “.pageSheet interactive swipe-to-dismiss freeze when UIDesignRequiresCompatibility=YES on iPhone 14 Pro Max / 17 Pro Max (iOS 26.x)”).
  • Short summary (2‑3 sentences): expected vs actual.
  • Exact reproduction steps (copy/paste). Include Info.plist snippet setting UIDesignRequiresCompatibility = YES.
  • Minimal reproducible Xcode project (public GitHub is ideal). If you cannot publicize the full app, provide a small project that reproduces the issue when built with the same Info.plist settings. Note: mention that the bug is project‑specific (does NOT reproduce in a fresh project) and attach a “failing” small project plus a “clean” project that doesn’t fail.
  • Video/GIF of the freeze.
  • sysdiagnose archive captured while reproducing.
  • Instruments Time Profiler .trace file captured during the freeze.
  • Xcode paused thread backtraces (text output from lldb thread backtrace all) and dSYMs for the build tested.
  • Device info: model identifier (e.g., iPhone16,3), exact iOS build (26.1 build number), region, locale, device uptime.
  • Console logs from Console.app or Xcode while reproducing.
  • List of linked frameworks and third‑party libraries (CocoaPods / SPM / Carthage).
  • Any differences between fresh project and your app (launch arguments, scene lifecycle changes, window/root view controller customizations).
  • Steps you’ve already tried (e.g., reproducer, .fullScreen avoids the issue, isModalInPresentation stops swipe, background/foreground unsticks).

Example problem statement (copy/paste):

  • Title: “.pageSheet swipe-to-dismiss freezes when UIDesignRequiresCompatibility=YES on iPhone 14 Pro Max / 17 Pro Max (iOS 26.x)”
  • Steps to reproduce: [include the numbered steps you provided above].
  • Expected: interactive dismissal completes, viewDidDisappear called.
  • Actual: freeze at ~20% dismissal progress; viewDidDisappear never called; programmatic dismiss still works; background/foreground unblocks.
  • Attach: minimal Xcode project (link), video.mp4, sysdiagnose.zip, timeprofiler.trace, lldb_backtraces.txt, Info.plist, list_of_frameworks.txt.

Also link to the related Apple Developer Forums thread for context: https://developer.apple.com/forums/thread/703791.


FAQ & related threads


Sources

  1. Bottom sheets are broken with UIDesignRequiresCompatibility enabled in iOS 26 (Stack Overflow)
  2. Sheet dismiss gesture with swipe back causes app to freeze (Apple Developer Forums)
  3. Sheet-dismiss gesture with swipe-back gesture causes app to freeze (Stack Overflow)
  4. UIDesignRequiresCompatibility | Apple Developer Documentation
  5. Disabling the pull-down gesture for a sheet | Apple Developer Documentation
  6. Deep Dive: UIDesignRequiresCompatibility — Medium article
  7. HeroTransitions/Hero issue #614 (GitHub)
  8. facebook/react-native issue #26892 (GitHub)

Conclusion

In short: on affected iPhones iOS 26.x, the compatibility path enabled by UIDesignRequiresCompatibility can expose a UIKit gesture/animation state machine bug that leaves a .pageSheet half‑dismissed and the UI unresponsive. Start by creating a minimal repro and collecting Instruments, sysdiagnose and paused main‑thread backtraces; use targeted, per‑device workarounds (present .fullScreen for large devices, disable swipe dismissal with isModalInPresentation, or temporarily disable conflicting gestures) rather than flipping off UIDesignRequiresCompatibility globally. File a Feedback Assistant report with a minimal project, video, sysdiagnose, and thread dumps so Apple can reproduce and fix the underlying UIKit issue.

Authors
Verified by moderation
Moderation
UIKit iOS: Fix .pageSheet Swipe Freeze on Pro Max iOS 26