Blueprint icon indicating copy to clipboard operation
Blueprint copied to clipboard

Implement keyboard reader, which allows observing the position of the keyboard on-screen.

Open kyleve opened this issue 5 years ago • 0 comments

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

  1. As mentioned, introduce KeyboardReader which allows responding to keyboard positioning within an element.

  2. From a discussion on Slack; introduce Conditionals.swift, which allows if, if/else, if/let, etc, operators on elements, to reduce the need for temporary variables and nests logic visually when writing code.

  3. Alongside the demo included, add some helpers to Overlay to make building Overlays easier.

  4. Add similar helpers to StackElement.

  5. Introduce inheritedEnvironment on BlueprintView, so when we're passing through to an internal BlueprintView, we can pass through the base environment to use. I went this route instead of using an AdaptedEnvironment because we want the inner BlueprintView to 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 the Keys used to write into the environment are private, there's no good way to provide a generic merging strategy here.

  6. Change BlueprintView and 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 to self.point(insideSubviewsOnly: point, with: event). This is needed because the KeyboardReader maintains an internal BlueprintView; but we want to pass through touches to lower views as long as those touches do not intersect with an element. Without this change, KeyboardReader erroneously ends up eating touches.

  7. 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.

kyleve avatar Oct 01 '20 02:10 kyleve