Skip to content

AbstractSceneView: pin scrollView bottom to keyboardLayoutGuide#4

Merged
Sajjon merged 5 commits into
mainfrom
fix_keyboard_avoidance
May 3, 2026
Merged

AbstractSceneView: pin scrollView bottom to keyboardLayoutGuide#4
Sajjon merged 5 commits into
mainfrom
fix_keyboard_avoidance

Conversation

@Sajjon
Copy link
Copy Markdown
Owner

@Sajjon Sajjon commented May 3, 2026

Default setupScrollViewConstraints() previously pinned the scroll view's bottom edge to self.bottomAnchor. When the on-screen keyboard appeared, the scroll view stayed full-height and the keyboard simply covered the lower portion of the form — including primary CTAs (e.g. the "Continue" button on a sign-up / set-password screen). The user had no way to scroll to reveal the covered content.

Switch the default bottom pin to keyboardLayoutGuide.topAnchor, and set keyboardLayoutGuide.usesBottomSafeArea = false in setupAbstractSceneView(). The system layout guide tracks the keyboard's frame automatically:

  • No keyboard on screen → with usesBottomSafeArea = false, the guide's top sits at self.bottomAnchor (under the home indicator). Resting layout is therefore identical to the previous self.bottomAnchor pin — non-text-input scenes are unaffected.
  • Keyboard appears → the guide's top sits at the keyboard's top edge, the scroll view shrinks, and the existing contentLayoutGuide / frameLayoutGuide machinery in BaseScrollableStackViewOwner handles the reflow so the focused field stays visible and the rest of the form is reachable by scrolling.

BaseScrollableStackViewOwner's content-view bottom anchor is split per mode to keep the auto-applied content-inset coherent:

  • PullToRefreshCapable scenes use contentInsetAdjustmentBehavior = .always, which adds a safe-area bottom inset; the content view pins to safeAreaLayoutGuide.bottomAnchor so the inset and content-end align (no blank scrollable strip), and the refresh spinner clears the home indicator when pulled.
  • Other scenes pin the content view to keyboardLayoutGuide.topAnchor (which equals self.bottomAnchor at rest with usesBottomSafeArea = false).

No observer boilerplate (no NotificationCenter.default.addObserver for keyboardWillShow/Hide), no global swizzling (no IQKeyboardManager).

Subclasses that override setupScrollViewConstraints() should propagate the change — see updated DocC example on the override hook.

Default `setupScrollViewConstraints()` previously pinned the scroll
view's bottom edge to `self.bottomAnchor`. When the on-screen keyboard
appeared, the scroll view stayed full-height and the keyboard simply
covered the lower portion of the form — including primary CTAs (e.g.
the "Continue" button on a sign-up / set-password screen). The user had
no way to scroll to reveal the covered content.

Switch the default bottom pin to `keyboardLayoutGuide.topAnchor` (iOS 15+).
The system layout guide tracks the keyboard's frame automatically:

  - No keyboard on screen → top anchor coincides with
    `safeAreaLayoutGuide.bottomAnchor`, so layout looks identical to a
    plain `bottomAnchor` pin in the resting state.
  - Keyboard appears → top anchor sits at the keyboard's top edge, the
    scroll view shrinks, and the existing `contentLayoutGuide` /
    `frameLayoutGuide` machinery in `BaseScrollableStackViewOwner`
    handles the reflow so the field that took focus stays visible and
    the rest of the form is reachable by scrolling.

No observer boilerplate (no `NotificationCenter.default.addObserver` for
`keyboardWillShow/Hide`), no global swizzling (no `IQKeyboardManager`).

Subclasses that override `setupScrollViewConstraints()` should propagate
the change — see updated DocC example on the override hook.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates AbstractSceneView so its owned scroll view tracks the keyboard by pinning its bottom edge to keyboardLayoutGuide.topAnchor, aiming to keep form content reachable when the on-screen keyboard covers the lower part of the screen.

Changes:

  • Changed the default bottom constraint in setupScrollViewConstraints() from bottomAnchor to keyboardLayoutGuide.topAnchor.
  • Expanded the DocC for setupScrollViewConstraints() to explain the keyboard-aware behavior.
  • Updated the override example so subclasses preserve the new keyboard-aware bottom pin.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/NanoViewControllerSceneViews/AbstractSceneView.swift
