65

多年来,我一直在这里使用公认的答案。

在 iOS 7 上,contentSize.height 变为 frame.height-8,与文本内容无关。

在 iOS 7 上调整高度的工作方法是什么?

4

14 回答 14

55

我赞成这种最小的代码更改:只需在addSubview获取heightframe

...
[scrollView1 addSubview: myTextView];

    [myTextView sizeToFit]; //added
    [myTextView layoutIfNeeded]; //added

CGRect frame = myTextView.frame;
...

这是向后兼容 iOS 6 测试的。请注意,它会收缩宽度。如果您只对高度感兴趣并且具有固定宽度,只需获取新高度但设置原始宽度,它在 iOS 6 和 7 上都像以前一样工作。

(推测:它的大小也适合 iOS 7,但布局稍后或在单独的线程中更新,这会立即强制布局,以便其框架及时更新,以便在几行后使用其高度值在同一个线程中。)

笔记:

1)您可能已经或可能没有以这种方式实现外部容器调整大小。不过,它似乎确实是一个常见的片段,我已经在我的项目中使用了它。

2) 由于sizeToFit似乎在 iOS 7 上按预期工作,您可能不需要过早的 addSubView。我还没有测试它是否仍然可以在 iOS 6 上运行。

3) 推测:额外的layoutIfNeeded中间线程可能代价高昂。我看到的另一种方法是在布局回调上调整外部容器的大小(根据操作系统是否决定是否需要布局来触发),其中外部容器调整大小将导致另一个布局更新。两种更新都可以与其他布局更新结合使用以提高效率。如果您确实有这样的解决方案并且您可以证明它有效,请将其添加为答案,我一定会在此处提及。

于 2013-09-30T09:21:07.500 回答
33

由于我使用的是 Auto Layout,因此我使用 的值[textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height来更新constanttextViewheight UILayoutConstraint

于 2013-10-17T20:17:50.103 回答
17

我使用了 madmik 答案的改编版本,它消除了软糖因素:

- (CGFloat)measureHeightOfUITextView:(UITextView *)textView
{
    if ([textView respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
    {
        // This is the code for iOS 7. contentSize no longer returns the correct value, so
        // we have to calculate it.
        //
        // This is partly borrowed from HPGrowingTextView, but I've replaced the
        // magic fudge factors with the calculated values (having worked out where
        // they came from)

        CGRect frame = textView.bounds;

        // Take account of the padding added around the text.

        UIEdgeInsets textContainerInsets = textView.textContainerInset;
        UIEdgeInsets contentInsets = textView.contentInset;

        CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right;
        CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;

        frame.size.width -= leftRightPadding;
        frame.size.height -= topBottomPadding;

        NSString *textToMeasure = textView.text;
        if ([textToMeasure hasSuffix:@"\n"])
        {
            textToMeasure = [NSString stringWithFormat:@"%@-", textView.text];
        }

        // NSString class method: boundingRectWithSize:options:attributes:context is
        // available only on ios7.0 sdk.

        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];

        NSDictionary *attributes = @{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle };

        CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                                  options:NSStringDrawingUsesLineFragmentOrigin
                                               attributes:attributes
                                                  context:nil];

        CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);
        return measuredHeight;
    }
    else
    {
        return textView.contentSize.height;
    }
}
于 2013-10-21T11:26:11.403 回答
16

根据其他答案,我使它工作(在 Swift 中)。这解决了换行符的问题。

textView.sizeToFit()
textView.layoutIfNeeded()
let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height
textView.contentSize.height = height

需要自动布局。

于 2015-01-11T05:28:34.597 回答
15

如果您使用的是自动布局,您可以创建一个简单的UITextView子类,它可以自行调整文本视图高度以适应内容:

@interface ContentHeightTextView : UITextView

@end

@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end

@implementation ContentHeightTextView

- (void)layoutSubviews
{
    [super layoutSubviews];

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

    if (!self.heightConstraint) {
        self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
        [self addConstraint:self.heightConstraint];
    }

    self.heightConstraint.constant = size.height;

    [super layoutSubviews];
}

@end

当然,文本视图的宽度和位置必须由程序中其他地方配置的附加约束来定义。

如果您在 IB 中创建此自定义文本视图,请给文本视图一个高度约束以满足 Xcode;只需确保在 IB 中创建的高度约束只是一个占位符(即,勾选“在构建时删除”框)。

在此处输入图像描述

实现UITextView子类的另一种方法如下(此实现可能符合最佳实践):

@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end

@implementation ContentHeightTextView

- (void)layoutSubviews
{
    [super layoutSubviews];

    [self setNeedsUpdateConstraints];
}

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

    if (!self.heightConstraint) {
        self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
        [self addConstraint:self.heightConstraint];
    }

    self.heightConstraint.constant = size.height;
    [super updateConstraints];
}

