Skip to content

fix(ios): multiline inputs must insert a newline on every keyboard - #42

Open
JeremieMercier wants to merge 1 commit into
NativePHP:mainfrom
JeremieMercier:fix/ios-multiline-newline
Open

fix(ios): multiline inputs must insert a newline on every keyboard#42
JeremieMercier wants to merge 1 commit into
NativePHP:mainfrom
JeremieMercier:fix/ios-multiline-newline

Conversation

@JeremieMercier

Copy link
Copy Markdown

The bug

On iOS, a multiline text input (<native:outlined-text-input multiline …>, and the filled/bare variants) never inserts a line break when Return is pressed from a hardware keyboard — the simulator driven from the Mac keyboard, or an iPad with an external keyboard. The field silently drops focus instead. Soft (on-screen) keyboards were unaffected. Android has always behaved correctly (Enter inserts a newline).

Root cause

The multiline branch of NativeUITextInputCore was a TextField(axis: .vertical). SwiftUI's vertical-axis TextField unfocuses on hardware-keyboard Return by design — confirmed by Apple DTS in https://developer.apple.com/forums/thread/760511 — and no modifier combination opts out (.onKeyPress interception also unfocuses). Removing .onSubmit is not enough.

The fix

Rebuild the multiline branch on TextEditor (UITextView-backed), whose return key inserts a line break on every keyboard — the alternative Apple suggests in that thread:

  • An invisible Text mirror re-creates the auto-growing lineLimit(min...max) window the vertical TextField had (same clamps, same default cap of 5 lines); it is accessibilityHidden so VoiceOver doesn't read the typed text twice.
  • Placeholder recreated as an overlay (TextEditor has no placeholder slot), with UITextView's internal container padding pulled back so typed text lines up with the single-line variants inside the same chrome.
  • scrollContentBackground(.hidden) lets the variant chrome keep painting the background.
  • .submitLabel / .onSubmit now attach to single-line fields only. Multiline Return always inserts a newline and @submit is send-button-only — matching what Android multiline (Enter = newline, no keyboard submit) has always done. Documented on BaseTextInput::multiline() and in the core's header.

Value sync, echo prevention, the three sync_mode policies, max_length, keyboard type, disabled/read-only, custom fonts and the iOS 18 selection: binding (@selectionChange) are unchanged and apply to TextEditor as-is. Single-line fields keep a byte-for-byte identical code path. No wire/PHP behavior change; Android untouched.

Verification

  • swiftc -parse clean; full simulator build succeeds and the rebuilt app was manually tested: multiline fields accept newlines from both the Mac keyboard (simulator) and the on-screen keyboard; single-line @submit + keep-focus-on-submit chat pattern unchanged.
  • Wire props verified via the Pest harness (multiline, min_lines, max_lines serialize as before).

🤖 Generated with Claude Code

A vertical-axis TextField drops focus on Return from a hardware keyboard
(simulator driven from the Mac, iPad + external keyboard) instead of
inserting a line break — as-designed per Apple DTS
(developer.apple.com/forums/thread/760511), and no modifier combination
opts out. Soft keyboards inserted the newline, hardware ones never could.

Rebuild the multiline branch of NativeUITextInputCore on TextEditor,
whose return key inserts a line break on every keyboard:

- an invisible Text mirror re-creates the auto-growing
  lineLimit(min...max) window (same clamps, same default cap of 5),
  hidden from VoiceOver so typed text isn't read twice
- placeholder recreated as an overlay (TextEditor has no slot),
  UITextView container padding pulled back so text lines up with the
  single-line variants inside the same chrome
- scrollContentBackground(.hidden) keeps the variant chrome's background

submitLabel/onSubmit now attach to single-line fields only: multiline
Return always inserts a newline and @submit is send-button-only, matching
what Android multiline (Enter = newline, no keyboard submit) always did.
Value sync, echo prevention, sync modes, max_length, keyboard type and
the iOS 18 selection binding are unchanged and apply to TextEditor as-is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant