在input时调整包含UITextView的UITableViewCell

我有一个包含自定义单元格的UITableViewController。 每个单元格都使用一个笔尖创build,并包含一个不可滚动的UITextView。 我在每个单元格中添加了约束,以便单元格调整其高度以适应UITextView的内容。 所以最初我的控制器是这样的:

初始状态

现在我希望当用户在单元格中键入内容时,内容会自动适应。 这个问题已经被问了好多次了,特别是这个或者第二个答案 。 我已经在我的代码中写下了如下代表:

- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text { [self.tableView beginUpdates]; [self.tableView endUpdates]; return YES; } 

然而,它会导致以下奇怪的行为:所有的约束被忽略,所有单元格高度折叠到最小值。 看到下面的图片:

错误的行为

如果我向下滚动tableView为了强制一个新的cellForRowAtIndexPath调用,我恢复正确的高度单元格:

正确的行为

请注意,我没有实现heightForRowAtIndexPath,因为我希望autoLayout来照顾这个。

有人能告诉我我做错了什么,或者帮助我吗? 非常感谢你 !

这是一个很好的解决scheme。 假设您正在使用自动布局,则需要为estimatedRowHeight指定一个值,然后返回行高的UITableViewAutomaticDimension。 最后在文本视图委托中做类似下面的事情。

 override func viewDidLoad() { super.viewDidLoad() self.tableView.estimatedRowHeight = 44.0 } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return UITableViewAutomaticDimension } // MARK: UITextViewDelegate func textViewDidChange(textView: UITextView) { // Calculate if the text view will change height, then only force // the table to update if it does. Also disable animations to // prevent "jankiness". let startHeight = textView.frame.size.height let calcHeight = textView.sizeThatFits(textView.frame.size).height //iOS 8+ only if startHeight != calcHeight { UIView.setAnimationsEnabled(false) // Disable animations self.tableView.beginUpdates() self.tableView.endUpdates() // Might need to insert additional stuff here if scrolls // table in an unexpected way. This scrolls to the bottom // of the table. (Though you might need something more // complicated if editing in the middle.) let scrollTo = self.tableView.contentSize.height - self.tableView.frame.size.height self.tableView.setContentOffset(CGPointMake(0, scrollTo), animated: false) UIView.setAnimationsEnabled(true) // Re-enable animations. } 

以下示例适用于dynamic行高度,因为用户将文本input到单元格中。 即使您使用自动布局,您仍然必须实现heightForRowAtIndexPath方法。 对于这个例子来说,工作约束必须以这样的方式设置为textView:如果单元格高度增加,textView也将增加高度。 这可以通过从textView向单元格内容视图添加顶部约束和底部约束来实现。 但是不要为textView本身设置高度限制。 同时启用textView的滚动function,以便在用户input文本时更新textView的内容大小。 然后我们使用这个内容大小来计算新的行高。 只要行高足够长,可以将textView垂直拉伸到等于或大于其内容大小,即使启用了滚动,文本视图也不会滚动,而这正是您所需要的。

在这个例子中,我只有一行,我只使用一个variables来跟踪行高。 但是,当我们有多行时,我们需要每行的variables,否则所有的行将有相同的高度。 在这种情况下,可以使用对应于tableView数据源数组的rowHeight数组。

 @interface ViewController () @property (nonatomic, assign)CGFloat rowHeight;; @property (weak, nonatomic) IBOutlet UITableView *tableView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.rowHeight = 60; } #pragma mark - UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell1"]; return cell; } #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return self.rowHeight; } #pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { [self.tableView beginUpdates]; CGFloat paddingForTextView = 40; //Padding varies depending on your cell design self.rowHeight = textView.contentSize.height + paddingForTextView; [self.tableView endUpdates]; } @end 

