web-dev-qa-db-ja.com

UILabelがテキストを正しくラップしないことがある(自動レイアウト)

ビューにUilabelのセットアップがあります。幅の制約はありませんが、その幅は代わりにサムネイル画像の先頭の制約と、ビューのエッジの末尾の制約によって決定されます。

enter image description here

ラベルは0行に設定され、ワー​​ドラップされます。私の理解では、これによりUilabelのフレームが成長するはずであり、実際に時々そうなることもあります。 (自動レイアウトの前に、コードでラベルのフレームを計算して更新します)。

結果は、ある場合には正しく機能し、他の場合には正しく機能しないことです。ほとんどのセルが正常に機能しているのを確認しますが、最後のセルが大きすぎるようです。実際、適切なサイズです。タイトル「Fair Oaks Smog Check Test」は実際には「Only」で終わります。セルサイズの計算は正しいので、そのサイズにする必要があります。ただし、ラベルは何らかの理由でテキストをラップしません。フレーム幅が右に伸びていないので、それは問題ではありません。

enter image description here

ここで何が起こっているのでしょうか?それは100%一貫性があり、常にそのセルであり、その上のセルではなく、それはテキストのサイズに関連していると思うようになり、このビューがセルに追加されるとUILabelはテキストを再レイアウトしません(これは実際には幅を小さくします)。

何かご意見は?

いくつかの追加情報

セルの高さは、作成して静的変数に保存する1つのサンプルセルから計算されます。

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.items.count == 0) {
        return 60;
    }
    static TCAnswerBuilderCell *cell = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred,
                  ^{
                      // get a sample cellonce
                      cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL];
                  });
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return [cell heightForCellWithTableWidth:self.tableView.frame.size.width];
}

データオブジェクトを使用してセルをオンザフライで構成し、指定されたテーブル幅のセルの高さを計算するメソッドを呼び出します(最初はセルフレームが常に正しいとは限りません)。

これは、実際にはラベルが存在する場所であるため、ビューのheightメソッドを呼び出します。

- (CGFloat)heightForCellWithTableWidth:(CGFloat)tableWidth {
    // subtract 38 from the constraint above
    return [self.thirdPartyAnswerView heightForViewWithViewWidth:tableWidth - 38];
}

このメソッドは、ラベルの正しい幅を計算して計算を行うことにより、高さを決定します。

- (CGFloat)heightForViewWithViewWidth:(CGFloat)viewWidth {
    CGFloat widthForCalc = viewWidth - self.imageFrameLeadingSpaceConstraint.constant - self.thumbnailFrameWidthConstraint.constant - self.titleLabelLeadingSpaceConstraint.constant;
    CGSize size = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(widthForCalc, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
    CGFloat returnHeight = self.frame.size.height - self.titleLabel.frame.size.height + size.height;
    CGFloat height = returnHeight < self.frame.size.height ? self.frame.size.height : returnHeight;
    return height;
}

これは100%正しく動作します。

セルはcellForRowAtIndexPathで明らかに作成され、すぐに構成されます。

if (self.items.count > 0) {
    TCAnswerBuilderCell *cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL forIndexPath:indexPath];
    [cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
    return cell;
}

セルの構成では、ビューはペン先から読み込まれます(他の場所で再利用されるため、セル内に直接存在しないのはこのためです)。セルは次のように追加します。

- (void) configureCellWithThirdPartyObject:(TCThirdPartyObject *)object {
    self.detailDisclosureImageView.hidden = NO;
    if (!self.thirdPartyAnswerView) {
        self.thirdPartyAnswerView = [TCThirdPartyAPIHelper thirdPartyAnswerViewForThirdPartyAPIServiceType:object.thirdPartyAPIType];
        self.thirdPartyAnswerView.translatesAutoresizingMaskIntoConstraints = NO;
        [self.contentView addSubview:self.thirdPartyAnswerView];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_thirdPartyAnswerView]-38-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_thirdPartyAnswerView)]];
    }
    [self.thirdPartyAnswerView configureViewForThirdPartyObject:object forViewStyle:TCThirdPartyAnswerViewStyleSearchCell];
}

最後に、私のビュー構成は次のようになります。

- (void) configureViewForThirdPartyObject:(TCTPOPlace *)object forViewStyle:(TCThirdPartyAnswerViewStyle) style {
    self.titleLabel.text = object.name;
    self.addressLabel.text = [NSString stringWithFormat:@"%@, %@, %@", object.address, object.city, object.state];
    self.ratingsLabel.text = [NSString stringWithFormat:@"%d Ratings", object.reviewCount];
    NSString *ratingImageName = [NSString stringWithFormat:@"Yelp_star_rating_%.1f.png", object.rating];
    UIImage *ratingsImage = [UIImage imageNamed:ratingImageName];
    if (ratingsImage) {
        self.ratingImageView.image = ratingsImage;
    }
    if (object.imageUrl) {
        [self.thumbnailImageView setImageWithURL:[NSURL URLWithString:object.imageUrl] completed:nil];
    }
}

ある種の解決策ですが、理由がわかりません

  1. 私のサブビューは320の幅で設計されましたが、幅の制限はありません
  2. サブビューがセルに追加されましたが、次のような水平方向の制約があります。
    • @"|[_thirdPartyAnswerView]-38-|"
  3. ビューは、セルに追加された直後に構成されました。つまり、titleLabelのテキストはその直後に設定されました。
  4. なんらかの理由で、テキストは、ビューが282ではなく320になったようにレイアウトされました。
  5. サブビューのフレームが282に更新された場合でも、ラベルは更新されず、ラベルのサイズが正しく維持されるという制約がありました。
  6. Xibのビューのサイズを282に変更すると、ラベルのサイズが正しいため、問題が修正されました。

親ビューに先行制約と後続制約の両方がある場合、親ビューのサイズが更新された後にラベルが再レイアウトされない理由がまだわかりません。

解決済み

以下のマットの答えを参照してください: https://stackoverflow.com/a/15514707/2874

コメントを読んでいない場合、主な問題は、表示されるよりも大きな幅でビューを設計するときに、IBを介してpreferredMaxLayoutWidthを設定していることです(場合によって)。 preferredMaxLayoutWidthは、テキストの折り返し位置を決定するために使用されるものです。そのため、私のビューとtitleLabelのサイズは正しく変更されましたが、preferredMaxLayoutWidthはまだ古い値であり、予期しないポイントで折り返しが発生していました。代わりにtitleLabelを自動サイズ(IBでは⌘=)に設定し、superを呼び出す前にpreferredMaxLayoutWidthlayoutSubviewsを動的に更新することが重要でした。ありがとう、マット!

40
Bob Spryn

私は、セルの高さが異なるテーブル内のセルで5つのラベルの自動レイアウトを使用するアプリを書いた人です。セルの高さは異なります。したがって、あなたが問題を抱えている理由は、制約がレイアウトを決定していないこと、つまりセルの要素のレイアウトがあいまいであるためかもしれないことをお勧めします。私はあなたの制約を見ることができないので、その仮説をテストすることはできません。ただし、po [[UIWindow keyWindow] _autolayoutTrace]デバッガーで一時停止したとき。

また、もう1つ提案があります(物を投げるだけで申し訳ありません):ラベルのpreferredMaxLayoutWidthを設定していることを確認してください。これは、ラベルが水平方向に拡大を停止し、垂直方向に拡大を開始する幅であるため、重要です。

42
matt

私は同じ問題を抱えていました この答え からの提案を使用してそれを解決しました。 UILabelのサブクラスにこのコードを配置しました。

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

これがUILabelの既定の動作ではない理由、または少なくともフラグを使用してこの動作を有効にできない理由はわかりません。

PreferredMaxLayoutWidthがレイアウトプロセスの途中で設定されることを少し心配していますが、それを回避する簡単な方法はありません。

13
phatmann

また、コードでレイアウト制約に整数を渡していることを確認してください。

私にとっては、いくつかの計算(例:convertPoint:toView :)の後、23.99999997のようなものを渡しており、最終的には2行のラベルが1行として表示されます(フレームは正しく計算されているように見えました) 。私の場合、CGRectIntegralがトリックを行いました!

丸めエラーはyaを殺す可能性があります:)

0