Skip to main content
← Blog

Address Autocomplete vs. The Keyboard

VauDium ·

Why tapping a dropdown while the keyboard is up is unreasonably hard in React Native.

Address Autocomplete vs. The Keyboard

Simple requirement. Type an address, suggestions appear, tap to select. Standard autocomplete UI.

The keyboard had other plans.

The Discovery

It worked in the simulator. It didn’t work on a real device. Specifically: with the soft keyboard up, tapping a suggestion did nothing.

The cause: when you touch outside a TextInput while the keyboard is up, the system dismisses the keyboard first. onEndEditing fires, the dropdown disappears, and the tap event is lost.

The simulator uses a hardware keyboard — no soft keyboard, no problem. That’s why it worked there.

Attempt 1: Timing

We delayed deactivate() in onEndEditing by 300ms. The dropdown stays visible briefly after the keyboard dismisses, so the tap should have time to register.

It didn’t work. The touch event itself was lost during keyboard dismissal. Neither onPress nor onPressIn fired.

Attempt 2: keyboardShouldPersistTaps

React Native’s ScrollView has a keyboardShouldPersistTaps prop. Set it to "handled" and taps on Pressable components won’t dismiss the keyboard.

Problem: it applies to ALL Pressable components in the ScrollView. Not just the dropdown — every button on the page keeps the keyboard up. State change buttons, navigation buttons, everything.

We tried dynamically switching between "handled" and undefined based on dropdown visibility. The value change during touch processing caused re-renders that cancelled the touch.

Attempt 3: Native Module

JS-level solutions exhausted, we went native. Built a KeyboardPersistView Expo module.

iOS — hitTest Override

On iOS, we overrode hitTest(_:with:) to return self as the touch target. This routes the touch through our view instead of React Native’s normal touch handler.

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    let hit = super.hitTest(point, with: event)
    if hit != nil && hit != self {
        return self  // Handle touch directly
    }
    return hit
}

In touchesEnded, we find which child view was tapped and send the index to JS. The keyboard still goes down, but selection works reliably.

We also tried preventing keyboard dismissal entirely:

  • Not calling super in touchesBegan — didn’t work. Keyboard dismiss happens at the gesture recognizer level.
  • UITapGestureRecognizer with delaysTouchesBegan = true — didn’t work.
  • require(toFail:) on all parent gesture recognizers — didn’t work.
  • UIView.setAnimationsEnabled(false) + immediate becomeFirstResponder — keyboard animations aren’t UIView animations.
  • Rendering the dropdown in a separate UIWindow — keyboard stayed up, but everything else felt wrong.

The hitTest approach — keyboard goes down but selection works — was the pragmatic winner.

Android — The Wall

Android was different. The native view’s dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent — none of them were called.

Two reasons.

First, on Android, views positioned outside their parent’s bounds with position: "absolute" are visible but don’t receive touch events. Unlike iOS.

Second, even with normal flow positioning, Android’s OS consumes the touch event during keyboard dismissal. The event never reaches the view.

FOCUS_BLOCK_DESCENDANTS, isFocusable = false — tried everything. The OS intercepts the touch before the view can see it.

We also tried rendering the dropdown via Portal outside the FlatList. New problems appeared endlessly: position tracking, keyboard height compensation, scroll synchronization.

The Outcome

iOS: solved with a native module. Keyboard goes down, but selection works. Not perfect, but practical.

Android: disabled. The OS-level touch interception can’t be solved at the view level.

Two days on a simple autocomplete UI. Over ten different approaches tried. “Tapping a dropdown while the keyboard is up” turned out to be an unreasonably hard problem.

In mobile development, the keyboard is always the final boss.