调整大小的UITextView

28

我在我的 UIView 上添加了一个不可编辑的 UITextView,用于显示一些数据。在文本视图中显示的数据是动态的,因此行数是不固定的,可能会变化。所以如果行数增加,文本视图的大小也需要增加。我不知道该如何实现这一点,请给我一些想法。

更新:

这是我正在做的:

UIView *baseView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
baseView.backgroundColor = [UIColor grayColor];
[window addSubview:baseView];

UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, 30)];
textView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
textView.text = @"asdf askjalskjalksjlakjslkasj";
[textView sizeToFit];
[baseView addSubview:textView];

可能是重复的问题:如何根据内容调整UITextView的大小? - Dave Haigh
10个回答

58

这里有一个答案发布在如何根据UITextView的内容调整其大小?

CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

或者更好(考虑到contentInset,感谢kpower的评论)

CGRect frame = _textView.frame;
UIEdgeInsets inset = textView.contentInset;
frame.size.height = _textView.contentSize.height + inset.top + inset.bottom;
_textView.frame = frame;

注意:如果你要多次引用对象的属性(例如 frame 或 contentInset),最好将其赋值给一个本地变量,这样就不会触发额外的方法调用(_textView.frame/[_textView frame] 都是方法调用)。如果你需要频繁调用此代码(10万次以上),那么这种做法会明显变慢(几十个方法调用是微不足道的)。

然而...如果你想在一行代码中完成而不使用额外的变量,可以这样写:

_textView.frame = CGRectMake(_textView.frame.origin.x, _textView.frame.origin.y, _textView.frame.size.width, _textView.contentSize.height + _textView.contentInset.top + _textView.contentInset.bottom);

以牺牲额外的5个方法调用为代价。


7
frame.size.height = _textView.contentInset.top + _textView.contentInset.bottom + _textView.contentSize.height; - kpower
[textView setFrame: CGRectMake( [textView frame].origin.x, [textView frame].origin.y, [textView contentSize].width, [textView contentSize].height)]; [textView setFrame: CGRectMake([textView frame].origin.x, [textView frame].origin.y, [textView contentSize].width, [textView contentSize].height)]; - folex
当内容为HTML格式时,有没有办法实现这个? - Rotten
更好的答案,没有常量,没有脏代码,性能更好。 - txulu
但请参见下面的答案。 - Chris Prince

30

您可以使用 setFrame:sizeToFit

更新:

我在 UILabel 中使用 sizeToFit,它很好用,但是 UITextViewUIScrollView 的子类,所以我能理解为什么 sizeToFit 不能产生期望的结果。

您仍然可以计算文本高度并使用 setFrame,但如果文本太长,可能需要利用 UITextView 的滚动条。

以下是获取文本高度的方法:

#define MAX_HEIGHT 2000

NSString *foo = @"Lorem ipsum dolor sit amet.";
CGSize size = [foo sizeWithFont:[UIFont systemFontOfSize:14]
              constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
                  lineBreakMode:UILineBreakModeWordWrap];

然后您可以将此与您的UITextView一起使用:

[textView setFont:[UIFont systemFontOfSize:14]];
[textView setFrame:CGRectMake(5, 30, 100, size.height + 10)];

或者您可以先进行高度计算,避免使用setFrame行:

UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(5, 30, 100, size.height + 10)];

这个代码运行良好。但是需要最初设置框架。也就是在你的代码片段之前,我添加了textView = [[UITextView alloc] initWithFrame:CGRectMake(5.0, 30.0, 100, 30)];。 - saikamesh
添加上述内容后,只有TextView变为可见状态。而且TextView中的最后一行没有完全显示出来,只有上半部分被显示出来了。 - saikamesh
这是因为UITextView本身比文本要大。只需将size.height替换为size.height + 10或其他数值即可。 - Can Berk Güder
好的,你必须将textView初始化为某些内容。但是你可以先进行高度计算并且去掉setFrame这一行。 - Can Berk Güder
3
正确答案在下面,由Gabe提供。它不依赖于神奇的+10并且更准确。 - TomSwift
显示剩余4条评论

4

sizeToFit可以起作用