@end
于 2014-02-23T08:25:01.003 回答
4

如果您使用自动布局,则可以使用以下 UITextView 子类添加固有高度:

@implementation SelfSizingTextView

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

- (void)setFont:(UIFont *)font
{
    [super setFont:font];
    [self invalidateIntrinsicContentSize];
}

- (CGSize)intrinsicContentSize
{
    CGFloat width = self.frame.size.width;
    CGSize size = [self sizeThatFits:CGSizeMake(width, MAXFLOAT)];
    return CGSizeMake(UIViewNoIntrinsicMetric, size.height);
}

@end
于 2014-12-05T22:08:05.187 回答
3

这种方法似乎有效。

// Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
- (CGFloat)measureHeight
{
if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
{
    CGRect frame = internalTextView.bounds;
    CGSize fudgeFactor;
    // The padding added around the text on iOS6 and iOS7 is different.
    fudgeFactor = CGSizeMake(10.0, 16.0);

    frame.size.height -= fudgeFactor.height;
    frame.size.width -= fudgeFactor.width;

    NSMutableAttributedString* textToMeasure;
    if(internalTextView.attributedText && internalTextView.attributedText.length > 0){
        textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText];
    }
    else{
        textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text];
        [textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)];
    }

    if ([textToMeasure.string hasSuffix:@"\n"])
    {
        [textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]];
    }

    // NSAttributedString class method: boundingRectWithSize:options:context is
    // available only on ios7.0 sdk.
    CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                              context:nil];

    return CGRectGetHeight(size) + fudgeFactor.height;
}
else
{
    return self.internalTextView.contentSize.height;
}
}
于 2013-09-29T22:58:36.537 回答
1

如果您使用的是 iOS 7+,您只需打开自动布局,将文本视图的每一侧固定到其父视图的边缘,就可以正常工作。不需要额外的代码。

于 2015-03-08T00:05:37.107 回答
1

不确定这是否总是如此,但至少从 iOS 10 开始以下情况是正确的。

UITextView实现intrinsicContentSize属性 if scrollEnabled == NO。这意味着您只需要确保文本视图的宽度受到足够的限制,然后您就可以使用内在的内容高度(通过自动布局内容拥抱/压缩阻力优先级或在手动布局期间直接使用该值)。

不幸的是,没有记录这种行为。苹果本可以很容易地为我们省去一些麻烦……不需要额外的高度限制、子类化等。

于 2021-01-23T16:43:50.247 回答
0

在 iOS 8 中,您将从父级继承一些内容偏移量,您也需要摆脱这些偏移量。

子类示例

// Originally from https://github.com/Nikita2k/resizableTextView

#import "ResizableTextView.h"

@implementation ResizableTextView

- (void) updateConstraints {

    // calculate contentSize manually
    // ios7 doesn't calculate it before viewDidAppear and we'll get here before
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

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

    [super updateConstraints];
}

- (void)setContentOffset:(CGPoint)contentOffset
{
    // In iOS 8 we seem to be inheriting the content offset from the parent.
    // I'm not interested
}

@end
于 2014-09-16T11:57:27.457 回答
0

在情节提要中,如果使用约束,请确保您在 UITextView 的 xcode 右侧窗格的“标尺”选项卡中被约束到您的超级视图。我的问题是我对“尾随空格”的限制为 -80 分。

在此处输入图像描述

于 2014-11-25T16:56:53.630 回答
0

使用自动布局的人并且您的 sizetofit 不起作用,那么请检查您的宽度限制一次。如果您错过了宽度限制,那么高度将是准确的。

无需使用任何其他 API。只需一行就可以解决所有问题。

[_textView sizeToFit];

在这里,我只关心高度,保持宽度固定,并错过了我的 TextView 在情节提要中的宽度约束。

这是为了显示来自服务的动态内容。

希望这可能会有所帮助..

于 2015-01-02T07:02:52.013 回答
0

我写了一个类别UITextView

- (CGSize)intrinsicContentSize {
    return self.contentSize;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];
    [self invalidateIntrinsicContentSize];
}

UIKit设置其contentSize时,UITextView调整其intrinsic content size。这与autolayout.

于 2016-03-30T16:14:01.743 回答
0

bilobatum 给出的答案与自动布局完美配合,即子类化 textview。

如果您想限制文本视图的高度,请添加另一个约束(我使用情节提要添加它,即高度 <= 166(根据您的需要高度))

然后在子类内部将高度约束的优先级降低到 750(self.heightConstraint.priority = 750),以避免在子类中添加的高度约束与在情节提要上添加的高度约束之间的冲突。

于 2017-03-06T08:06:52.697 回答