61

次の質問は、この質問の一種の続きです。

iOS: 自動レイアウトの複数行の UILabel

主なアイデアは、AutoLayout が適切に表示する方法を知ることができるように、すべてのビューが「優先」(本質的な) サイズであることを示すことになっているということです。UILabel は、ビュー自体が表示に必要なサイズを認識できない状況の一例です。提供される幅によって異なります。

mwhussが指摘したように、setPreferredMaxLayoutWidthは、ラベルが複数の行にまたがるようにするトリックを行いました。しかし、それはここでの主な質問ではありません。問題は、引数としてsetPreferredMaxLayoutWidthに送信するこのの値をいつどこで取得するかです。

私は合法的に見えるものを作ることができたので、何か間違っている場合は修正してください。より良い方法を知っている場合は教えてください.

UIView では

-(CGSize) intrinsicContentSize

self.frame.widthに従って、UILabels に PreferredMaxLayoutWidth を設定しました。

UIViewController の

-(void) viewDidLayoutSubviews

メインビューのサブビューが画面上に存在する正確なフレームで指定されている場所を知っている最初のコールバックメソッドです。そのメソッド内から、サブビューを操作し、固有のサイズを無効にして、指定された幅に基づいて UILabels が複数の行に分割されるようにします。

4

9 に答える 9

63

Advanced Auto Layout Toolboxの「Intrinsic Content Size of Multi-Line Text」セクションのobjc.ioに関するこの質問に対する回答があります。関連情報は次のとおりです。

UILabel と NSTextField の固有のコンテンツ サイズは、複数行のテキストに対してあいまいです。テキストの高さは、制約を解決するときにまだ決定されていない線の幅に依存します。この問題を解決するために、両方のクラスに、固有のコンテンツ サイズを計算するための最大行幅を指定する、preferredMaxLayoutWidth という新しいプロパティがあります。

通常、この値は事前にわからないため、これを正しく行うには 2 段階のアプローチを取る必要があります。最初に Auto Layout を機能させてから、結果のフレームをレイアウト パスで使用して、推奨される最大幅を更新し、レイアウトを再度トリガーします。

View Controller内で使用するために提供されるコード:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

彼らの投稿を見てください。レイアウトを 2 回行う必要がある理由について、さらに詳しい情報があります。

于 2013-11-29T14:19:14.797 に答える
44

その幅を明確に定義している制約がある場合、 UILabel が優先される最大レイアウト幅の幅にデフォルト設定されないのは面倒です。

Autolayout でラベルを使用したほぼすべてのケースで、推奨される最大レイアウト幅は、残りのレイアウトが実行された後のラベルの実際の幅でした。

したがって、これを自動的に行うために、UILabel をオーバーライドするサブクラスを使用しましたsetBounds:。ここで、スーパー実装を呼び出します。まだそうでない場合は、優先される最大レイアウト幅を境界サイズの幅に設定します。

強調することは重要です。preferred max layout を設定すると、別のレイアウト パスが実行されるため、無限ループに陥る可能性があります。

于 2013-07-05T16:18:09.063 に答える
33

アップデート

私の元の回答は役に立ちそうなので、以下ではそのままにしておきますが、私自身のプロジェクトでは、iOS 7 と iOS 8 のバグを回避するより信頼性の高いソリューションを見つけました。 https://github.com/nicksnyder/ios -セルレイアウト

元の答え

これは、iOS 7 および iOS 8 で機能する完全なソリューションです。

オブジェクティブ C

@implementation AutoLabel

- (void)setBounds:(CGRect)bounds {
  if (bounds.size.width != self.bounds.size.width) {
    [self setNeedsUpdateConstraints];
  }
  [super setBounds:bounds];
}

- (void)updateConstraints {
  if (self.preferredMaxLayoutWidth != self.bounds.size.width) {
    self.preferredMaxLayoutWidth = self.bounds.size.width;
  }
  [super updateConstraints];
}

@end

迅速

import Foundation

class EPKAutoLabel: UILabel {

    override var bounds: CGRect {
        didSet {
            if (bounds.size.width != oldValue.size.width) {
                self.setNeedsUpdateConstraints();
            }
        }
    }

    override func updateConstraints() {
        if(self.preferredMaxLayoutWidth != self.bounds.size.width) {
            self.preferredMaxLayoutWidth = self.bounds.size.width
        }
        super.updateConstraints()
    }
}
于 2014-10-03T15:21:49.737 に答える
9

UIScrollView 内の自動レイアウトされた UILabel が縦向きにうまくレイアウトされていましたが、横向きに回転すると UILabel の高さが再計算されませんでした。

おそらく、preferredMaxLayoutWidth が正しく設定されているため、@jrturton からの回答でこれが修正されたことがわかりました。

使用したコードは次のとおりです。Interface builder の Custom クラスをCVFixedWidthMultiLineLabelに設定するだけです。

CVFixedWidthMultiLineLabel.h

@interface CVFixedWidthMultiLineLabel : UILabel

@end 

CVFixedWidthMultiLineLabel.m

@implementation CVFixedWidthMultiLineLabel

// Fix for layout failure for multi-line text from
// http://stackoverflow.com/questions/17491376/ios-autolayout-multi-line-uilabel
- (void) setBounds:(CGRect)bounds {
    [super setBounds:bounds];

    if (bounds.size.width != self.preferredMaxLayoutWidth) {
        self.preferredMaxLayoutWidth = self.bounds.size.width;
    }
}

@end
于 2014-05-16T13:21:59.780 に答える
2

boundingRectWithSize の使用

UITableViewCell次のように目的の幅を測定することにより、改行として「\ n」を使用していた従来の2つの複数行ラベルとの闘いを解決しました。

- (CGFloat)preferredMaxLayoutWidthForLabel:(UILabel *)label
{
    CGFloat preferredMaxLayoutWidth = 0.0f;
    NSString *text = label.text;
    UIFont *font = label.font;
    if (font != nil) {
        NSMutableParagraphStyle *mutableParagraphStyle = [[NSMutableParagraphStyle alloc] init];
        mutableParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

        NSDictionary *attributes = @{NSFontAttributeName: font,
                                     NSParagraphStyleAttributeName: [mutableParagraphStyle copy]};
        CGRect boundingRect = [text boundingRectWithSize:CGSizeZero options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
        preferredMaxLayoutWidth = ceilf(boundingRect.size.width);

        NSLog(@"Preferred max layout width for %@ is %0.0f", text, preferredMaxLayoutWidth);
    }


    return preferredMaxLayoutWidth;
}

次に、メソッドの呼び出しは次のように簡単でした。

CGFloat labelPreferredWidth = [self preferredMaxLayoutWidthForLabel:textLabel];
if (labelPreferredWidth > 0.0f) {
    textLabel.preferredMaxLayoutWidth = labelPreferredWidth;
}
[textLabel layoutIfNeeded];
于 2014-06-17T17:58:05.013 に答える
0

きれいな解決策はrowcount = 0、ラベルの heightconstraint のプロパティを設定して使用することです。次に、内容が設定された後に呼び出します

CGSize sizeThatFitsLabel = [_subtitleLabel sizeThatFits:CGSizeMake(_subtitleLabel.frame.size.width, MAXFLOAT)];
_subtitleLabelHeightConstraint.constant = ceilf(sizeThatFitsLabel.height);

-(void) updateViewConstraintsiOS 7.1以降に問題があります。

于 2014-05-05T18:42:33.097 に答える