Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@ Hard-won rules from past corrections, grouped by theme.
- Question the premise before designing around it: when an existing behavior drives a decision, first check whether that behavior is intentional (documented, tested for its own sake, or explained in history) or incidental. Incidental behavior is a candidate to change, not a constraint to satisfy.
- Surface rejected alternatives: when you consider an option and drop it for scope, cost, or risk, state it in one line with the reason. A silently dropped alternative takes the decision away from the reviewer.
- Treat API surface as a variable, not a constraint: when a fix adds compensating logic (invalidation, special cases, extra cache keys) only so that a public type or option stays safe to misuse, first find who can reach that misuse. If only callers outside the module can, propose removing or narrowing the API next to the compensating fix, with the cost of each, before implementing either. Compatibility kept by default is the option that hides its cost.
- Refuse an invalid use where it enters, instead of correcting for it downstream: when tolerating a use that no caller needs, such as a callback changing the settings an operation is applying, takes retries, guards or extra state, make the entry point reject it, with an assertion that keeps the current value and a documented restriction. Each correction tends to expose the next case, while the restriction removes the whole class.
- Enumerate a system's inputs before replacing its evaluation: when computed logic replaces what a system evaluates, list every kind of input the system honors, decide for each whether the logic models it or falls back, and test each decision. An input the logic silently misreads gives a wrong result where the replaced path gave a right one, and nothing flags it, such as a Core Animation group that a computed presentation value doesn't see.
- Check whether an input applies before requiring it to be supported: an input that has no effect, such as an animation that hasn't begun, shouldn't force a fallback or a failure only because the logic can't evaluate it.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
- Delayed animations are now scheduled with Core Animation's `beginTime` instead of a GCD timer.
- Zero-duration transitions now call their completion, and a completion is also called when its animation is torn down early.
- `RenderableTransition.opacity`'s `to` is now optional, and without it the fade ends at the opacity the content sets, for example with the `opacity` modifier, instead of 1.
- `ComposeView.tile()` is now final on macOS. The content lays out for the view's bounds whether or not scroll bars show, so tiling that takes space from the clip view would cover the content. Place accessory views outside the `ComposeView` instead.
- On macOS, while legacy scroll bars show, `ComposeView` lays out its content for its size minus the space the scroll bars take, so they no longer cover the content. Render handlers and renderable updates receive this smaller area as their render bounds, matching `visibleSize`. `onWillLayout` can now run more than once in a render pass, because the content lays out again when a scroll bar takes space. `tile()` is now final on `ComposeView`, because it sets the visible size that the content lays out for.
- Removed `ScrollViewType`. `ScrollView` now has UIKit's scroll view API on macOS too: `contentOffset` replaces `contentOffset()` and `setContentOffset(_:)`, `contentSize` replaces `contentSize()` and `setContentSize(_:)`, `adjustedContentInset` replaces `contentInsets()`, `contentInset` replaces `setContentInsets(_:)`, and `contentOffset` with `visibleSize` replace `bounds()`. On macOS, `setContentOffset(_:)` clamped the offset into the scrollable range, while setting `contentOffset` keeps it as set, as on UIKit. To keep `setBounds(_:)`'s behavior, assign `bounds` on iOS, tvOS and visionOS, which resizes the view around its center, and on macOS set `frame.size`, then `contentOffset`, as it did there except for ignoring the x offset and clamping the y offset into the scrollable range. Setting `frame.size`, then `contentOffset` resizes from the view's origin on every platform. `contentView()` and the macOS `documentView()` are no longer public: on macOS, `contentView()` shared its name with AppKit's `NSScrollView.contentView()`, so a call in an optional or inferred context returned the clip view instead of the document view. Use `documentView` on macOS, and the scroll view itself on iOS, tvOS and visionOS.
- Renamed `BaseScrollView.isScrollable` to UIKit's `isScrollEnabled`, which `ScrollView` now has on macOS too, and the debug event `renderDidUpdateScrollableBehavior(isScrollable:alwaysBounceHorizontal:alwaysBounceVertical:)` to `renderDidUpdateScrollableBehavior(isScrollEnabled:alwaysBounceHorizontal:alwaysBounceVertical:)`.
- On macOS, `ComposeView` no longer supports automatic content insets, magnification or borders, so its content insets stay the same under the title bar and toolbar, and its content always lays out for its bounds. `automaticallyAdjustsContentInsets` and `allowsMagnification` stay `false`, `magnification`, `minMagnification` and `maxMagnification` stay 1, so `setMagnification(_:centeredAt:)` and `magnify(toFit:)` don't magnify either, even through the animator, and `borderType` stays `.noBorder`. Changing them asserts and keeps the current value, and they're final on `ComposeView`.
- On macOS, `ComposeView` no longer supports automatic content insets, magnification, borders or auto-hiding scroll bars, so its content insets stay the same under the title bar and toolbar. `automaticallyAdjustsContentInsets`, `allowsMagnification` and `autohidesScrollers` stay `false`, `magnification`, `minMagnification` and `maxMagnification` stay 1, so `setMagnification(_:centeredAt:)` and `magnify(toFit:)` don't magnify either, even through the animator, and `borderType` stays `.noBorder`. Changing them asserts and keeps the current value, and they're final on `ComposeView`.

### Changes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,11 @@ extension ComposeView {
/// The bounds from the last completed render pass, or nil before the first render.
let previousRenderBounds: CGRect?

/// The viewport bounds proposed for this render pass's layout.
let renderBounds: CGRect
/// The view's bounds when the update was made: the content offset as the origin and the view's size.
///
/// The bounds.size is not always the same as the visible bounds, on macOS, the legacy scroll bar
/// (`NSScrollView.scrollerStyle == .legacy`) takes space from the view's size.
let bounds: CGRect

/// The parent's animation decision handed over with prepared content, capping the render pass that applies it.
/// `.all` for other updates, which caps nothing.
Expand All @@ -71,7 +74,7 @@ extension ComposeView {
lhs.contentEvaluation === rhs.contentEvaluation &&
lhs.updateType == rhs.updateType &&
lhs.previousRenderBounds == rhs.previousRenderBounds &&
lhs.renderBounds == rhs.renderBounds &&
lhs.bounds == rhs.bounds &&
lhs.preparedAnimationDecision == rhs.preparedAnimationDecision
}

Expand Down
Loading
Loading