| 1 | # Mobile navigation |
| 2 | |
| 3 | Home and thread routes share the [root native stack](../../apps/mobile/src/Stack.tsx) |
| 4 | so UIKit can animate the header within one navigation controller. The iPad sidebar |
| 5 | has its own stack. Splitting Home and threads between controllers loses that |
| 6 | continuous transition. |
| 7 | |
| 8 | ## UIKit constraints |
| 9 | |
| 10 | The [react-native-screens patch](../../patches/react-native-screens@4.26.2.patch) |
| 11 | preserves behavior that is easy to break when changing native headers: |
| 12 | |
| 13 | - The brand belongs in `headerTitle`. On iOS 26.5, UIKit morphs a background-free |
| 14 | leading toolbar item's rectangle into the next screen's glass back button, |
| 15 | even when the items have different identifiers. |
| 16 | - UIKit's scroll-edge fade does not recognize Fabric text views. An empty native |
| 17 | label supplies the custom title's geometry. Removing that apparently unused |
| 18 | view changes the fade. |
| 19 | - Leading, trailing, and center item groups need independent caches. A button can |
| 20 | belong to only one group; rebuilding an unchanged group can pull its buttons |
| 21 | out of a group UIKit is still animating. Cache ownership also includes the |
| 22 | header config, whose event emitter changes on remount. |
| 23 | - A horizontal ScrollView at its leading edge must yield to the full-screen back |
| 24 | gesture. Upstream gives every horizontal ScrollView priority, so swiping back |
| 25 | on a code block or table can only bounce its content. |
| 26 | |
| 27 | The patch adds native header props that require code generation and a new binary. |
| 28 | Patch rebuild guidance lives in the |
| 29 | [mobile development README](../../apps/mobile/README.md#development). |
| 30 | |
| 31 | ## Native media presentations |
| 32 | |
| 33 | iOS delegates full-screen video to [AVKit](../../apps/mobile/modules/t3-native-controls/ios/T3NativeVideoPresentation.swift) |
| 34 | and images and documents to [Quick Look](../../apps/mobile/modules/t3-native-controls/ios/T3NativeFilePresentation.swift). |
| 35 | Each framework owns its controls and transition. A separate UIKit zoom transition |
| 36 | prevents AVKit's native Close action from exiting full screen and interferes with |
| 37 | Quick Look's interactive return to its thumbnail. The AVKit entry selector is |
| 38 | guarded, with standard modal presentation as the fallback. |
| 39 | |
| 40 | [Thumbnail registration](../../apps/mobile/modules/t3-native-controls/ios/T3NativePresentation.swift) |
| 41 | holds weak view references for transition and share-sheet anchors. It does not own |
| 42 | the preview; a source row can disappear while a native presentation is open. |
| 43 | Identifiers must distinguish attachments that are visible at the same time. |
| 44 | Native promises complete after dismissal so callers keep local file leases until |
| 45 | the preview or share flow has finished. |
| 46 | |
| 47 | Treat a dismissal request as pending until UIKit finishes its current transition. |
| 48 | Starting another dismissal while presentation or an interactive dismissal is |
| 49 | settling can strand the controller. Video cleanup restores the previous audio |
| 50 | configuration only if it still matches the preview's configuration. It must not |
| 51 | deactivate the shared audio session, which may belong to another player or recorder. |
| 52 | |
| 53 | Video playback holds the first signed asset URL for the lifetime of the preview. |
| 54 | Following credential refreshes reactively would restart playback. Quick Look copies |
| 55 | original bytes into its own temporary directory so preview and sharing cannot |
| 56 | mutate a draft or workspace file. |