Как автоматически выдвинуть окно из-за клавиатуры, когда TextInput имеет фокус?
Я видел этот хак для нативных приложений для автоматической прокрутки окна, но удивлялся, как лучше это сделать для реагирующих нативных... Когда поле получает фокус и располагается низко в представлении, клавиатура закроет текстовое поле, Вы можете увидеть эту проблему в примере представления TextInputExample.js в UIExplorer. У кого-нибудь есть хорошее решение?
14 ответов
Ответ 2017
KeyboardAvoidingView
это, наверное, лучший способ пойти сейчас. Проверьте документы здесь. Это действительно просто по сравнению с Keyboard
модуль, который дает разработчику больше контроля над выполнением анимации. Спенсер Карли продемонстрировал все возможные способы в своем блоге.
2015 Ответ
Правильный способ сделать это в react-native
не требует внешних библиотек, использует нативный код и включает анимацию.
Сначала определите функцию, которая будет обрабатывать onFocus
событие для каждого TextInput
(или любой другой компонент, к которому вы хотите перейти):
// Scroll a component into view. Just pass the component ref string.
inputFocused (refName) {
setTimeout(() => {
let scrollResponder = this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(this.refs[refName]),
110, //additionalOffset
true
);
}, 50);
}
Затем в вашей функции рендеринга:
render () {
return (
<ScrollView ref='scrollView'>
<TextInput ref='username'
onFocus={this.inputFocused.bind(this, 'username')}
</ScrollView>
)
}
Это использует RCTDeviceEventEmitter
для событий и размеров клавиатуры измеряет положение компонента, используя RCTUIManager.measureLayout
и вычисляет точное движение прокрутки, необходимое в scrollResponderInputMeasureAndScrollToKeyboard
,
Вы можете поиграть с additionalOffset
параметр, чтобы соответствовать потребностям вашего конкретного дизайна пользовательского интерфейса.
Facebook с открытым исходным кодом KeyboardAvoidingView
в реагировать на родной 0.29, чтобы решить эту проблему. Документацию и пример использования можно найти здесь.
Мы объединили некоторую часть кода кода response-native-keyboard-spacer и кода из @Sherlock, чтобы создать компонент KeyboardHandler, который можно обернуть вокруг любого представления с элементами TextInput. Работает как шарм!:-)
/**
* Handle resizing enclosed View and scrolling to input
* Usage:
* <KeyboardHandler ref='kh' offset={50}>
* <View>
* ...
* <TextInput ref='username'
* onFocus={()=>this.refs.kh.inputFocused(this,'username')}/>
* ...
* </View>
* </KeyboardHandler>
*
* offset is optional and defaults to 34
* Any other specified props will be passed on to ScrollView
*/
'use strict';
var React=require('react-native');
var {
ScrollView,
View,
DeviceEventEmitter,
}=React;
var myprops={
offset:34,
}
var KeyboardHandler=React.createClass({
propTypes:{
offset: React.PropTypes.number,
},
getDefaultProps(){
return myprops;
},
getInitialState(){
DeviceEventEmitter.addListener('keyboardDidShow',(frames)=>{
if (!frames.endCoordinates) return;
this.setState({keyboardSpace: frames.endCoordinates.height});
});
DeviceEventEmitter.addListener('keyboardWillHide',(frames)=>{
this.setState({keyboardSpace:0});
});
this.scrollviewProps={
automaticallyAdjustContentInsets:true,
scrollEventThrottle:200,
};
// pass on any props we don't own to ScrollView
Object.keys(this.props).filter((n)=>{return n!='children'})
.forEach((e)=>{if(!myprops[e])this.scrollviewProps[e]=this.props[e]});
return {
keyboardSpace:0,
};
},
render(){
return (
<ScrollView ref='scrollView' {...this.scrollviewProps}>
{this.props.children}
<View style={{height:this.state.keyboardSpace}}></View>
</ScrollView>
);
},
inputFocused(_this,refName){
setTimeout(()=>{
let scrollResponder=this.refs.scrollView.getScrollResponder();
scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
React.findNodeHandle(_this.refs[refName]),
this.props.offset, //additionalOffset
true
);
}, 50);
}
}) // KeyboardHandler
module.exports=KeyboardHandler;
Сначала вам нужно установить https://github.com/johanneslumpe/react-native-keyboardevents.
- В XCode в навигаторе проекта щелкните правой кнопкой мыши Библиотеки Lib Добавить файлы в [имя вашего проекта] Перейдите в node_modules ➜act-native-keyboardevents и добавьте файл.xcodeproj
- В XCode в навигаторе проекта выберите ваш проект. Добавьте lib*.a из проекта keyboardevents к этапам сборки вашего проекта. ➜ Связать двоичные файлы с библиотеками. Нажмите на файл.xcodeproj, который вы добавили ранее, в навигаторе проекта и перейдите на вкладку Параметры сборки. Убедитесь, что включено "Все" (вместо "Основной"). Найдите пути поиска заголовка и убедитесь, что они содержат как $(SRCROOT)/../act-native/React, так и $(SRCROOT)/../../React - пометьте оба как рекурсивные.
- Запустите ваш проект (Cmd+R)
Затем вернемся в землю javascript:
Вам нужно импортировать response-native-keyboardevents.
var KeyboardEvents = require('react-native-keyboardevents');
var KeyboardEventEmitter = KeyboardEvents.Emitter;
Затем, по вашему мнению, добавьте некоторое состояние для пространства клавиатуры и обновите после прослушивания событий клавиатуры.
getInitialState: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, (frames) => {
this.setState({keyboardSpace: frames.end.height});
});
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, (frames) => {
this.setState({keyboardSpace: 0});
});
return {
keyboardSpace: 0,
};
},
Наконец, добавьте разделитель к вашей функции рендеринга под всем, чтобы при увеличении размера он увеличивал ваши данные.
<View style={{height: this.state.keyboardSpace}}></View>
Также возможно использовать анимационный API, но для простоты мы просто настраиваемся после анимации.
Реакция -нативная клавиатура-осведомленная-прокрутка-просмотр решила проблему для меня. реакция на встроенную клавиатуру в GitHub
Попробуй это:
import React, {
DeviceEventEmitter,
Dimensions
} from 'react-native';
...
getInitialState: function() {
return {
visibleHeight: Dimensions.get('window').height
}
},
...
componentDidMount: function() {
let self = this;
DeviceEventEmitter.addListener('keyboardWillShow', function(e: Event) {
self.keyboardWillShow(e);
});
DeviceEventEmitter.addListener('keyboardWillHide', function(e: Event) {
self.keyboardWillHide(e);
});
}
...
keyboardWillShow (e) {
let newSize = Dimensions.get('window').height - e.endCoordinates.height;
this.setState({visibleHeight: newSize});
},
keyboardWillHide (e) {
this.setState({visibleHeight: Dimensions.get('window').height});
},
...
render: function() {
return (<View style={{height: this.state.visibleHeight}}>your view code here...</View>);
}
...
Это сработало для меня. Представление в основном сжимается при отображении клавиатуры и восстанавливается, когда оно скрыто.
Возможно, уже поздно, но лучшее решение - использовать встроенную библиотеку IQKeyboardManager
Просто перетащите каталог IQKeyboardManager из демонстрационного проекта в ваш проект iOS. Вот и все. Также вы можете установить некоторые значения, как, например, isToolbar, или расстояние между вводом текста и клавиатурой в файле AppDelegate.m. Более подробную информацию о настройке можно найти в ссылке на страницу GitHub, которую я добавил.
Просто хотел упомянуть, сейчас есть KeyboardAvoidingView
в рн. Просто импортируйте его и используйте как любой другой модуль в RN.
Вот ссылка на коммит на RN:
https://github.com/facebook/react-native/commit/8b78846a9501ef9c5ce9d1e18ee104bfae76af2e
Доступен с 0.29.0
Они также включили пример на UIExplorer.
Я использовал TextInput.onFocus и ScrollView.scrollTo.
...
<ScrollView ref="scrollView">
...
<TextInput onFocus={this.scrolldown}>
...
scrolldown: function(){
this.refs.scrollView.scrollTo(width*2/3);
},
@Стивен
Если вы не возражаете против анимации высоты с той же скоростью, с которой появляется клавиатура, вы можете просто использовать LayoutAnimation, чтобы, по крайней мере, высота не встала на место. например
импортируйте LayoutAnimation из Reaction-native и добавьте следующие методы в ваш компонент.
getInitialState: function() {
return {keyboardSpace: 0};
},
updateKeyboardSpace: function(frames) {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: frames.end.height});
},
resetKeyboardSpace: function() {
LayoutAnimation.configureNext(animations.layout.spring);
this.setState({keyboardSpace: 0});
},
componentDidMount: function() {
KeyboardEventEmitter.on(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.on(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
componentWillUnmount: function() {
KeyboardEventEmitter.off(KeyboardEvents.KeyboardDidShowEvent, this.updateKeyboardSpace);
KeyboardEventEmitter.off(KeyboardEvents.KeyboardWillHideEvent, this.resetKeyboardSpace);
},
Некоторые примеры анимации (я использую весеннюю выше):
var animations = {
layout: {
spring: {
duration: 400,
create: {
duration: 300,
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.opacity,
},
update: {
type: LayoutAnimation.Types.spring,
springDamping: 400,
},
},
easeInEaseOut: {
duration: 400,
create: {
type: LayoutAnimation.Types.easeInEaseOut,
property: LayoutAnimation.Properties.scaleXY,
},
update: {
type: LayoutAnimation.Types.easeInEaseOut,
},
},
},
};
ОБНОВИТЬ:
См. Ответ @sherlock ниже, начиная с 0.11, изменение размера клавиатуры может быть решено с помощью встроенной функциональности.
Вы можете объединить несколько методов во что-то более простое.
Подключите слушатель onFocus к своим входам
<TextInput ref="password" secureTextEntry={true}
onFocus={this.scrolldown.bind(this,'password')}
/>
Наш метод прокрутки выглядит примерно так:
scrolldown(ref) {
const self = this;
this.refs[ref].measure((ox, oy, width, height, px, py) => {
self.refs.scrollView.scrollTo({y: oy - 200});
});
}
Это говорит нашему представлению прокрутки (не забудьте добавить ссылку), чтобы прокрутить вниз до позиции нашего сфокусированного ввода - 200 (это примерно размер клавиатуры)
componentWillMount() {
this.keyboardDidHideListener = Keyboard.addListener(
'keyboardWillHide',
this.keyboardDidHide.bind(this)
)
}
componentWillUnmount() {
this.keyboardDidHideListener.remove()
}
keyboardDidHide(e) {
this.refs.scrollView.scrollTo({y: 0});
}
Здесь мы сбрасываем наш вид прокрутки обратно наверх,
Я использую более простой метод, но он еще не анимирован. У меня есть состояние компонента, называемое "bumpedUp", которое по умолчанию равно 0, но устанавливается в 1, когда textInput получает фокус, например так:
На моем textInput:
onFocus={() => this.setState({bumpedUp: 1})}
onEndEditing={() => this.setState({bumpedUp: 0})}
У меня также есть стиль, который дает контейнеру обертки все на этом экране нижнее поле и отрицательное верхнее поле, например:
mythingscontainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
},
bumpedcontainer: {
marginBottom: 210,
marginTop: -210,
},
И затем на контейнере для упаковки я установил стили следующим образом:
<View style={[styles.mythingscontainer, this.state.bumpedUp && styles.bumpedcontainer]}>
Таким образом, когда состояние "bumpedUp" устанавливается равным 1, стиль bumpedcontainer включается и перемещает содержимое вверх.
Вроде хаки и поля жестко закодированы, но это работает:)
Я использую ответ Brysgo, чтобы поднять нижнюю часть моего прокрутки. Затем я использую OnScroll, чтобы обновить текущую позицию прокрутки. Затем я нашел этот React Native: получение позиции элемента, чтобы получить позицию ввода текста. Затем я делаю простую математику, чтобы выяснить, находится ли вход в текущем представлении. Затем я использую scrollTo для перемещения минимальной суммы плюс маржа. Это довольно гладко. Вот код для прокручиваемой части:
focusOn: function(target) {
return () => {
var handle = React.findNodeHandle(this.refs[target]);
UIManager.measureLayoutRelativeToParent( handle,
(e) => {console.error(e)},
(x,y,w,h) => {
var offs = this.scrollPosition + 250;
var subHeaderHeight = (Sizes.width > 320) ? Sizes.height * 0.067 : Sizes.height * 0.077;
var headerHeight = Sizes.height / 9;
var largeSpace = (Sizes.height - (subHeaderHeight + headerHeight));
var shortSpace = largeSpace - this.keyboardOffset;
if(y+h >= this.scrollPosition + shortSpace) {
this.refs.sv.scrollTo(y+h - shortSpace + 20);
}
if(y < this.scrollPosition) this.refs.sv.scrollTo(this.scrollPosition - (this.scrollPosition-y) - 20 );
}
);
};
},
Я тоже отвечаю на этот вопрос. Наконец, я разрешаю это, определяя высоту каждой сцены, например:
<Navigator
...
sceneStyle={{height: **}}
/>
И я также использую сторонний модуль https://github.com/jaysoo/react-native-extra-dimensions-android чтобы получить реальную высоту.