Skip to content

fix: close inner-edge gap on split spreads in scroll mode#745

Merged
everpcpc merged 4 commits intoeverpcpc:mainfrom
spawvn:fix/scroll-spread-inner-edge-alignment
Apr 29, 2026
Merged

fix: close inner-edge gap on split spreads in scroll mode#745
everpcpc merged 4 commits intoeverpcpc:mainfrom
spawvn:fix/scroll-spread-inner-edge-alignment

Conversation

@spawvn
Copy link
Copy Markdown
Contributor

@spawvn spawvn commented Apr 26, 2026

Closes #744

Summary

In the DIVINA reader, when "Split Wide Pages" is enabled and the user scrolls horizontally across a wide spread that's been split into two halves, a visible black gap appears in the middle of the spread instead of the two halves meeting seamlessly during the swipe.

The bug only manifests when all of these are true:

  • Page Layout is Auto (or otherwise resolves to single-page in the current orientation)
  • Split Wide Pages is on (Auto / LTR / RTL)
  • Page Transition Style is Scroll
  • Device is in portrait
  • The comic page is narrower than the device screen, so regular portrait pages already letterbox with black bars on the left and right (page aspect taller than screen aspect)

In landscape, Auto resolves to dual-page and the spread becomes a single .split(id, .both) rendered as a two-slot dual-page, which already aligns the halves correctly. In portrait single-page, the spread becomes two separate .split(id, .first) / .split(id, .second) pager items — both centered within their cells. The inner-edge letterbox bar of one half plus the inner-edge bar of the other stack at the cell boundary during the swipe, producing the gap.

Root cause

KMReader/Features/Reader/Views/PageImage/ReaderViewModel+NativePageData.swift:

The single-page .split path used alignment: .center. The dual-page .both path on the same file already does the right thing — .first → .trailing, .second → .leading — so the halves meet flush in the middle of the spread. The single-page path was missed.

Fix

For the .scroll transition only, mirror the dual-page alignment policy:

let alignment: HorizontalAlignment
switch pageTransitionStyle {
case .scroll:
  alignment = (part == .first) ? .trailing : .leading
case .none, .cover, .pageCurl:
  alignment = .center
}

.cover, .pageCurl, and .none keep the legacy centered alignment to preserve the discrete-page metaphor for those transitions and avoid a stationary asymmetry regression for users who don't actually swipe through transitions. (Page Curl is a separate rendering path through NativeImagePageViewController that doesn't go through nativePageData, so it's unaffected either way.)

The xOffset math in NativePageItem_iOS.swift (lines 444–469) and NativePageItem_macOS.swift already flips .trailing / .leading based on readingDirection == .rtl, so the direction-independent mapping above works in both LTR and RTL pagers — same as the existing dual-page case.

Plumbing

nativePageData now takes a pageTransitionStyle: PageTransitionStyle parameter, matching how splitWidePageMode and readingDirection are already passed in. The four call sites in NativePagedPageContentView_iOS, NativePagedPageContentView_macOS, and ReaderViewItemImageView read AppConfig.pageTransitionStyle (which is the same UserDefaults key the @AppStorage binding in DivinaReaderView writes to, so they stay in sync when the user toggles transition style mid-session).

Testing

Not tested. I do not have an Xcode build environment for this project, so the change has not been built or run. The reasoning is purely static:

  • Switch over PageTransitionStyle is exhaustive across all four cases.
  • The single-page .split(.first/.second) items only originate in generateViewItems when !allowDualPairs (verified — see lines 1238–1245 of ReaderViewModel.swift), so this code path is not reachable in dual-page mode and cannot affect dual-page rendering.
  • WebtoonPageView uses its own WebtoonReaderView and does not call nativePageData, so vertical/Webtoon mode is unaffected.
  • CurlPageView / CurlDualPageView render via NativeImagePageViewController which has its own configure(... alignment:) defaulting to .center and does not go through nativePageData, so PageCurl is unaffected.
  • AppConfig is already used elsewhere in KMReader/Features/Reader/Views/ (e.g., DivinaReaderView) without explicit imports, so the call sites should compile.

A maintainer with the build environment should verify:

  1. Reproducing the bug per the conditions in Two-page spreads show inner-edge gap during paged scroll transition (portrait, page is narrower than screen) #744 → confirm the gap disappears with the fix in .scroll.
  2. Cover, PageCurl, and None transitions show no visual change for split spreads.
  3. Dual-page mode (landscape Auto, or explicit Dual layout) is unchanged.
  4. RTL reading direction still produces a seamless spread when scrolling.
  5. Non-split content (regular .page items, .dual pairings of two portrait pages) is unchanged.

🤖 Generated with Claude Code

When Split Wide Pages was enabled and the reader's transition style was
Scroll, single-page mode rendered each split half centered within its
pager cell. For comics whose pages are narrower than the screen, this
produced a letterbox bar on the inner edge of every half, and swiping
across the spread stacked the two inner-edge bars into a visible gap in
the middle of the spread.

Mirror the dual-page (.both) behavior for the single-page split path
when the transition is .scroll: anchor .first to .trailing and .second
to .leading so the inner edges meet flush at the cell boundary during a
swipe. Cover, pageCurl, and none keep the legacy centered alignment to
preserve the discrete-page metaphor and avoid a stationary asymmetry
regression in those modes.

The xOffset math in NativePageItem already auto-flips trailing/leading
for RTL, so the same direction-independent mapping that the dual-page
case uses works here too.

Closes everpcpc#744

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread KMReader/Features/Reader/Views/PageImage/ReaderViewModel+NativePageData.swift Outdated
@everpcpc everpcpc merged commit 866ced9 into everpcpc:main Apr 29, 2026
3 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.

Two-page spreads show inner-edge gap during paged scroll transition (portrait, page is narrower than screen)

2 participants