如果在第一次设置文本后调用sizeToFit,则会调整大小。因此,在第一次设置后,即使调用sizeToFit,对setText的后续调用也不会导致大小改变。

但是,您可以通过以下方式强制调整大小:

  1. 设置文本。
  2. 将textView框架高度更改为CGFLOAT_MAX。
  3. 调用sizeToFit。

3

textView.contentSize.heighttextViewDidChange 中只能在文本实际增长后才能调整大小。为了获得最佳的视觉效果,最好事先调整大小。经过几个小时的研究,我已经找到了如何完美地使其与 Instagram 相同(顺便说一句,它是所有应用中算法最好的)

使用以下代码进行初始化:

    // Input
    _inputBackgroundView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, size.height - _InputBarHeight, size.width, _InputBarHeight)];
    _inputBackgroundView.autoresizingMask = UIViewAutoresizingNone;
   _inputBackgroundView.contentMode = UIViewContentModeScaleToFill;
    _inputBackgroundView.userInteractionEnabled = YES;
    [self addSubview:_inputBackgroundView];
   [_inputBackgroundView release];

   [_inputBackgroundView setImage:[[UIImage imageNamed:@"Footer_BG.png"] stretchableImageWithLeftCapWidth:80 topCapHeight:25]];

    // Text field
    _textField = [[UITextView alloc] initWithFrame:CGRectMake(70.0f, 0, 185, 0)];
   _textField.backgroundColor = [UIColor clearColor];
    _textField.delegate = self;
   _textField.contentInset = UIEdgeInsetsMake(-4, -2, -4, 0);
   _textField.showsVerticalScrollIndicator = NO;
   _textField.showsHorizontalScrollIndicator = NO;
    _textField.font = [UIFont systemFontOfSize:15.0f];
    [_inputBackgroundView addSubview:_textField];
   [_textField release];

   [self adjustTextInputHeightForText:@""];

填写UITextView委托方法:

- (void) textViewDidBeginEditing:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

- (void) textViewDidEndEditing:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

- (BOOL) textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {

   if ([text isEqualToString:@"\n"])
   {
      [self performSelector:@selector(inputComplete:) withObject:nil afterDelay:.1];
      return NO;
   }
   else if (text.length > 0)
   {
      [self adjustTextInputHeightForText:[NSString stringWithFormat:@"%@%@", _textField.text, text]];
   }
   return YES;
}

- (void) textViewDidChange:(UITextView*)textView {

   [self adjustTextInputHeightForText:_textField.text];
}

而诀窍就在于...
- (void) adjustTextInputHeightForText:(NSString*)text {

   int h1 = [text sizeWithFont:_textField.font].height;
   int h2 = [text sizeWithFont:_textField.font constrainedToSize:CGSizeMake(_textField.frame.size.width - 16, 170.0f) lineBreakMode:UILineBreakModeWordWrap].height;

   [UIView animateWithDuration:.1f animations:^
   {
      if (h2 == h1)
      {
         _inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - _InputBarHeight, self.frame.size.width, _InputBarHeight);
      }
      else
      {
         CGSize size = CGSizeMake(_textField.frame.size.width, h2 + 24);
         _inputBackgroundView.frame = CGRectMake(0.0f, self.frame.size.height - size.height, self.frame.size.width, size.height);
      }
      CGRect r = _textField.frame;
      r.origin.y = 12;
      r.size.height = _inputBackgroundView.frame.size.height - 18;
      _textField.frame = r;

   } completion:^(BOOL finished)
   {
      //
   }];
}

按下回车键时,字段不会增长。自动更正提示显示在键盘下方。不支持横屏模式。 - Kirby Todd

2
这对我来说完美地运作:
#define MAX_HEIGHT 2000     
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:14]
      constrainedToSize:CGSizeMake(100, MAX_HEIGHT)
          lineBreakMode:UILineBreakModeWordWrap];

[textview setFont:[UIFont systemFontOfSize:14]];
[textview setFrame:CGRectMake(45, 6, 100, size.height + 10)];
textview.text = text;

1
做以下操作:
_textView.text = someText;
[_textView sizeToFit];
_textView.frame.height = _textView.contentSize.height;

1

@Gabe提供的答案在iOS7.1上似乎直到viewDidAppear之后才能正常工作。请看下面我的测试结果。

更新:实际情况更加复杂。如果在resizeTheTextView方法中分配textView.text,在iOS7中,调整大小只允许单行文本。非常奇怪。

更新2:另请参阅iOS7中UITextView内容大小不同

更新3:请看我最后使用的代码。似乎可以完成工作。

#import "ViewController.h"

@interface ViewController ()
{
    UITextView *textView;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    textView = [[UITextView alloc] initWithFrame:CGRectMake(50, 50, 200, 1)];
    [self.view addSubview:textView];
    CALayer *layer = textView.layer;
    layer.borderColor = [UIColor blackColor].CGColor;
    layer.borderWidth = 1;

    textView.text = @"hello world\n\n";

    // Calling the method directly, after the view is rendered, i.e., after viewDidAppear, works on both iOS6.1 and iOS7.1
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:@"Change size" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(resizeTheTextView) forControlEvents:UIControlEventTouchUpInside];
    [button sizeToFit];
    CGRect frame = button.frame;
    frame.origin.y = 400;
    button.frame = frame;
    [self.view addSubview:button];

    // Works on iOS6.1, but does not work on iOS7.1
    //[self resizeTheTextView];
}

- (void) viewWillAppear:(BOOL)animated
{
    // Does not work on iOS7.1, but does work on iOS6.1
    //[self resizeTheTextView];
}

- (void) viewDidAppear:(BOOL)animated
{
    // Does work on iOS6.1 and iOS7.1
    //[self resizeTheTextView];
}

- (void) resizeTheTextView
{
    NSLog(@"textView.frame.size.height: %f", textView.frame.size.height);

    NSLog(@"textView.contentSize.height: %f", textView.contentSize.height);

    // 5) From https://dev59.com/3nRB5IYBdhLWcg3wET1J
    CGRect frame = textView.frame;
    UIEdgeInsets inset = textView.contentInset;
    frame.size.height = textView.contentSize.height + inset.top + inset.bottom;
    textView.frame = frame;

    NSLog(@"inset.top: %f, inset.bottom: %f",  inset.top,  inset.bottom);

    NSLog(@"textView.frame.size.height: %f", textView.frame.size.height);

    NSLog(@"textView.contentSize.height: %f", textView.contentSize.height);
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

更新3:
if ([[UIDevice currentDevice] majorVersionNumber] < 7.0) {
    CGRect frame = _abstractTextView.frame;
    UIEdgeInsets inset = _abstractTextView.contentInset;
    frame.size.height = _abstractTextView.contentSize.height + inset.top + inset.bottom;
    _abstractTextView.frame = frame;
}
else {
    CGSize textViewSize = [_abstractTextView sizeThatFits:CGSizeMake(_abstractTextView.frame.size.width, FLT_MAX)];
    _abstractTextView.frameHeight = textViewSize.height;
}

1

针对类似问题,我创建了一个基于自动布局的轻量级UITextView子类,它可以根据用户输入的大小自动增长和缩小,并且可以通过最大和最小高度进行约束 - 所有这些都不需要一行代码。

https://github.com/MatejBalantic/MBAutoGrowingTextView


1

只需将 scrollEnabled 设置为 NO,或在 IB 中的滚动视图部分取消选中 Scrolling EnabledUITextView 将自适应大小。


为什么这不是被接受的答案? - l3bel

1

在将UITextView添加到其父视图后,如果您对其进行内容模式设置,则它似乎会自动调整大小。

这意味着您不需要手动计算高度并应用高度约束。它似乎只是起作用!!在iPad上测试了iOS7和iOS8。

例如:

--
textView.contentMode = UIViewContentMode.Center;
--

如果有人能解释为什么这个有效,那将非常感激。我在界面构建器中尝试选项时意外发现了它。

@Martijn Pieters 我已经将这个问题标记为重复。如果这是规则的话,您应该取消删除另一个问题中的答案并删除这个问题的答案。 - Dave Haigh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接