Implement keyboard reader, which allows observing the position of the keyboard on-screen.
This PR introduces a KeyboardReader type, which allows you to programmatically respond to the keyboard frame as it changes. It does this by maintaining an inner BlueprintView which updates when the keyboard updates.
The simplified use case looks like this:
KeyboardReader { context in
myElement.inset(bottom: context.keyboardFrame.height)
}
Where you return an element in a provider, which updates when the keyboard frame is changed.
There's also some convenience initializers for layering, say, a background (like a list or table) which already respects a keyboard below, eg a bottom button that you want to hug the keyboard:
KeyboardReader {
myList
} adjustedForKeyboard: { context in
myButton.aligned(horizontal: .fill, vertical: .bottom)
}
Changes
-
As mentioned, introduce
KeyboardReaderwhich allows responding to keyboard positioning within an element. -
From a discussion on Slack; introduce
Conditionals.swift, which allowsif,if/else,if/let, etc, operators on elements, to reduce the need for temporary variables and nests logic visually when writing code. -
Alongside the demo included, add some helpers to
Overlayto make buildingOverlays easier. -
Add similar helpers to
StackElement. -
Introduce
inheritedEnvironmentonBlueprintView, so when we're passing through to an internalBlueprintView, we can pass through the base environment to use. I went this route instead of using anAdaptedEnvironmentbecause we want the innerBlueprintViewto override the inherited properties with its own properties (eg, the safe area), but still want to pass through the other values from the outer environment. Because theKeys used to write into the environment are private, there's no good way to provide a generic merging strategy here. -
Change
BlueprintViewand its child views to update how touches are handled; instead of absorbing all touches, instead only absorb touches on subviews. This is done by calling through toself.point(insideSubviewsOnly: point, with: event). This is needed because theKeyboardReadermaintains an internalBlueprintView; but we want to pass through touches to lower views as long as those touches do not intersect with an element. Without this change,KeyboardReadererroneously ends up eating touches. -
Copy over some changes from Listable into
KeyboardObserver, which are needed to move the keyboard observer to be a shared observer – it needs to be a shared observer, because the only way for us to know what the keyboard frame is, is to be constantly observing the keyboard did change notifications. Also copied over the tests.