Skip to content

Commit fb95fc1

Browse files
authored
fix: KeyboardToolbar on iOS 26 (#978)
## 📜 Description Adopt `KeyboardToolbar` UI for iOS 26. ## 💡 Motivation and Context ⚠️ This is a temporary solution. ⚠️ Starting from iOS 26 we: - have rounder corners for keyboard; - proposed to use liquid glass effect. An original implementation from Safari now uses rounded corners + glass effect. Also instead of "Done" button Apple shows ✔️ Overall current Safari implementation doesn't look so well (in my opinion). If we have a look how `IQKeyboardManager` handles that... well, it's even worse: <p align="center"> <img width="250" src="https://github.com/user-attachments/assets/6500200d-c8a0-444d-b522-de72c4d6bb6d"> </p> So I think the UI for this element is not finalized yet. Anyway current implementation in keyboard-controller doesn't look well at all, so in this PR I'm: - rounding corners; - adding horizontal margins (so toolbar doesn't take full width); - moving toolbar a little bit up, so that it has a small spacing between floating element and keyboard. This UI is "okay" in my understanding (but still not perfect). As a temporary solution I think it's good enough, however I'm currently in a big phase of research with `KeyboardExtender` and `KeyboardBackgroundView`. Theoretically we may want to utilize these components in the future or we can continue our research to see what is the new way of building keyboard "extenders" and how it can be back-ported to older versions. Closes #974 ## 📢 Changelog <!-- High level overview of important changes --> <!-- For example: fixed status bar manipulation; added new types declarations; --> <!-- If your changes don't affect one of platform/language below - then remove this platform/language --> ### JS - move `KeyboardToolbar` constant to separate file; - add `KEYBOARD_HAS_ROUNDED_CORNERS` variable; - make toolbar "floating" in case if keyboard has rounded corners; ## 🤔 How Has This Been Tested? Tested manually on iPhone 16 Pro (iOS 26). ## 📸 Screenshots (if appropriate): |Safari|Keyboard Controller| |------|--------------------| |<img width="250" src="https://github.com/user-attachments/assets/b066013a-2a98-4c59-8ac7-99955c981e82">|<img width="250" src="https://github.com/user-attachments/assets/7861c1d5-643b-42ea-a60c-5cd4b57820b2">| ### Landscape <img width="500" src="https://github.com/user-attachments/assets/96ab8dbf-e168-45c5-b373-794ca337da89"> ## 📝 Checklist - [x] CI successfully passed - [x] I added new mocks and corresponding unit-tests if library API was changed
1 parent e81311d commit fb95fc1

File tree

2 files changed

+58
-15
lines changed

2 files changed

+58
-15
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Platform } from "react-native";
2+
3+
import type { HEX } from "./types";
4+
5+
export const TEST_ID_KEYBOARD_TOOLBAR = "keyboard.toolbar";
6+
export const TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS = `${TEST_ID_KEYBOARD_TOOLBAR}.previous`;
7+
export const TEST_ID_KEYBOARD_TOOLBAR_NEXT = `${TEST_ID_KEYBOARD_TOOLBAR}.next`;
8+
export const TEST_ID_KEYBOARD_TOOLBAR_CONTENT = `${TEST_ID_KEYBOARD_TOOLBAR}.content`;
9+
export const TEST_ID_KEYBOARD_TOOLBAR_DONE = `${TEST_ID_KEYBOARD_TOOLBAR}.done`;
10+
11+
export const KEYBOARD_TOOLBAR_HEIGHT = 42;
12+
export const DEFAULT_OPACITY: HEX = "FF";
13+
export const KEYBOARD_HAS_ROUNDED_CORNERS =
14+
Platform.OS === "ios" && parseInt(Platform.Version, 10) >= 26;
15+
export const OPENED_OFFSET = KEYBOARD_HAS_ROUNDED_CORNERS ? -11 : 0;

