Создание многострочного расширяющегося TextInput с React-Native
Я работаю над адаптивным приложением и нуждаюсь в TextInput, который имеет аналогичную функциональность для textview в приложении "Сообщения" на iOS - он должен начинаться как одна строка, а затем изящно расширяться до большего количества строк до определенного предела (например, 5 строк текста), а затем при необходимости прокрутите до последней строки.
Посмотрел на SlackTextViewController
, но a) кажется, что у него много чего не нужно, и б) я хотел бы попытаться сохранить столько же кода в React (и из objective-C/swift).
Изменить: просто хочу подчеркнуть, что я бы предпочел код REACT (JAVASCRIPT), как указано выше, а не Objective-C или Swift.
Ответы
Ответ 1
Я попробовал два разных способа сделать это сегодня. Ни один из них не лучший, но я думал, что заработаю свои усилия, если они будут полезны. У них обоих определенно был эффект, который вы ищете, хотя иногда задерживается со всей асинхронной связью.
1) Высота экрана вне экрана
Итак, просто под TextInput я добавил регулярное текстовое поле с тем же шрифтом и дополнением и т.д. Я зарегистрировал прослушиватель onChange
на входе и назвал setState({text: event.nativeEvent.text})
. Полученный текст получил свое значение из состояния. У обоих было onLayout
Listeners. В основном цель заключалась в том, чтобы получить высоту для TextInput из (неограниченного) текста. Затем я спрятал Text way вне экрана
https://gist.github.com/bleonard/f7d748e89ad2a485ec34
2) Нативный модуль
Действительно, мне просто нужна высота содержимого в реальном UITextView. Поэтому я добавил категорию в RCTUIManager, поскольку есть несколько методов, которые уже полезны. Я избавился от скрытого текстового вида. Итак, onChange
, я прошу высоты и использую это так же через состояние.
https://gist.github.com/bleonard/6770fbfe0394a34c864b
3) Github PR
Я действительно надеюсь, что этот PR будет принят. Он пытается сделать что-то подобное автоматически.
https://github.com/facebook/react-native/pull/1229
Ответ 2
Добавление multiline={true}
в TextInput позволит прокручивать, если количество текста превышает доступное пространство. Затем вы можете изменить высоту TextInput, обратившись к атрибуту nativeEvent.contentSize.height события из команды onChange.
class Comment extends Component {
state = {
text: '',
height: 25
}
onTextChange(event) {
const { contentSize, text } = event.nativeEvent;
this.setState({
text: text,
height: contentSize.height > 100 ? 100 : contentSize.height
});
}
render() {
return (
<TextInput
multiline
style={{ height: this.state.height }}
onChange={this.onTextChange.bind(this)}
value={this.state.text}
/>
);
}
}
Ответ 3
Внедрите метод делегата UITextView
textViewDidChange
и играйте с прямым
- (void)textViewDidChange:(UITextView *)textView {
CGSize constraintSize = CGSizeMake(textView.frame.size.width, MAXFLOAT);
CGRect textRect = [textView.text boundingRectWithSize:constraintSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:textView.font}
context:nil];
NSLog(@"Frame:%@", NSStringFromCGRect(textRect));
CGRect newRect = textView.frame;
newRect.size.height = textRect.size.height;
textView.frame = newRect;
}
Ответ 4
По состоянию на октябрь 17 для этого есть приятный компонент из Wix:
https://github.com/wix/react-native-autogrow-textinput
Использование может быть очень простым:
<AutoGrowingTextInput
style={styles.textInput}
placeholder="Enter text"
value={this.state.text}
onChangeText={this._handleChangeText}
/>
И есть несколько дополнительных реквизитов, например minHeight
и maxHeight
.
Я использую его в RN 0.47.2
Ответ 5
Другое решение - проверить символы '\n'
и установить свойство numberOfLines
. Работает для меня.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {TextInput} from 'react-native';
export default class TextInputAutogrow extends Component {
constructor(props) {
super(props);
this._ref = null;
this.bindRef = this.bindRef.bind(this);
this.onChangeText = this.onChangeText.bind(this);
this.state = {
numberOfLines: this.getNumberOfLines()
};
}
bindRef(c) {
this._ref = c;
this.props.innerRef && this.props.innerRef(c);
}
getText() {
return typeof this.props.value === 'string' ?
this.props.value :
(
typeof this.props.defaultValue === 'string' ?
this.props.defaultValue :
''
);
}
getNumberOfLines(value) {
if (value === undefined) {
value = this.getText();
}
return Math.max(this.props.numberOfLines, value.split('\n').length - 1) + 1;
}
onChangeText(value) {
this.setState({numberOfLines: this.getNumberOfLines(value)})
}
render() {
return (
<TextInput
{...this.props}
ref={this.bindRef}
numberOfLines={this.state.numberOfLines}
onChangeText={this.onChangeText}
/>
)
}
}
TextInputAutogrow.propTypes = {
...TextInput.propTypes,
innerRef: PropTypes.func,
};
TextInputAutogrow.defaultProps = {
numberOfLines: 4,
};
Ответ 6
@Groovietunes понял почти все правильно, но по состоянию на 26-06-19 ситуация немного изменилась по сравнению с его первоначальным ответом.
С одной стороны, onChange
для textinputs больше не возвращает объект contentSize
, поэтому event.nativeEvent.contentSize
вернет undefined
.
Исправление для этого заключается в использовании onContentSizeChange
prop. Но есть подвох; onContentSizeChange
запускается только один раз, когда компонент монтируется, что делает невозможным динамическое получение высоты при его изменении.
Чтобы это исправить, нам нужно убедиться, что value
prop установлено после onContentSizeChange
в иерархии реквизитов, чтобы продолжать получать размер контента по textinput
роста textinput
.
В конце всего этого у меня был компонент, который был похож на это
<TextInput
placeholder={this.props.placeholder}
autoFocus={this.props.autoFocus}
style={[
styles.inputFlat,
{ height: this.state.height > 40 ? this.state.height : 40 }
]}
ref="inputFlat"
onFocus={() => {
this.toggleInput();
this.toggleButton();
}}
multiline={this.props.multiline}
onBlur={() => {
this.toggleInput();
this.toggleButton();
}}
onChangeText={this.props.onChangeText}
onContentSizeChange={event => {
const { contentSize } = event.nativeEvent;
this.setState({
height: contentSize.height
});
}}
value={this.state.value}
/>
если по какой-либо причине он кажется неисправным на момент просмотра этого ответа, обратитесь к этой документации для получения обновлений.