Comment thread Sources/NanoViewControllerSceneViews/AbstractSceneView.swift Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 3, 2026

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

Two real issues:

1. `BaseScrollableStackViewOwner` was pinning the content view's bottom
   to `self.bottomAnchor` (or `safeAreaLayoutGuide.bottomAnchor` for
   pull-to-refresh), but the scroll view itself now stops at
   `keyboardLayoutGuide.topAnchor`. On devices with a home indicator,
   the content view extended ~34pt below the scroll view's resting-
   state frame, causing an unintended blank scrollable strip.

   Fixed by also pinning the content view's bottom to
   `keyboardLayoutGuide.topAnchor`, matching the scroll view. This also
   removes the now-unnecessary `bottomToSafeArea` branch (the
   pull-to-refresh distinction never affected the actual layout
   target — `keyboardLayoutGuide.topAnchor` coincides with
   `safeAreaLayoutGuide.bottomAnchor` when no keyboard is on screen).

2. The keyboard-avoidance docstring claimed "available everywhere the
   package targets (iOS 15+)", but the package now declares iOS 26 as
   its minimum platform. The "iOS 15+" mention was meant as "the API
   itself doesn't require anything newer," but it misleads consumers
   about the actual deployment-target floor. Reworded to drop the
   version note and keep just the architectural pitch (no observer
   boilerplate, no IQKeyboardManager swizzling).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/NanoViewControllerSceneViews/AbstractSceneView.swift
…rea = false

The previous revision pinned both the scroll view and content view to
`keyboardLayoutGuide.topAnchor`, which by default sits at
`safeAreaLayoutGuide.bottomAnchor` when no keyboard is visible. Effect:
every scene became 34pt shorter on home-indicator devices, including
scenes with no text input — broader than the PR's stated keyboard-
avoidance scope, and a regression for full-bleed layouts.

Fix: set `keyboardLayoutGuide.usesBottomSafeArea = false` in
`setupAbstractSceneView()`. With that property `false`, the guide's
top tracks `self.bottomAnchor` (under the home indicator) when no
keyboard is up, and only moves up when a keyboard is actually on
screen. The resting layout is now identical to pre-keyboard-avoidance
for every scene; only scenes that actually present a keyboard see the
shrink.

Updated the doc comment to reflect the new behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/NanoViewControllerSceneViews/BaseScrollableStackViewOwner.swift Outdated
Pull-to-refresh scenes set `contentInsetAdjustmentBehavior = .always`
in `AbstractSceneView.setupAbstractSceneView()`, which auto-adds a
~34pt safe-area bottom inset on home-indicator devices. Combined with
the previous revision pinning the content view to
`keyboardLayoutGuide.topAnchor` (= `self.bottomAnchor` at rest with
`usesBottomSafeArea = false`), the scrollable area became
contentSize + 34pt-extra-inset = 34pt blank zone below the content.

Fix: restore the per-mode bottom anchor for the content view only:

- `PullToRefreshCapable`: content view pins to
  `safeAreaLayoutGuide.bottomAnchor`. The auto-applied 34pt content
  inset and the safe-area-bound content end now align (no blank zone),
  and the refresh spinner still clears the home indicator when pulled.
- Otherwise: content view pins to `keyboardLayoutGuide.topAnchor`,
  same as before. Full-bleed at rest (under the home indicator),
  shrinks when a keyboard appears.

The scroll view itself still pins to `keyboardLayoutGuide.topAnchor`
in both modes, so keyboard avoidance works regardless.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Sources/NanoViewControllerSceneViews/AbstractSceneView.swift
@Sajjon Sajjon merged commit 89a884e into main May 3, 2026
7 checks passed
@Sajjon Sajjon deleted the fix_keyboard_avoidance branch May 3, 2026 18:35
Sajjon added a commit to Sajjon/Zhip that referenced this pull request May 3, 2026
Picks up the keyboard-avoidance fix
(Sajjon/NanoViewController#4):
`AbstractSceneView` now pins its scroll view bottom to
`keyboardLayoutGuide.topAnchor`, so password / form scenes
(set-encryption-password, restore-from-keystore, etc.) automatically
shrink when the keyboard appears and the user can scroll to reach the
covered Continue button + checkbox.

Co-Authored-By: Claude Opus 4.7 <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.

2 participants