使用React-Native制作多行、扩展TextInput

问题描述 投票:0回答:9

我正在开发一个反应本机应用程序,需要一个与 iOS 上“消息”应用程序中的文本视图具有类似功能的 TextInput - 它应该从一行开始,然后优雅地扩展到更多行,直到达到某种限制(例如5 行文本),然后根据需要开始滚动到最新行。

看了一下

SlackTextViewController
,但是a)似乎它有很多我不想要的东西,b)我想尝试在React中保留尽可能多的代码(并且不在objective-C/迅速)尽可能。

编辑:只是想强调,如上所述,我更喜欢 REACT (JAVASCRIPT) 代码,而不是 Objective-C 或 Swift。

ios ios8 react-native
9个回答
21
投票

今天我尝试了两种不同的方法来做到这一点。两者都不是最好的,但我想我会记录我的努力,以防它们有帮助。它们肯定都达到了您正在寻找的效果,尽管有时会因所有异步通信而延迟。

1) 离屏文本高度

因此,在 TextInput 下,我添加了一个具有相同字体和填充等的常规文本字段。我在输入上注册了

onChange
侦听器并调用
setState({text: event.nativeEvent.text})
。文本归档的价值来自于国家。两者都有
onLayout
听众。基本上,目标是从(不受限制的)文本获取 TextInput 的高度。然后我将文本隐藏在屏幕之外

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

2) 本机模块

真的,我只需要 real UITextView 中内容的高度。因此,我向 RCTUIManager 添加了一个类别,因为那里已经有几种有用的方法。我摆脱了隐藏的文本视图。所以

onChange
,我询问高度并通过状态以相同的方式使用它。

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

3)Github 公关

我真正希望的是这个 PR 被接受。它看起来会自动执行类似的操作。

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


14
投票

multiline={true}
添加到 TextInput 将允许在文本量超出可用空间时滚动。然后,您可以通过从 onChange 属性访问事件的 nativeEvent.contentSize.height 来更改 TextInput 的高度。

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}
         />
      );
   }
}

2
投票

截至 2017 年 10 月,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 上使用它


2
投票

对于任何想要简单解决方案的人来说,使用新版本的 RN,目前使用的是 0.63.4, 如果 TextInput 是多行,团队将 autoGrow 添加到其中。

不要对组件或父视图/视图使用 height(StyeSheet 属性)

使用 minHeight, maxHeight 代替,如果启用多行,TextInput 将增长直至达到 maxHeight,然后将可滚动。

无需维护任何计算或内部状态。

如果您需要广泛扩展,同样适用于minWidthmaxWidth


1
投票

实现

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;
}

1
投票

@Groovietunes 几乎所有内容都是正确的,但截至 19 年 6 月 26 日,情况与他最初的答案相比发生了一些变化。

首先,textinputs 上的

onChange
属性不再返回
contentSize
对象,因此
event.nativeEvent.contentSize
将返回
undefined

解决此问题的方法是使用

onContentSizeChange
属性。但有一个问题;
onContentSizeChange
仅在组件安装时运行一次,因此无法在高度变化时动态获取高度。

要解决此问题,我们需要确保在属性层次结构中将

value
属性设置在
onContentSizeChange
之后,以便随着
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}
      />

如果由于某种原因在查看此答案时似乎已损坏,请参阅this文档以获取任何更新


0
投票

另一个解决方案是检查

'\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,
};

0
投票

由于

react-native-autogrow-textinput
似乎没有维护,我建议使用 native-base
<Input/>
,因为它具有启用多行和自动高度的属性,如下所示:

import { Input } from 'native-base'

<Input
    height={'auto'}
    multiline={true}
    // ...
/>

0
投票

在这里我找到了可以像聊天文本输入一样设置最大和最小高度的代码。 检查并让我知道同样的情况。 100% 工作无任何故障。

import { useState } from 'react';
import { TextInput, ScrollView, StyleSheet } from 'react-native';

const GrowingTextInput = ({ style }) => {

  const [inputValue, setInputValue] = useState('');
  const minHeight = 40;
  const maxHeight = 120;

  const calculateInputHeight = contentHeight => {
    const height = Math.min(maxHeight, Math.max(minHeight, contentHeight));
    return height;
  };

  const [inputHeight, setInputHeight] = useState(minHeight);

  return (
    <ScrollView style={styles.container}>
      <TextInput
        multiline
        value={inputValue}
        onChangeText={setInputValue}
        onContentSizeChange={event => {
          const height = calculateInputHeight(event.nativeEvent.contentSize.height);
          setInputHeight(height);
        }}
        style={[styles.input, { height: inputHeight }, style]}
        placeholder="Type here..."
      />
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  input: {
    borderColor: 'gray',
    borderWidth: 1,
    fontSize: 16,
    padding: 10,
  },
  container: {
    flex: 1,
    padding: 20,
  },
});

export default GrowingTextInput;

© www.soinside.com 2019 - 2024. All rights reserved.