使用 React-Native 制作多行扩展 TextInput
Posted
技术标签:
【中文标题】使用 React-Native 制作多行扩展 TextInput【英文标题】:making a multiline, expanding TextInput with React-Native 【发布时间】:2015-07-17 11:50:28 【问题描述】:我正在开发一个 react-native 应用程序,需要一个 TextInput,它与 ios 上的“消息”应用程序中的 textview 具有相似的功能——它应该从一行开始,然后优雅地扩展到更多行,直到达到某个限制(如 5 行文本),然后根据需要开始滚动到最新行。
查看了SlackTextViewController
,但 a) 似乎它有很多我不想要的东西,并且 b) 我想尝试在 React 中保留尽可能多的代码(并且出于目标 - C/swift) 尽可能。
编辑:只是想强调一下,如上所述,我更喜欢 REACT (JAVASCRIPT) 代码,而不是 Objective-C 或 Swift。
【问题讨论】:
看看这个库,它已经有一段时间没有更新了,但它运行得很好。 github.com/HansPinckaers/GrowingTextView 【参考方案1】:我今天尝试了两种不同的方法。两者都不是最好的,但我想我会记录我的努力以防万一。他们都肯定有你正在寻找的效果,尽管有时会因所有异步通信而延迟。
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
【讨论】:
你能把这些要点公开而不是秘密吗?在您的 Gist 个人资料中找不到它们 :) 更新:已添加对自动调整文本字段大小的支持github.com/facebook/react-native/commit/…【参考方案2】:如果文本量超过可用空间,将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
/>
);
【讨论】:
【参考方案3】:截至 17 年 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 上使用它
【讨论】:
今天仍在使用 "react-native": "0.62.2", "react-native-autogrow-input": "0.2.1", 【参考方案4】:实现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;
【讨论】:
【参考方案5】:@Groovietunes 几乎所有内容都正确,但截至 2019 年 6 月 26 日,情况与他最初的回答有所不同。
一方面,textinputs 上的 onChange
属性不再返回 contentSize
对象,因此 event.nativeEvent.contentSize
将返回 undefined
。
解决此问题的方法是使用 onContentSizeChange
属性。但是有一个问题。 onContentSizeChange
仅在组件安装时运行一次,因此无法在组件更改时动态获取高度。
要解决这个问题,我们需要确保 value
在 props 层次结构中设置在 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 文档以获取任何更新
【讨论】:
【参考方案6】:另一种解决方案是检查'\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,
;
【讨论】:
【参考方案7】:对于任何想要简单解决方案的人,使用新版本的 RN,目前使用 0.63.4, 如果 TextInput 是多行的,则团队会将 autoGrow 添加到 TextInput,就是这种情况。
不要为组件或父视图/视图使用 height(StyeSheet 属性)
改用minHeight、maxHeight,如果启用多行,TextInput会一直增长到maxHeight,然后就可以滚动了。
无需维护任何计算或内部状态。
minWidth、maxWidth同样适用,如果您需要大幅扩展。
【讨论】:
以上是关于使用 React-Native 制作多行扩展 TextInput的主要内容,如果未能解决你的问题,请参考以下文章
如何在react-native中的多行文本中断后确定“更多”链接/按钮的位置
如何通过扩展 mx.containers.Panel 在 flex 3 中创建具有多行标题的面板?