src/components/KeyboardToolbar/index.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ import KeyboardStickyView from "../KeyboardStickyView";
99
import Arrow from "./Arrow";
1010
import Button from "./Button";
1111
import { colors } from "./colors";
12+
import {
13+
DEFAULT_OPACITY,
14+
KEYBOARD_HAS_ROUNDED_CORNERS,
15+
KEYBOARD_TOOLBAR_HEIGHT,
16+
OPENED_OFFSET,
17+
TEST_ID_KEYBOARD_TOOLBAR,
18+
TEST_ID_KEYBOARD_TOOLBAR_CONTENT,
19+
TEST_ID_KEYBOARD_TOOLBAR_DONE,
20+
TEST_ID_KEYBOARD_TOOLBAR_NEXT,
21+
TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS,
22+
} from "./constants";
1223

1324
import type { HEX, KeyboardToolbarTheme } from "./types";
1425
import type { KeyboardStickyViewProps } from "../KeyboardStickyView";
@@ -65,15 +76,6 @@ export type KeyboardToolbarProps = Omit<
6576
insets?: SafeAreaInsets;
6677
} & Pick<KeyboardStickyViewProps, "offset" | "enabled">;
6778

68-
const TEST_ID_KEYBOARD_TOOLBAR = "keyboard.toolbar";
69-
const TEST_ID_KEYBOARD_TOOLBAR_PREVIOUS = `${TEST_ID_KEYBOARD_TOOLBAR}.previous`;
70-
const TEST_ID_KEYBOARD_TOOLBAR_NEXT = `${TEST_ID_KEYBOARD_TOOLBAR}.next`;
71-
const TEST_ID_KEYBOARD_TOOLBAR_CONTENT = `${TEST_ID_KEYBOARD_TOOLBAR}.content`;
72-
const TEST_ID_KEYBOARD_TOOLBAR_DONE = `${TEST_ID_KEYBOARD_TOOLBAR}.done`;
73-
74-
const KEYBOARD_TOOLBAR_HEIGHT = 42;
75-
const DEFAULT_OPACITY: HEX = "FF";
76-
7779
/**
7880
* `KeyboardToolbar` is a component that is shown above the keyboard with `Prev`/`Next` buttons from left and
7981
* `Done` button from the right (to dismiss the keyboard). Allows to add customizable content (yours UI elements) in the middle.
@@ -129,15 +131,32 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = (props) => {
129131
{
130132
backgroundColor: `${theme[colorScheme].background}${opacity}`,
131133
},
132-
{
133-
paddingLeft: insets?.left,
134-
paddingRight: insets?.right,
135-
},
134+
!KEYBOARD_HAS_ROUNDED_CORNERS
135+
? {
136+
paddingLeft: insets?.left,
137+
paddingRight: insets?.right,
138+
}
139+
: null,
140+
KEYBOARD_HAS_ROUNDED_CORNERS ? styles.floating : null,
136141
],
137142
[colorScheme, opacity, theme, insets],
138143
);
144+
const containerStyle = useMemo(
145+
() => [
146+
KEYBOARD_HAS_ROUNDED_CORNERS
147+
? {
148+
marginLeft: (insets?.left ?? 0) + 16,
149+
marginRight: (insets?.right ?? 0) + 16,
150+
}
151+
: null,
152+
],
153+
[insets],
154+
);
139155
const offset = useMemo(
140-
() => ({ closed: closed + KEYBOARD_TOOLBAR_HEIGHT, opened }),
156+
() => ({
157+
closed: closed + KEYBOARD_TOOLBAR_HEIGHT,
158+
opened: opened + OPENED_OFFSET,
159+
}),
141160
[closed, opened],
142161
);
143162
const ButtonContainer = button || Button;
@@ -175,7 +194,11 @@ const KeyboardToolbar: React.FC<KeyboardToolbarProps> = (props) => {
175194
);
176195

177196
return (
178-
<KeyboardStickyView enabled={enabled} offset={offset}>
197+
<KeyboardStickyView
198+
enabled={enabled}
199+
offset={offset}
200+
style={containerStyle}
201+
>
179202
<View {...rest} style={toolbarStyle} testID={TEST_ID_KEYBOARD_TOOLBAR}>
180203
{blur}
181204
{showArrows && (
@@ -258,6 +281,11 @@ const styles = StyleSheet.create({
258281
marginRight: 16,
259282
marginLeft: 8,
260283
},
284+
floating: {
285+
alignSelf: "center",
286+
borderRadius: 20,
287+
overflow: "hidden",
288+
},
261289
});
262290

263291
export { colors as DefaultKeyboardToolbarTheme };

0 commit comments

Comments
 (0)