Skip to content

[scroll] Make ComposeView's scrolling and fitting content account for content insets #108

Description

@honghaoz

Context: found while deciding #103, which keeps contentInsetAdjustmentBehavior adjustable on iOS, tvOS and visionOS. Its default stays .never.

Observation

ComposeView's render pass ignores content insets in two places:

  • Scrolling. With the default scrollBehavior = .auto, it turns scrolling on only when the content overflows the bounds.
  • Fitting content. Along an axis the content fits, it makes the content size the bounds' size and centers the content in it.

Insets add scroll space around the content, and UIKit's automatic adjustment also moves the offset so the content starts below the bars. So content that fits the bounds can end up partly out of view with scrolling off:

  • Automatic insets on iOS. With .always, or with .automatic for a view controller's root view inside a navigation controller (a legacy rule), UIKit insets an axis even when its content fits. In a probe on the iPhone 11 simulator, a 500 pt tall ComposeView with fitting content, as a navigation controller's root view, got a 102 pt top and 34 pt bottom adjustment, and UIKit moved the offset to -102. ComposeView kept the content centered in its full bounds with scrolling off, so three 20 pt rows showed 68 pt below the middle of the visible area, and fitting content taller than about 300 pt would run off the bottom of the screen with no way to scroll to it.
  • Insets set by the caller, on both platforms. With a top inset left for an overlay header, fitting content that reaches under the header can't be scrolled out from under it.
  • No insets under a translucent bar. With .never, a full-screen view's fitting content can sit under the navigation bar. Insets are how a caller solves that, which leads to the cases above.

With overflowing content, automatic insets work: the content starts below the bar, the rendered rows match the visible area, and everything scrolls into view. An iOS test added with #103 covers that.

Direction (to decide)

  1. Scrolling: with .auto, turn scrolling on along an axis when the scroll range, counting adjustedContentInset, is positive, not only when the content overflows the bounds.
  2. Fitting content: along an axis the content fits, size the content to the bounds minus the adjusted insets, and center it there, so it centers in the area the insets leave clear and scrolls only when it doesn't fit that area.
  3. Layout: decide whether the content should also lay out for the bounds minus the adjusted insets, or keep laying out for the bounds, the logical viewport in [render] Make visibleSize and the render size exact on macOS, instead of rounded to pixels #102. The second option alone keeps the layout and changes only where fitting content sits.

On macOS, adjustedContentInset is contentInsets, which #103 keeps free of automatic adjustment, so the same rules apply to insets set by the caller.

To verify

  • How UIKit's offset adjustment interacts with ComposeView setting its content size on each render pass when insets apply, for example while rotating.
  • The legacy scroller cases with content insets in ComposeView+RenderBoundsTests.

To do

  1. Pick the direction, and update the .auto scroll decision and the content size along a fitting axis.
  2. Tests on both platforms: fitting content with a top inset scrolls the inset into view and centers in the clear area, overflowing content keeps today's behavior, and iOS automatic insets in a navigation controller keep fitting content reachable.
  3. Decide the scroll bars before applying them, which needs this issue's rule for how insets and scroll bars share space. See the plan.

Related: #102, #103, #113.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions