Skip to content

iOS: Tap gestures on derived keys not working until after scrolling #2528

@stepanLav

Description

@stepanLav

Description

When viewing the key details screen with a scrollable list of derived keys, tapping on a key row does not trigger any action until the user scrolls the list at least once. After scrolling, tap gestures work correctly.

Steps to Reproduce

  1. Open the app and navigate to a key-set that has multiple derived keys
  2. Ensure the list of derived keys is scrollable (more keys than fit on screen)
  3. Without scrolling, tap on any visible derived key row
  4. Observe: Nothing happens - the tap gesture is not recognized
  5. Scroll the list slightly (even just a tiny bit)
  6. Tap on any derived key row again
  7. Observe: Tap gesture now works correctly and navigates to key details

Expected Behavior

Tap gestures on derived key rows should work immediately when the screen loads, without requiring any scrolling interaction first.

Actual Behavior

Tap gestures are not recognized until after the user performs at least one scroll gesture on the list.

Root Cause

The issue is caused by a combination of factors:

  1. NavigationLink placement: The NavigationLink was placed inside the LazyVStack, which can interfere with gesture recognition in SwiftUI
  2. LazyVStack gesture initialization: LazyVStack inside a ScrollView can have delayed gesture recognizer initialization, especially for items that are initially visible without scrolling
  3. Tap area coverage: The tap gesture area may not cover the full width of the row initially

Solution

The fix involves two key changes to ios/PolkadotVault/Screens/KeyDetails/Views/KeyDetailsView+MainList.swift:

  1. Move NavigationLink to ZStack overlay: Changed the container from VStack to ZStack and placed the NavigationLink as an overlay, isolating it from the scrollable content. This matches the pattern used in LogsListView.swift.

  2. Ensure full-width tap area: Added .frame(maxWidth: .infinity) to each derived key row to ensure the entire width is tappable from the start.

Code Changes

// NavigationLink inside LazyVStack
LazyVStack(spacing: 0) {
    ForEach(...) { deriveKey in
        DerivedKeyRow(...)
            .contentShape(Rectangle())
            .onTapGesture { ... }
    }
    NavigationLink(...) { EmptyView() }  // ❌ Inside LazyVStack
}

// Proposed solution - NavigationLink in ZStack overlay
ZStack {
    ScrollView(...) {
        LazyVStack(spacing: 0) {
            ForEach(...) { deriveKey in
                DerivedKeyRow(...)
                    .frame(maxWidth: .infinity)  // ✅ Full width tap area
                    .contentShape(Rectangle())
                    .onTapGesture { ... }
            }
        }
    }
    NavigationLink(...) { EmptyView() }  // ✅ In overlay layer
}

Environment

  • Platform: iOS
  • Component: Key Details Screen (KeyDetailsView)
  • File: ios/PolkadotVault/Screens/KeyDetails/Views/KeyDetailsView+MainList.swift

Additional Context

This is a SwiftUI-specific issue related to gesture recognizer initialization in lazy-loaded views. The pattern of using ZStack with NavigationLink as an overlay is a common solution for this type of gesture conflict in SwiftUI applications.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions