Skip to content

[@expo/ui] feat[iOS] Added contentShape() modifier for SwiftUI#42813

Merged
intergalacticspacehighway merged 12 commits intoexpo:mainfrom
sam-shubham:main
Feb 4, 2026
Merged

[@expo/ui] feat[iOS] Added contentShape() modifier for SwiftUI#42813
intergalacticspacehighway merged 12 commits intoexpo:mainfrom
sam-shubham:main

Conversation

@sam-shubham
Copy link
Copy Markdown
Contributor

@sam-shubham sam-shubham commented Feb 3, 2026

Fixes #42747

Why

SwiftUI’s HStack inside a List does not respond to tap gestures when the user taps on empty space (for example, areas created by Spacer). This is a known SwiftUI behavior and is typically solved using .contentShape(Rectangle()) in native SwiftUI.

However, @expo/ui/swift-ui did not expose a contentShape modifier, making it impossible to implement this common and expected interaction pattern. As a result:

  • Only visible elements like Text or Image responded to taps
  • List rows with spacing behaved inconsistently compared to native SwiftUI
  • Developers could not match native iOS UX expectations

This PR adds first-class support for contentShape in @expo/ui, aligning Expo’s SwiftUI layer with native SwiftUI capabilities.


How

This PR introduces a new contentShape modifier across the full Expo UI stack.

iOS (SwiftUI)

  • Added ContentShapeModifier implemented as a ViewModifier
  • Supports all existing Expo shape types:
    • rectangle
    • roundedRectangle
    • capsule
    • circle
    • ellipse
  • Internally maps to SwiftUI’s native contentShape(...) API
  • Registered the modifier in ViewModifierRegistry under "contentShape"

JavaScript / TypeScript

  • Added contentShape to @expo/ui/swift-ui/modifiers
  • Reuses the existing shapes API for consistency with clipShape and containerShape
  • Exported via the modifiers index and included in BuiltInModifier typing

Developer Experience

  • API mirrors native SwiftUI usage
  • Works seamlessly with onTapGesture
  • Makes empty layout areas (e.g. Spacer) participate in hit testing

Test Plan

Manual Testing

  1. Open UI → Modifiers Screen in the native component list app on iOS
  2. Navigate to “Content Shape Modifier” section
  3. Tap between the left and right text in the first HStack (without contentShape)
    • ❌ Tap does not trigger
  4. Tap between the texts in the second HStack (with contentShape(rectangle()))
    • ✅ Tap triggers alert
    • ✅ Counter increments correctly
  5. Verified taps work across the entire row, including spacer / empty areas

Expected vs Actual

  • Before: Only Text elements receive tap events
  • After: Entire row responds to taps, matching native SwiftUI behavior
Screen.Recording.2026-02-03.at.2.47.57.PM.mov

Checklist

  • I added a changelog.md entry and rebuilt the package sources according to the guide
  • This diff works correctly with npx expo prebuild & EAS Build
  • API is consistent with existing clipShape and containerShape modifiers
  • No breaking changes introduced

Summary by CodeRabbit

  • New Features
    • Added contentShape modifier to define interactive hit-test areas independent of visual boundaries, enabling more flexible touch responsiveness for UI elements.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

Subscribed to pull request

