Skip to content

NO-1497: Improve table modal scroll performance - #401

Merged
vivekjyani merged 6 commits into
mainfrom
NO-1497
Oct 28, 2026
Merged

vivekjyani merged 6 commits into
mainfrom
NO-1497

Conversation

@vivekjyani

Copy link
Copy Markdown
Collaborator

Context

The table modal grid scrolled sluggishly with large row counts. Profiling showed every row was being materialized on load (no virtualization), and the SwiftUI body re-evaluated on every scroll frame because the sticky header and left row-number column were kept in sync through @State-driven offsets.

What changed

Single file: Sources/JoyfillUI/View/Fields/TableView/TableModalView.swift

  • Restored LazyVStack virtualization by removing .fixedSize(horizontal: false, vertical: true) from the grid stack — it forced the stack to its full ideal height, eagerly building every row. Now only visible rows are built.
  • Replaced the manual offset sync (ViewOffsetKey preference + @State offset) with UIKit contentOffset KVO. A small TableScrollSync handle + TableGridScrollReader/TableScrollCapture mirror the grid's scroll offset onto the left row-number column (y) and column header (x) on the render thread — off the SwiftUI update path.
  • Extracted leftCornerHeader, columnsHeader, and a TableRowNumbersColumn view; collapsed the duplicated iOS 15/16 branches.

Decisions

  • KVO over a @State-driven offset. Driving the header/left column from the grid's contentOffset in UIKit keeps the sync on the render thread, so steady-state scrolling no longer re-evaluates the modal body each frame.
  • TableScrollSync is a plain class held in @State (not ObservableObject) so mutating the captured scroll views never invalidates the body.
  • .scrollDisabled(true) on iOS 16+ (programmatic contentOffset still works); drag-blocking gesture fallback on iOS 15.

Media

Public API / docs

No public API change — all new types (TableScrollSync, TableGridScrollReader, TableScrollCapture, TableRowNumbersColumn) are internal/private. No docs update needed.

Tests

Full unit suite passes (2334/2334). Scroll smoothness verified manually on-device. No new unit tests — this is a view-layer change not covered by the unit harness; existing table UI tests exercise the accessibility identifiers, all of which are preserved.

Reviewer notes

Behavior is intended to be identical to before apart from the scroll performance improvement. All accessibility identifiers, selection, filter/sort, pencil→edit-sheet, and scrollTo behavior are unchanged.

@vivekjyani vivekjyani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed — approach is sound and the diff is clean; approving with a few non-blocking notes (inline).

One that can't be inlined (not a changed line): collections share this grid UX and still have the same perf bug — CollectionModelView.swift:275 and :510 keep .fixedSize(horizontal: false, vertical: true) plus the old preference-based offset sync. Is that intentionally out of scope / a follow-up ticket?

Comment thread Sources/JoyfillUI/View/Fields/TableView/TableModalView.swift Outdated
Comment thread Sources/JoyfillUI/View/Fields/TableView/TableModalView.swift
Comment thread Sources/JoyfillUI/View/Fields/TableView/TableModalView.swift Outdated
@vivekjyani

Copy link
Copy Markdown
Collaborator Author

Re-reviewed — all inline threads are addressed (DEBUG assert on the superview-walk nil path, on-device check for the scrollTo race, helper types now fileprivate). CI is green. Nothing more to address here.

Collections (CollectionModelView.swift:275/:510) share the same pattern but are intentionally out of scope — tracking as a follow-up ticket. Good to approve.

@VishnuBishnoi VishnuBishnoi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed 031c04f against 616d3d6.

No additional confirmed code defect found. I checked row virtualization, edit/selection/filter paths, observer cleanup, and header/row-number synchronization. The earlier hierarchy assertions and helper access-control comments are addressed.

Approval is pending one UI validation gap: the reply on initial alignment only checks opening at the top, not goto/navigation into a selected row. I posted the focused follow-up in the existing thread: #401 (comment)

Build and all 2,334 unit tests passed at this head. UI CI was skipped, and I did not run the app locally. Please verify row-number/grid alignment immediately after the row-38 goto flow, including modal reappearance and iOS 15. This is a request for evidence, not a claimed reproduction.

@VishnuBishnoi VishnuBishnoi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed 45da8f3 against 92bc067. No blocking findings.

The added row-38 alignment/reappearance test addresses the earlier visibility-only test gap. I verified the fixture, assertion, and UI test target registration, and accept the author's reported iOS 26.5/27 runs as runtime evidence. The previous late-capture concern was never a reproduced bug. No new regression found in virtualization, row editing/selection, filtering, or scroll-observer cleanup.

Exact-head build and all 2,344 unit tests passed. UI CI was skipped; I did not run local tests. iOS 15 remains unverified because the UI test target requires iOS 16 and current CI chooses the latest runtime. These coverage limits are noted, not claimed as full platform coverage.

@vivekjyani
vivekjyani merged commit dabf335 into main Oct 28, 2026
13 checks passed
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.

2 participants