web-dev-qa-db-ja.com

UITextViewをiOS 7のコンテンツに合わせてサイズを変更するにはどうすればよいですか?

私は受け入れられた答えを使用しています here 何年もの間。

IOS 7では、テキストコンテンツに関係なく、contentSize.heightはframe.height-8になります。

IOS 7で高さを調整する作業方法は何ですか?

64

私はこの最小限のコード変更を好みます:addSubviewの後で、heightframeを取得する前に、これらの2行を追加するだけです

...
[scrollView1 addSubview: myTextView];

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

CGRect frame = myTextView.frame;
...

これは、iOS 6との後方互換性がテストされています。NOTE幅を縮小ラップします。高さに関心があり、幅が固定されている場合は、新しい高さを取得して元の幅を設定するだけで、iOS 6と7の両方で以前と同じように機能します。

(推測:iOS 7にも収まるサイズになりますが、レイアウトは後でまたは別のスレッドで更新され、これによりフレームが時間内に更新されるようにレイアウトがすぐに強制されます同じスレッドで数行後に高さの値を使用するため。)

注:

1)この方法で外部コンテナのサイズ変更を実装する場合としない場合があります。ただし、これは一般的なスニペットのようで、プロジェクトで使用しました。

2)sizeToFitはiOS 7で期待どおりに機能するようであるため、時期尚早のaddSubViewは必要ないでしょう。まだiOS 6で動作するかどうかは、私がテストしていません。

3)推測:余分なlayoutIfNeededミッドスレッドはコストがかかる場合があります。私が見る代替案は、外部コンテナのサイズ変更が別のレイアウト更新を引き起こすレイアウトコールバックで外部コンテナのサイズを変更することです(OSがレイアウトが必要かどうかを決定するかどうかに応じて起動または非起動)。両方の更新を他のレイアウト更新と組み合わせて、より効率的にすることができます。あなたがdoそのような解決策を持っていて、あなたがそれを示すことができる場合isより効率的に、答えとしてそれを追加し、私はそれをここで必ず言及するでしょう。

55

自動レイアウトを使用しているので、[textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].heightの値を使用して、constantの高さtextViewUILayoutConstraintを更新します。

33
ma11hew28

ファッジファクターを排除する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;
    }
}
17
tarmes

他の回答に基づいて、私はそれを機能させました(Swiftで)。これにより、改行文字に関する問題が解決されます。

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

自動レイアウトが必要です。

16
MKatleast3

自動レイアウトを使用している場合、コンテンツに合わせてテキストビューの高さを自動調整する簡単な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で作成された高さの制約が単なるプレースホルダーであることを確認してください(つまり、「ビルド時に削除」というボックスにチェックを入れてください)。

enter image description here

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
15
bilobatum

自動レイアウトを使用している場合、固有の高さを追加する次の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
4
phatmann

この方法はうまくいくようです。

// 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;
}
}
3
madmik3

IOS 7以降を使用している場合は、自動レイアウトをオンにして、テキストビューの各辺をその親ビューのエッジに固定するだけで正常に機能します。追加のコードは必要ありません。

1
Swindler

ストーリーボードで制約を使用する場合、UITextViewのxcodeの右側のペインの「ルーラー」タブでスーパービューに制約されていることを確認してください。私の問題は、「Trailing space to」に-80ポイントの制約があることでした。

enter image description here

0
DanWebster

Bilobatumの答えは、自動レイアウト、つまりテキストビューのサブクラス化で完全に機能しました。

テキストビューの高さを制限する場合は、別の制約を追加します(ストーリーボード、つまり高さ<= 166(必要に応じて高さ)を使用して追加しました)

次に、サブクラス内で、高さ制約の優先度を750(self.heightConstraint.priority = 750)に下げて、サブクラスに追加された高さ制約とストーリーボードに追加された高さ制約の間の競合を回避します。

0
Sunil Bhosale

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
0
Johan Forssell

UITextViewにカテゴリを書きました:

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

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

UIKitcontentSizeを設定すると、UITextViewintrinsic content sizeを調整します。 autolayoutでうまく動作します。

0
Mert Buran

自動レイアウトを使用しているユーザーがsizetofitが機能しない場合は、幅の制約を一度確認してください。幅の制約を逃した場合、高さは正確になります。

他のAPIを使用する必要はありません。 1行だけですべての問題が修正されます。

[_textView sizeToFit];

ここでは、幅を固定したまま、高さのみに関心があり、ストーリーボードのTextViewの幅の制約を逃していました。

そして、これはサービスからの動的コンテンツを表示することでした。

これが役立つことを願っています。

0
Swaroop S