ios 如何根据其内容调整 UITextView 的大小?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50467/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:16:07  来源:igfitidea点击:

How do I size a UITextView to its content?

ioscocoa-touchautolayoutuikituitextview

提问by drewh

Is there a good way to adjust the size of a UITextViewto conform to its content? Say for instance I have a UITextViewthat contains one line of text:

有没有一种好的方法可以调整 a 的大小UITextView以符合其内容?比如说我有一个UITextView包含一行文本的内容:

"Hello world"

I then add another line of text:

然后我添加另一行文本:

"Goodbye world"

Is there a good way in Cocoa Touch to get the rectthat will hold all of the lines in the text view so that I can adjust the parent view accordingly?

Cocoa Touch 中是否有一个很好的方法来获取rect文本视图中的所有行,以便我可以相应地调整父视图?

As another example, look at the notes' field for events in the Calendar application - note how the cell (and the UITextViewit contains) expands to hold all lines of text in the notes' string.

作为另一个示例,查看日历应用程序中事件的备注字段 - 注意单元格(及其UITextView包含的单元格)如何扩展以容纳备注字符串中的所有文本行。

回答by jhibberd

This works for both iOS 6.1 and iOS 7:

这适用于 iOS 6.1 和 iOS 7:

- (void)textViewDidChange:(UITextView *)textView
{
    CGFloat fixedWidth = textView.frame.size.width;
    CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
    CGRect newFrame = textView.frame;
    newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
    textView.frame = newFrame;
}

Or in Swift (Works with Swift 4.1 in iOS 11)

或者在 Swift 中(适用于 iOS 11 中的 Swift 4.1)

let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)

If you want support for iOS 6.1 then you should also:

如果您想要支持 iOS 6.1,那么您还应该:

textview.scrollEnabled = NO;

回答by Ronnie Liew

This no longer works on iOS 7 or above

这不再适用于 iOS 7 或更高版本

There is actually a very easy way to do resizing of the UITextViewto its correct height of the content. It can be done using the UITextViewcontentSize.

实际上有一种非常简单的方法可以将 调整为UITextView正确的内容高度。可以使用UITextViewcontentSize.

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

One thing to note is that the correct contentSizeis only available afterthe UITextViewhas been added to the view with addSubview. Prior to that it is equal to frame.size

有一点要注意的是,正确的contentSize是唯一可用UITextView已加入的观点addSubview。在此之前它等于frame.size

This will not work if auto layout is ON. With auto layout, the general approach is to use the sizeThatFitsmethod and update the constantvalue on a height constraint.

如果自动布局打开,这将不起作用。使用自动布局,一般方法是使用该sizeThatFits方法并更新constant高度约束上的值。

CGSize sizeThatShouldFitTheContent = [_textView sizeThatFits:_textView.frame.size];
heightConstraint.constant = sizeThatShouldFitTheContent.height;

heightConstraintis a layout constraint that you typically setup via a IBOutlet by linking the property to the height constraint created in a storyboard.

heightConstraint是一个布局约束,您通常通过 IBOutlet 通过将属性链接到故事板中创建的高度约束来设置。



Just to add to this amazing answer, 2014, if you:

只是为了补充这个惊人的答案,2014,如果你:

[self.textView sizeToFit];

there is a difference in behaviour with the iPhone6+only:

iPhone6+ 的行为有所不同:

enter image description here

在此处输入图片说明

With the 6+ only (not the 5s or 6) it does add "one more blank line" to the UITextView. The "RL solution" fixes this perfectly:

仅使用 6+(不是 5s 或 6),它确实向 UITextView 添加了“多一个空行”。“RL 解决方案”完美地解决了这个问题:

CGRect _f = self.mainPostText.frame;
_f.size.height = self.mainPostText.contentSize.height;
self.mainPostText.frame = _f;

It fixes the "extra line" problem on 6+.

它修复了 6+ 上的“额外线路”问题。

回答by Brett Donald

To make a dynamically sizing UITextViewinside a UITableViewCell, I found the following combination works in Xcode 6 with the iOS 8 SDK:

为了使动态上浆UITextView里面UITableViewCell,我发现在Xcode 6以下的组合可与iOS的8 SDK:

  • Add a UITextViewto a UITableViewCelland constrain it to the sides
  • Set the UITextView's scrollEnabledproperty to NO. With scrolling enabled, the frame of the UITextViewis independent of its content size, but with scrolling disabled, there is a relationship between the two.
  • If your table is using the original default row height of 44 then it will automatically calculate row heights, but if you changed the default row height to something else, you may need to manually switch on auto-calculation of row heights in viewDidLoad:

    tableView.estimatedRowHeight = 150;
    tableView.rowHeight = UITableViewAutomaticDimension;
    
  • 将 a 添加UITextView到 aUITableViewCell并将其约束到两侧
  • UITextViewscrollEnabled属性设置为NO. 启用滚动时, 的框架UITextView与其内容大小无关,但禁用滚动时,两者之间存在关系。
  • 如果您的表格使用原始默认行高 44,那么它会自动计算行高,但如果您将默认行高更改为其他内容,则可能需要手动打开行高的自动计算viewDidLoad

    tableView.estimatedRowHeight = 150;
    tableView.rowHeight = UITableViewAutomaticDimension;
    

For read-only dynamically sizing UITextViews, that's it. If you're allowing users to edit the text in your UITextView, you also need to:

对于只读动态调整大小UITextView,就是这样。如果您允许用户编辑 中的文本UITextView,您还需要:

  • Implement the textViewDidChange:method of the UITextViewDelegateprotocol, and tell the tableViewto repaint itself every time the text is edited:

    - (void)textViewDidChange:(UITextView *)textView;
    {
        [tableView beginUpdates];
        [tableView endUpdates];
    }
    
  • And don't forget to set the UITextViewdelegate somewhere, either in Storyboardor in tableView:cellForRowAtIndexPath:

  • 实现协议的textViewDidChange:方法UITextViewDelegate,并告诉tableView每次编辑文本时重新绘制自己:

    - (void)textViewDidChange:(UITextView *)textView;
    {
        [tableView beginUpdates];
        [tableView endUpdates];
    }
    
  • 并且不要忘记将UITextView委托设置在某处,无论是 inStoryboard还是 intableView:cellForRowAtIndexPath:

回答by Alok

Very easy working solution using code and storyboard both.

使用代码和故事板的非常简单的工作解决方案。

By Code

按代码

textView.scrollEnabled = false

By Storyboard

按故事板

Uncheck the Scrolling Enable

取消选中滚动启用

enter image description here

在此处输入图片说明

No need to do anything apart of this.

除此之外无需做任何事情。

回答by Juan Boero

Swift :

斯威夫特:

textView.sizeToFit()

回答by user63934

In my (limited) experience,

在我(有限的)经验中,

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

does not respect newline characters, so you can end up with a lot shorter CGSizethan is actually required.

不尊重换行符,因此最终可能CGSize比实际需要的要短得多。

- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

does seem to respect the newlines.

似乎确实尊重换行符。

Also, the text isn't actually rendered at the top of the UITextView. In my code, I set the new height of the UITextViewto be 24 pixels larger than the height returned by the sizeOfFontmethods.

此外,文本实际上并未呈现在UITextView. 在我的代码中,我将 的新高度设置为UITextViewsizeOfFont方法返回的高度大 24 像素。

回答by phatmann

In iOS6, you can check the contentSizeproperty of UITextView right after you set the text. In iOS7, this will no longer work. If you want to restore this behavior for iOS7, place the following code in a subclass of UITextView.

在 iOS6 中,您可以contentSize在设置文本后立即检查UITextView的属性。在 iOS7 中,这将不再起作用。如果要为 iOS7 恢复此行为,请将以下代码放在 UITextView 的子类中。

- (void)setText:(NSString *)text
{
    [super setText:text];

    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
        CGRect rect = [self.textContainer.layoutManager usedRectForTextContainer:self.textContainer];
        UIEdgeInsets inset = self.textContainerInset;
        self.contentSize = UIEdgeInsetsInsetRect(rect, inset).size;
    }
}

回答by Nikita Took

I will post right solution at the bottom of the page in case someone is brave (or despaired enough) to read to this point.

我会在页面底部发布正确的解决方案,以防有人勇敢(或足够绝望)读到这里。

Here is gitHub repo for those, who don't want to read all that text: resizableTextView

这是 gitHub 存储库,适用于不想阅读所有文本的人:resizableTextView

This works with iOs7 (and I do believe it will work with iOs8) and with autolayout. You don't need magic numbers, disable layout and stuff like that.Short and elegant solution.

这适用于 iOs7(我相信它适用于 iOs8)和自动布局。你不需要幻数,禁用布局和类似的东西。简短而优雅的解决方案。

I think, that all constraint-related code should go to updateConstraintsmethod. So, let's make our own ResizableTextView.

我认为,所有与约束相关的代码都应该转到updateConstraints方法。所以,让我们自己制作ResizableTextView.

The first problem we meet here is that don't know real content size before viewDidLoadmethod. We can take long and buggy road and calculate it based on font size, line breaks, etc. But we need robust solution, so we'll do:

我们在这里遇到的第一个问题是在viewDidLoad方法之前不知道实际内容大小。我们可以走很长的路,并根据字体大小、换行符等来计算它。但是我们需要强大的解决方案,所以我们会这样做:

CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

So now we know real contentSize no matter where we are: before or after viewDidLoad. Now add height constraint on textView (via storyboard or code, no matter how). We'll adjust that value with our contentSize.height:

所以现在我们知道真正的 contentSize 无论我们在哪里: before 或 after viewDidLoad。现在在 textView 上添加高度约束(通过故事板或代码,无论如何)。我们将使用我们的 来调整该值contentSize.height

[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
    if (constraint.firstAttribute == NSLayoutAttributeHeight) {
        constraint.constant = contentSize.height;
        *stop = YES;
    }
}];

The last thing to do is to tell superclass to updateConstraints.

最后要做的是告诉超类到updateConstraints.

[super updateConstraints];

Now our class looks like:

现在我们的类看起来像:

ResizableTextView.m

可调整大小的TextView.m

- (void) updateConstraints {
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        if (constraint.firstAttribute == NSLayoutAttributeHeight) {
            constraint.constant = contentSize.height;
            *stop = YES;
        }
    }];

    [super updateConstraints];
}

Pretty and clean, right? And you don't have to deal with that code in your controllers!

漂亮干净,对吧?而且您不必在控制器中处理该代码!

But wait!Y NO ANIMATION!

可是等等!Y 没有动画!

You can easily animate changes to make textViewstretch smoothly. Here is an example:

您可以轻松地为更改设置动画以textView平滑拉伸。下面是一个例子:

    [self.view layoutIfNeeded];
    // do your own text change here.
    self.infoTextView.text = [NSString stringWithFormat:@"%@, %@", self.infoTextView.text, self.infoTextView.text];
    [self.infoTextView setNeedsUpdateConstraints];
    [self.infoTextView updateConstraintsIfNeeded];
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];

回答by Mike McMaster

Did you try [textView sizeThatFits:textView.bounds]?

你试了[textView sizeThatFits:textView.bounds]吗?

Edit: sizeThatFits returns the size but does not actually resize the component. I'm not sure if that's what you want, or if [textView sizeToFit]is more what you were looking for. In either case, I do not know if it will perfectly fit the content like you want, but it's the first thing to try.

编辑:sizeThatFits 返回大小,但实际上并不调整组件的大小。我不确定这是否是您想要的,还是[textView sizeToFit]您正在寻找的更多。在任何一种情况下,我都不知道它是否会像您想要的那样完美契合内容,但这是首先要尝试的。

回答by Code Addict

Another method is the find the size a particular string will take up using the NSStringmethod:

另一种方法是使用以下NSString方法查找特定字符串将占用的大小:

-(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

-(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

This returns the size of the rectangle that fits the given string with the given font. Pass in a size with the desired width and a maximum height, and then you can look at the height returned to fit the text. There is a version that lets you specify line break mode also.

这将返回适合具有给定字体的给定字符串的矩形的大小。传入具有所需宽度和最大高度的大小,然后您可以查看返回的高度以适合文本。还有一个版本可以让您指定换行模式。

You can then use the returned size to change the size of your view to fit.

然后,您可以使用返回的大小来更改视图的大小以适合。