react-native-ui-lib
react-native-ui-lib copied to clipboard
RCTBridge parentBridge is nil when presenting custom keyboard via presentCustomInputComponent
Description
When calling presentCustomInputComponent in iOS native code to display a custom keyboard, the following line in Objective-C:
RCTBridge* bridge = [self.bridge valueForKey:@"parentBridge"];
Related to
- [x] Components
- [ ] Demo
- [ ] Docs
- [ ] Typings
Steps to reproduce
- Register a keyboard component using KeyboardRegistry.registerKeyboard("unicorn.ImagesKeyboard", () => ImagesKeyboard).
- Call CustomInputControllerTemp.presentCustomInputComponent(...) from JS.
- In native code (presentCustomInputComponent), parentBridge is nil.
- The keyboard view shows but is blank (white).
Expected behavior
Actual behavior
More Info
Code snippet
import React from "react"
import { ScrollView, StyleProp, ViewStyle } from "react-native"
import { Keyboard, View, Text, Image, Spacings } from "react-native-ui-lib"
import _ from "lodash"
const KeyboardRegistry = Keyboard.KeyboardRegistry
const images: string[] = [
"https://images.pexels.com/photos/1148521/pexels-photo-1148521.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=200",
"https://images.pexels.com/photos/1528975/pexels-photo-1528975.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=200",
"https://images.pexels.com/photos/1495580/pexels-photo-1495580.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=200",
"https://images.pexels.com/photos/943150/pexels-photo-943150.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=200",
"https://images.pexels.com/photos/1769408/pexels-photo-1769408.jpeg?auto=compress&cs=tinysrgb&dpr=1&h=200",
]
function ImagesKeyboard() {
return (
<View flex>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ padding: Spacings.s4 } as StyleProp<ViewStyle>}
>
{images.map((image, i) => (
<Image
key={i}
source={{ uri: image }}
style={{ height: "100%", width: 200 }}
marginR-s4
/>
))}
</ScrollView>
</View>
)
}
function CustomKeyboard() {
return (
<View flex bg-violet80 center>
<Text h2>Custom Keyboard</Text>
</View>
)
}
// Register keyboards with registry
KeyboardRegistry.registerKeyboard("unicorn.ImagesKeyboard", () => ImagesKeyboard())
KeyboardRegistry.registerKeyboard("unicorn.CustomKeyboard", () => CustomKeyboard())
import { AppStackScreenProps } from "@/navigators"
import { observer } from "mobx-react-lite"
import React, { useCallback, useRef, useState } from "react"
import { StyleSheet, TextInput } from "react-native"
import {
Button,
Colors,
Constants,
Keyboard,
Spacings,
Switch,
Text,
View,
} from "react-native-ui-lib"
import "./CustomKeyboard"
const KeyboardAccessoryView = Keyboard.KeyboardAccessoryView
const KeyboardUtils = Keyboard.KeyboardUtils
const KeyboardRegistry = Keyboard.KeyboardRegistry
const TrackInteractive = true
const demoKeyboards = [
{
id: "unicorn.ImagesKeyboard",
},
{
id: "unicorn.CustomKeyboard",
},
]
export const ConversationDetailScreen = observer(
({ route }: AppStackScreenProps<"ConversationDetail">) => {
const [customKeyboard, setCustomKeyboard] = useState<any>({})
const [receivedKeyboardData, setReceivedKeyboardData] = useState<string | undefined>()
const [useSafeArea, setUseSafeArea] = useState<boolean>(true)
const [keyboardOpenState, setKeyboardOpenState] = useState<boolean>(false)
const [keyboardAccessoryViewHeight, setKeyboardAccessoryViewHeight] = useState<
number | undefined
>()
const textInputRef = useRef(null)
const textInputRef2 = useRef(null)
const onKeyboardItemSelected = useCallback((component?: string, args?: any) => {
const receivedData = `onItemSelected from "${component}"\nreceived params: ${JSON.stringify(args)}`
setReceivedKeyboardData(receivedData)
}, [])
const onKeyboardResigned = useCallback(() => {
resetKeyboardView()
}, [])
const isCustomKeyboardOpen = useCallback(() => {
return keyboardOpenState && !_.isEmpty(customKeyboard)
}, [keyboardOpenState, customKeyboard])
const resetKeyboardView = useCallback(() => {
setCustomKeyboard({})
}, [])
const dismissKeyboard = useCallback(() => {
KeyboardUtils.dismiss()
}, [])
const toggleUseSafeArea = useCallback(() => {
setUseSafeArea((prev) => !prev)
if (isCustomKeyboardOpen()) {
dismissKeyboard()
showLastKeyboard()
}
}, [isCustomKeyboardOpen, dismissKeyboard])
const showLastKeyboard = useCallback(() => {
setCustomKeyboard({})
setTimeout(() => {
setKeyboardOpenState(true)
setCustomKeyboard((prev: any) => ({ ...prev }))
}, 500)
}, [])
const showKeyboardView = useCallback((component: string, title?: string) => {
setKeyboardOpenState(true)
setCustomKeyboard({
component,
initialProps: { title },
})
}, [])
const onHeightChanged = useCallback((height: number) => {
if (Constants.isIOS) {
setKeyboardAccessoryViewHeight(height)
}
}, [])
const renderKeyboardAccessoryViewContent = useCallback(
() => (
<View style={styles.keyboardContainer} paddingV-s4>
<View bg-white row spread centerV paddingH-s5 paddingV-s3>
<TextInput ref={textInputRef} placeholder="Test" onFocus={resetKeyboardView} />
<Button link grey10 onPress={KeyboardUtils.dismiss} marginL-s2 label="Test" />
</View>
<View row paddingH-s4 marginT-s2 spread>
<View row>
{demoKeyboards.map((keyboard, index) => (
<Button
key={`${keyboard.id}_${index}`}
grey10
link
onPress={() => showKeyboardView(keyboard.id)}
marginR-s2
label="Test"
/>
))}
</View>
<Button grey10 label="Reset" link onPress={resetKeyboardView} />
</View>
</View>
),
[showKeyboardView, resetKeyboardView],
)
const requestShowKeyboard = useCallback(() => {
KeyboardRegistry.requestShowKeyboard("unicorn.ImagesKeyboard")
}, [])
const onRequestShowKeyboard = useCallback(() => {
setCustomKeyboard({
component: "unicorn.ImagesKeyboard",
initialProps: { title: "Keyboard 1 opened by button" },
})
}, [])
const safeAreaSwitchToggle = useCallback(() => {
if (!Constants.isIOS) {
return null
}
return (
<View center>
<View style={styles.separatorLine} />
<View centerV row margin-10>
<Text text80 grey40>
Safe Area Enabled:
</Text>
<Switch value={useSafeArea} onValueChange={toggleUseSafeArea} marginL-14 />
</View>
<View style={styles.separatorLine} />
</View>
)
}, [useSafeArea, toggleUseSafeArea])
return (
<View flex style={{ marginTop: 100 }}>
<Button
grey10
link
onPress={() => showKeyboardView("unicorn.ImagesKeyboard")}
label="Test213"
/>
<View flex>
<KeyboardAccessoryView
renderContent={renderKeyboardAccessoryViewContent}
onHeightChanged={onHeightChanged}
trackInteractive={TrackInteractive}
kbInputRef={textInputRef}
kbComponent={customKeyboard.component}
kbInitialProps={customKeyboard.initialProps}
onItemSelected={onKeyboardItemSelected}
onKeyboardResigned={onKeyboardResigned}
onRequestShowKeyboard={onRequestShowKeyboard}
useSafeArea={useSafeArea}
addBottomView={useSafeArea}
allowHitsOutsideBounds
/>
</View>
</View>
)
},
)
const styles = StyleSheet.create({
scrollContainer: {
paddingHorizontal: Spacings.s5,
flex: 1,
justifyContent: "center",
},
textField: {
flex: 1,
backgroundColor: Colors.grey60,
paddingVertical: Spacings.s2,
paddingHorizontal: Spacings.s4,
borderRadius: 8,
},
button: {
padding: Spacings.s2,
},
keyboardContainer: {
backgroundColor: Colors.white,
borderWidth: 1,
borderColor: Colors.grey60,
},
separatorLine: {
flex: 1,
height: 1,
backgroundColor: Colors.grey80,
},
})
Screenshots/Video
Environment
- React Native version: 0.76.9
- React Native UI Lib version: 7.44.0
Affected platforms
- [ ] Android
- [x] iOS
- [ ] Web
@ethanshar please help me check, thanks
Hello,
We have a version that supports new-arch (RN77), you can use the next tag for now.
Please make sure to go over the v8 doc, it includes breaking changes and some known issues.
Please close this ticket if it solved your bug.