web-dev-qa-db-fra.com

créer un TextInput multi-lignes avec React-Native

Je travaille sur une application native native et j'ai besoin d'un objet TextInput doté d'une fonctionnalité similaire à celle de la vue texte de l'application "messages" sur iOS. Elle devrait commencer par une ligne, puis se développer gracieusement en plusieurs lignes jusqu'à ce que lignes de texte), puis commencez à faire défiler la dernière ligne si nécessaire.

J'ai jeté un œil à la variable SlackTextViewController mais a) semble contenir beaucoup de choses que je ne veux pas et b) j'aimerais essayer de garder autant de code dans React (et en dehors de son objectif-C/Swift) possible.

Edit: Je veux juste souligner que je préférerais le code REACT (JAVASCRIPT), comme indiqué ci-dessus, plutôt que Objective-C ou Swift.

24
Aaron Yodaiken

J'ai essayé deux façons différentes de le faire aujourd'hui. Ni sont les meilleurs, mais j'ai pensé enregistrer mes efforts au cas où ils seraient utiles. Ils ont tous les deux certainement eu l'effet que vous recherchiez, bien que parfois retardé par toute la communication asynchrone.

1) Hauteur du texte hors écran

Donc, juste sous l’entrée TextInput, j’ai ajouté un champ de texte standard avec la même police et le même remplissage, etc. J'ai enregistré l'auditeur onChange sur l'entrée et appelé setState({text: event.nativeEvent.text}). Le texte déposé tire sa valeur de l'État. Les deux avaient onLayout auditeurs. Fondamentalement, l'objectif était d'obtenir la hauteur de TextInput à partir du texte (non restreint). Puis j'ai caché la manière de texte hors écran

https://Gist.github.com/bleonard/f7d748e89ad2a485ec34

2) Module natif

Vraiment, j'avais juste besoin de la hauteur du contenu dans real UITextView. J'ai donc ajouté une catégorie à RCTUIManager car il existe déjà plusieurs méthodes utiles. Je me suis débarrassé de la vue masquée du texte. Alors onChange, je demande la hauteur et l’utilise de la même manière via l’État. 

https://Gist.github.com/bleonard/6770fbfe0394a34c864b

3) Github PR

Ce que j’espère vraiment, c’est que cette communication soit acceptée. Il semble faire quelque chose comme ça automatiquement.

https://github.com/facebook/react-native/pull/1229

19
bleonard

Ajouter multiline={true} à un TextInput permettra le défilement si la quantité de texte dépasse l’espace disponible. Vous pouvez ensuite changer la hauteur de TextInput en accédant à nativeEvent.contentSize.height de l'événement à partir de la propriété 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}
         />
      );
   }
}
11
Groovietunes

A partir du 17 octobre, un composant Nice de Wix a été créé:

https://github.com/wix/react-native-autogrow-textinput

L'utilisation peut être très simple:

<AutoGrowingTextInput
  style={styles.textInput}
  placeholder="Enter text"
  value={this.state.text}
  onChangeText={this._handleChangeText}
/>

Et il y a quelques accessoires supplémentaires comme minHeight et maxHeight par exemple.

Je l'utilise sur RN 0.47.2

1
zubko

Implémentez la méthode déléguée UITextViewtextViewDidChange et jouez avec le rect 

- (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;
}
1
Inder Kumar Rathore

Une autre solution consiste à vérifier les symboles '\n' et à définir la propriété numberOfLines . Fonctionne pour moi.

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,
};
0
Yuri Tinyukov