File Patterns Mentions
docs/** @amandeepmittal
packages/expo-ui/** @aleqsio, @behenate, @douglowder

Generated by CodeMention

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 3, 2026

📝 Walkthrough

Walkthrough

Implements a new contentShape() modifier for SwiftUI hit testing that allows interactive gestures to respond to empty space within containers. Adds TypeScript API declarations and implementations, creates a Swift ViewModifier supporting multiple shape types, registers it in the modifier system, and demonstrates usage in a UI test screen.

Changes

Cohort / File(s) Summary
TypeScript API & Exports
packages/expo-ui/src/swift-ui/modifiers/contentShape.ts, packages/expo-ui/build/swift-ui/modifiers/contentShape.d.ts
Defines new contentShape(shape: Shape) function that creates a modifier config. Includes JSDoc documentation with usage examples for hit-testing non-visible areas.
Modifier Index Updates
packages/expo-ui/src/swift-ui/modifiers/index.ts, packages/expo-ui/build/swift-ui/modifiers/index.d.ts
Imports contentShape, extends BuiltInModifier union type to include it, and re-exports for public API exposure.
Swift Implementation
packages/expo-ui/ios/Modifiers/ContentShapeModifier.swift, packages/expo-ui/ios/Modifiers/ViewModifierRegistry.swift
New ContentShapeModifier ViewModifier that applies contentShape based on shape type (rectangle, capsule, circle, ellipse, roundedRectangle). Registered in modifier factory registry.
UI Demo & Documentation
apps/native-component-list/src/screens/UI/ModifiersScreen.ios.tsx, packages/expo-ui/CHANGELOG.md
Adds demonstration section in ModifiersScreen comparing contentShape behavior with and without the modifier. Includes state tracking and tap gesture examples. Updates changelog with feature entry.

Sequence Diagram(s)

Not applicable — changes represent a straightforward modifier registration and application pattern without complex multi-component interactions or non-trivial sequential flows.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

bot: passed checks, bot: fingerprint compatible

Suggested reviewers

  • hassankhan

Poem

🐰 Hops with joy through SwiftUI's delight,
A contentShape modifier shines so bright!
Now Spacers respond to eager taps,
Filling in those interactive gaps—
Hit testing dreams come true, hip-hip-hooray! 🎉✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a contentShape() modifier for SwiftUI in the @expo/ui package on iOS.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #42747: contentShape modifier accepts all required shape types (rectangle, roundedRectangle, capsule, circle, ellipse), aligns with existing modifiers (clipShape, containerShape), is usable in JS/TS, and enables hit-testing on empty layout areas.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the contentShape modifier as specified in issue #42747; the addition of demo UI in ModifiersScreen and changelog entry are appropriate supporting changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@expo-bot expo-bot added the bot: suggestions ExpoBot has some suggestions label Feb 3, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/native-component-list/src/screens/UI/ModifiersScreen.ios.tsx`:
- Around line 719-724: The onTapGesture handler reads the stale
contentShapeButtonCounter and calls
setcontentShapeButtonCounter(contentShapeButtonCounter + 1), which can drop
rapid taps; switch to a functional state update in the onTapGesture handler (use
setcontentShapeButtonCounter(prev => prev + 1)) and compute the new count from
prev so the Alert uses the correct incremented value (e.g., call Alert.alert
with the nextCount derived inside the functional updater or invoke Alert from
within the updater closure using prev to build nextCount). This change affects
the onTapGesture callback and the setcontentShapeButtonCounter usage to ensure
increments and displayed counts never use stale state.

@expo-bot expo-bot added bot: needs changes ExpoBot found things that don't meet our guidelines and removed bot: suggestions ExpoBot has some suggestions labels Feb 3, 2026
@sam-shubham sam-shubham changed the title [@expo/ui] feat(swift-ui) Added contentShape() modifier for SwiftUI [@expo/ui] feat[iOS] Added contentShape() modifier for SwiftUI Feb 3, 2026
@expo-bot expo-bot added bot: passed checks ExpoBot has nothing to complain about and removed bot: needs changes ExpoBot found things that don't meet our guidelines labels Feb 3, 2026
sam-shubham and others added 2 commits February 3, 2026 15:52
Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
@expo-bot expo-bot added bot: suggestions ExpoBot has some suggestions and removed bot: passed checks ExpoBot has nothing to complain about labels Feb 3, 2026
Copy link
Copy Markdown
Contributor

@intergalacticspacehighway intergalacticspacehighway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sam-shubham for the PR.

One last thing: Can you regenerate the docs by running below command?

et gdad 'expo-ui/swift-ui/modifiers'

@sam-shubham
Copy link
Copy Markdown
Contributor Author

'expo-ui/swift-ui/modifiers'

Done

Copy link
Copy Markdown
Member

@amandeepmittal amandeepmittal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the docs data files for the non-expo-ui packages that are included in this PR.

@sam-shubham
Copy link
Copy Markdown
Contributor Author

Please remove the docs data files for the non-expo-ui packages that are included in this PR.

Done 👍

@intergalacticspacehighway
Copy link
Copy Markdown
Contributor

@sam-shubham Can you also fix the failing CI lint issues?

sam-shubham and others added 2 commits February 3, 2026 21:54
@expo-bot
Copy link
Copy Markdown
Collaborator

expo-bot commented Feb 3, 2026

Hi there! 👋 I'm a bot whose goal is to ensure your contributions meet our guidelines.

I've found some issues in your pull request that should be addressed (click on them for more details) 👇

⚠️ Suggestion: Missing links in changelog entries


I've added some suggestions below, you can just apply and commit them 😉


Generated by ExpoBot 🤖 against 504859e

@sam-shubham
Copy link
Copy Markdown
Contributor Author

@sam-shubham Can you also fix the failing CI lint issues?

I tried to fixx all of them which were there can you have a look once..

Copy link
Copy Markdown
Member

@amandeepmittal amandeepmittal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@sam-shubham
Copy link
Copy Markdown
Contributor Author

sam-shubham commented Feb 3, 2026

hey @amandeepmittal @intergalacticspacehighway can you help m with the "ios unit test flow" failure in CI

@intergalacticspacehighway intergalacticspacehighway merged commit cfef54e into expo:main Feb 4, 2026
14 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot: suggestions ExpoBot has some suggestions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[@expo/ui] Add contentShape() modifier for SwiftUI hit testing

4 participants