Подтвердить что ты не робот

Создание многострочного расширяющегося TextInput с React-Native

Я работаю над адаптивным приложением и нуждаюсь в TextInput, который имеет аналогичную функциональность для textview в приложении "Сообщения" на iOS - он должен начинаться как одна строка, а затем изящно расширяться до большего количества строк до определенного предела (например, 5 строк текста), а затем при необходимости прокрутите до последней строки.

Посмотрел на SlackTextViewController, но a) кажется, что у него много чего не нужно, и б) я хотел бы попытаться сохранить столько же кода в React (и из objective-C/swift).

Изменить: просто хочу подчеркнуть, что я бы предпочел код REACT (JAVASCRIPT), как указано выше, а не Objective-C или Swift.

4b9b3361

Ответ 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}
      />

если по какой-либо причине он кажется неисправным на момент просмотра этого ответа, обратитесь к этой документации для получения обновлений.