使用Swift 2.2(早期的版本也可能工作),如果你设置TableView使用自动尺寸(假设你在一个子类UITableViewController ,像这样:

  self.tableView.rowHeight = UITableViewAutomaticDimension self.tableView.estimatedRowHeight = 50 // or something 

你只需要在这个文件, UITextViewDelegate实现委托,并添加下面的函数,它应该工作。 只要记住将textView的委托设置为self (所以,也许在您将单元出列后, cell.myTextView.delegate = self

 func textViewDidChange(textView: UITextView) { self.tableView.beginUpdates() textView.frame = CGRectMake(textView.frame.minX, textView.frame.minY, textView.frame.width, textView.contentSize.height + 40) self.tableView.endUpdates() } 

感谢“何塞托米约瑟夫”鼓舞(使能,真的)这个答案。

我已经实现了一个类似的方法使用UITextView但是为了做到这一点,我不得不实现heightForRowAtIndexPath

 #pragma mark - SizingCell - (USNTextViewTableViewCell *)sizingCell { if (!_sizingCell) { _sizingCell = [[USNTextViewTableViewCell alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.tableView.frame.size.width, 0.0f)]; } return _sizingCell; } #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { self.sizingCell.textView.text = self.profileUpdate.bio; [self.sizingCell setNeedsUpdateConstraints]; [self.sizingCell updateConstraintsIfNeeded]; [self.sizingCell setNeedsLayout]; [self.sizingCell layoutIfNeeded]; CGSize cellSize = [self.sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; return cellSize.height; } 

sizingCell是仅用于计算大小的单元格的实例。

需要注意的是,需要将UITextView的上下边缘附加到UITableViewCells contentView的上下边缘,以便随着UITableViewCell高度的变化,UITextView的高度也会发生变化。

对于约束布局,我使用了一个PureLayout( https://github.com/smileyborg/PureLayout ),所以下面的约束布局代码可能对您而言并不常见:

 #pragma mark - Init - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self.contentView addSubview:self.textView]; } return self; } #pragma mark - AutoLayout - (void)updateConstraints { [super updateConstraints]; /*-------------*/ [self.textView autoPinEdgeToSuperviewEdge:ALEdgeLeft withInset:10.0f]; [self.textView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:5.0f]; [self.textView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:5.0f]; [self.textView autoSetDimension:ALDimensionWidth toSize:200.0f]; } 

受到前面两个答案的启发,我find了解决问题的方法。 我认为我有一个UITextView的事实导致autoLayout一些麻烦。 我将以下两个函数添加到我的原始代码中。

 - (CGFloat)textViewHeightForAttributedText: (NSAttributedString*)text andWidth: (CGFloat)width { UITextView *calculationView = [[UITextView alloc] init]; [calculationView setAttributedText:text]; CGSize size = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)]; return size.height; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { UIFont *font = [UIFont systemFontOfSize:14.0]; NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:self.sampleStrings[indexPath.row] attributes:attrsDictionary]; return [self textViewHeightForAttributedText:attrString andWidth:CGRectGetWidth(self.tableView.bounds)-31]+20; } 

在最后一行31是我的约束的左侧和右侧的总和, 20是只是一些任意松懈。

我在阅读这个非常有趣的答案时发现了这个解决scheme。

我的解决scheme与@atlwx类似,但稍短。 用静态表进行testing。 UIView.setAnimationsEnabled(false)来防止单元格的内容“跳跃”,而表格更新单元格的高度

 override func viewDidLoad() { super.viewDidLoad() self.tableView.estimatedRowHeight = 44.0 } override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return UITableViewAutomaticDimension } func textViewDidChange(_ textView: UITextView) { UIView.setAnimationsEnabled(false) textView.sizeToFit() self.tableView.beginUpdates() self.tableView.endUpdates() UIView.setAnimationsEnabled(true) } 

在不closures键盘的情况下,以平滑方式立即更新tableview单元格高度的技巧是,在设置textView或单元格中其他内容的大小后,运行以下代码片段,以在textViewDidChange事件中调用:

 [tableView beginUpdates]; [tableView endUpdates]; 

然而,这可能是不够的。 你也应该确保tableView有足够的弹性来保持相同的contentOffset。 你通过设置tableView contentInset底部来获得弹性。 我build议这个弹性值至less是从最后一个单元格的底部到tableView底部所需要的最大距离。 例如,它可能是键盘的高度。

 self.tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0); 

有关此事件的更多详细信息和一些有用的额外function,请查看以下链接: resize并平滑移动UITableViewCell,而不必closures键盘

查看我在下面的链接中提供的Objective C解决scheme。 简单实施,干净,无需自动布局。 不需要约束。 testingiOS10和iOS11。

顺利调整UITableViewCell的大小并移动键盘

Interesting Posts