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.
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
- Create an iOS app with deployment target iOS 13.0.
- In Info.plist set:
UIDesignRequiresCompatibility = YES
- Present a modal view controller using
.pageSheet. - Swipe down to dismiss the modal.
- Observe freeze mid-gesture.
Minimal code sample
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.
viewDidDisappearis called.- App remains responsive.
Actual results
- App freezes mid-dismissal gesture; UI becomes unresponsive.
- No crash or console error.
viewDidDisappearis 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
UIDesignRequiresCompatibilityand.pageSheetinteractive dismissal? - What practical workarounds exist that do not require disabling
UIDesignRequiresCompatibilityglobally (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)
- Minimal reproducible example & project differences
- Likely causes: gesture conflicts, compatibility layout, and main‑thread stalls
- How to debug: logs, Instruments, main‑thread backtraces and view hierarchy dumps
- Workarounds and fixes that don’t require turning off UIDesignRequiresCompatibility globally
- What to include when filing a bug with Apple (attachments & template)
- FAQ & related threads
- Sources
- Conclusion
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 = YESand 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:
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:
<key>UIDesignRequiresCompatibility</key>
<true/>
If the problem doesn’t reproduce with a plain new app, build a progressive minimal repro:
- Start from the fresh project and add your app’s Info.plist, then test.
- Add your app’s Launch arguments, entitlements, and build settings. Test again.
- Add frameworks one-by-one (or group-by-group). Test.
- 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:
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)
UIDesignRequiresCompatibilitycan 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.
-
Build a debug-symbol (not stripped) build and reproduce on a physical affected device (not simulator). Include the app’s exact Info.plist.
-
Capture a video/GIF of the freeze — this is invaluable.
-
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.
- 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.
- 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”.
- 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.)
- 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:
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))")
}
}
}
- Lightweight in‑app instrumentation
- Add os_signpost / OSLog around presentation/dismissal points and in viewWillAppear/Disappear to get timestamps in Console. Example:
import os
let sheetLog = OSLog(subsystem: "com.example", category: "sheet")
os_signpost(.begin, log: sheetLog, name: "presentSheet")
- 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.
- 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:
- Present .fullScreen on affected devices (non-invasive, reliable)
- Detect the compatibility flag and device size, then pick .fullScreen for those devices:
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.
- Disable interactive swipe-to-dismiss for that sheet (
isModalInPresentation)
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.
- Temporarily disable conflicting gestures on the presenting controller
- If your app uses a UINavigationController, try disabling
interactivePopGestureRecognizerwhile the sheet is present and re-enable on dismissal:
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:
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.
- Use presentationController delegate hooks
- Implement
UIAdaptivePresentationControllerDelegatemethods to control dismissal attempts and to guard re-enabling gestures or state cleanup.
- Add an explicit dismiss control (Done button) and set
isModalInPresentationto true
- This avoids user swipe paths entirely while keeping control in your code.
- 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
-
Q: Is this a known Apple bug?
A: There are public reports on the Apple Developer Forums describing similar gesture-ordering freezes; that strongly suggests a UIKit regression or an edge-case interaction in iOS 26.x: https://developer.apple.com/forums/thread/703791. -
Q: Does turning off UIDesignRequiresCompatibility fix it?
A: Yes — disabling the flag avoids the compatibility code path and the freeze. But that may force non‑optimal layouts on new devices, which is why targeted workarounds are preferred. -
Q: Any community threads to watch?
A: Related community discussions exist on Stack Overflow and GitHub where pageSheet/gesture interactions were discussed: -
Q: Should I keep isModalInPresentation = true as permanent fix?
A: It’s a legitimate workaround, but it removes a platform‑expected behavior (swipe-to-dismiss). Use it temporarily or when you provide an alternate dismiss affordance.
Sources
- Bottom sheets are broken with UIDesignRequiresCompatibility enabled in iOS 26 (Stack Overflow)
- Sheet dismiss gesture with swipe back causes app to freeze (Apple Developer Forums)
- Sheet-dismiss gesture with swipe-back gesture causes app to freeze (Stack Overflow)
- UIDesignRequiresCompatibility | Apple Developer Documentation
- Disabling the pull-down gesture for a sheet | Apple Developer Documentation
- Deep Dive: UIDesignRequiresCompatibility — Medium article
- HeroTransitions/Hero issue #614 (GitHub)
- 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.