私は自動レイアウトを学ぼうとしていますが、最終的に私はこれが起こったときにハングしていると思いました。プロトタイプのセルとラベルで遊んでいます。私が持っているしようとしています
titleLbl
-写真の青いボックスmoneyLbl
-写真の緑色のボックスsubTitleLbl
-写真の赤いボックスこれまでのところ私はこれを持っています:
私が達成したいことは:
ご覧のように、最後の行の例を除き、これはほぼすべて機能しています。私はこれを調査しようとしましたが、収集できることから、本質的なコンテンツサイズに関係しています。オンラインの多くの投稿はsetPreferredMaxLayoutWidth
を設定することを推奨していますが、titleLbl
に対して複数の場所でこれを実行しましたが、効果はありませんでした。 180
にハードコーディングした場合にのみ結果が得られましたが、テキストの上/下の他の青いボックスに空白/パディングも追加され、赤/青のボックス間のスペースが大幅に増加しました。
私の制約は次のとおりです。
題名:
お金:
字幕:
他のテキストサンプルで実行したテストに基づいて、ストーリーボードに表示されている幅を使用しているように見えますが、それを修正しようとしても機能しません。
セルのivarを作成し、それを使用してセルの高さを作成しました。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
[sizingCellTitleSubMoney.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]];
[sizingCellTitleSubMoney.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]];
[sizingCellTitleSubMoney.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]];
[sizingCellTitleSubMoney layoutIfNeeded];
CGSize size = [sizingCellTitleSubMoney.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height+1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MATitleSubtitleMoneyTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifierTitleSubTitleMoney];
if(!cell)
{
cell = [[MATitleSubtitleMoneyTableViewCell alloc] init];
}
cell.titleLbl.layer.borderColor = [UIColor blueColor].CGColor;
cell.titleLbl.layer.borderWidth = 1;
cell.subtitleLbl.layer.borderColor = [UIColor redColor].CGColor;
cell.subtitleLbl.layer.borderWidth = 1;
cell.moneyLbl.layer.borderColor = [UIColor greenColor].CGColor;
cell.moneyLbl.layer.borderWidth = 1;
[cell.titleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"title"]];
[cell.subtitleLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"subtitle"]];
[cell.moneyLbl setText:[[tableViewData objectAtIndex:indexPath.row] objectForKey:@"cost"]];
return cell;
}
セルのレイアウトサブビュー内でsetPreferredMaxLayoutWidth
を設定しようとしました。
- (void)layoutSubviews
{
[super layoutSubviews];
NSLog(@"Money lbl: %@", NSStringFromCGRect(self.moneyLbl.frame));
NSLog(@"prefferredMaxSize: %f \n\n", self.moneyLbl.frame.Origin.x-8);
[self.titleLbl setPreferredMaxLayoutWidth: self.moneyLbl.frame.Origin.x-8];
}
ログ:
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] Money lbl: {{209, 20}, {91, 61}}
2014-04-30 21:37:32.475 AutoLayoutTestBed[652:60b] prefferredMaxSize: 201.000000
2つの要素間に8pxがあり、コンソールがこれを検証するので、これは私が期待するものです。青いボックスに追加するテキストの量(ストーリーボードと同じ)に関係なく、最初の行で完全に機能しますが、緑色のボックスを増やすと幅の計算が無効になります。他の質問からの回答に基づいて、tableView:willDisplayCell
およびtableView:heightForRowAtIndexPath:
でこれを設定しようとしました。しかし、それが何であれレイアウトしません。
どんな助け、アイデアやコメントも大歓迎です
これを読んだ後、さらに良い答えを見つけました: http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights
UILabel
サブクラスを作成して、layoutSubviews
をオーバーライドします。
- (void)layoutSubviews
{
[super layoutSubviews];
self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
[super layoutSubviews];
}
これにより、preferredMaxLayoutWidth
が常に正しいことが保証されます。
更新:
IOSをさらに数回リリースし、さらにユースケースをテストした結果、上記のケースではすべてのケースで十分ではないことがわかりました。 iOS 8(私が信じる)の時点では、いくつかの状況下では、画面がロードされる前にdidSet
bounds
だけが呼び出されました。
IOS 9では、UIVIewController
でモーダルUITableView
を使用しているときに、layoutSubviews
とset bounds
の両方が呼び出されていたafterheightForRowAtIndexPath
。多くのデバッグの後、唯一の解決策はset frame
をオーバーライドすることでした。
以下のコードは、すべてのiOS、コントローラー、画面サイズなどで機能するようにするために必要なようです。
override public func layoutSubviews()
{
super.layoutSubviews()
self.preferredMaxLayoutWidth = self.bounds.width
super.layoutSubviews()
}
override public var bounds: CGRect
{
didSet
{
self.preferredMaxLayoutWidth = self.bounds.width
}
}
override public var frame: CGRect
{
didSet
{
self.preferredMaxLayoutWidth = self.frame.width
}
}
私があなたを正しく理解しているかどうかはわかりませんが、私の解決策は最高のものからはほど遠いですが、ここにあります:
カットされたラベルに高さの制約を追加し、その制約をセルのアウトレットに接続しました。 layoutSubviewメソッドをオーバーライドしました:
- (void) layoutSubviews {
[super layoutSubviews];
[self.badLabel setNeedsLayout];
[self.badLabel layoutIfNeeded];
self.badLabelHeightConstraint.constant = self.badLabel.intrinsicContentSize.height;
}
そしてヴィオラ!その方法を試して結果を教えてください、ありがとう。
次のように、セルをラップするのに少し時間を費やしました。
+--------------------------------------------------+
| This is my cell.contentView |
| +------+ +-----------------+ +--------+ +------+ |
| | | | multiline | | title2 | | | |
| | img | +-----------------+ +--------+ | img | |
| | | +-----------------+ +--------+ | | |
| | | | title3 | | title4 | | | |
| +------+ +-----------------+ +--------+ +------+ |
| |
+--------------------------------------------------+
多くの試みの後、実際に動作しているプロトタイプセルを使用する方法のソリューションを見つけました。
主な問題は、私のラベルがそこにあるかもしれないし、ないかもしれないということでした。すべての要素はオプションである必要があります。
まず、「プロトタイプ」セルは、「実際の」環境にないときにのみレイアウトする必要があります。
したがって、フラグ 'heightCalculationInProgress'を作成しました。
誰か(tableViewのdataSource)が高さを計算することを求めているとき、ブロックを使用してプロトタイプセルにデータを提供しています:そして、プロトタイプセルにコンテンツをレイアウトし、そこから高さを取得するように求めています。シンプル。
LayoutSubview再帰によるiOS7のバグを回避するために、「layoutingLock」変数があります
- (CGFloat)heightWithSetupBlock:(nonnull void (^)(__kindof UITableViewCell *_Nonnull cell))block {
block(self);
self.heightCalculationInProgress = YES;
[self setNeedsLayout];
[self layoutIfNeeded];
self.layoutingLock = NO;
self.heightCalculationInProgress = NO;
CGFloat calculatedHeight = [self.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
CGFloat height = roundf(MAX(calculatedHeight, [[self class] defaultHeight]));
return height;
}
外部からのこの「ブロック」は次のようになります。
[prototypeCell heightWithSetupBlock:^(__kindof UITableViewCell * _Nonnull cell) {
@strongify(self);
cell.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 0);
cell.dataObject = dataObject;
cell.multilineLabel.text = @"Long text";
// Include any data here
}];
そして今、最も興味深い部分を開始します。レイアウト:
- (void)layoutSubviews {
if(self.layoutingLock){
return;
}
// We don't want to do this layouting things in 'real' cells
if (self.heightCalculationInProgress) {
// Because of:
// Support for constraint-based layout (auto layout)
// If nonzero, this is used when determining -intrinsicContentSize for multiline labels
// We're actually resetting internal constraints here. And then we could reuse this cell with new data to layout it correctly
_multilineLabel.preferredMaxLayoutWidth = 0.f;
_title2Label.preferredMaxLayoutWidth = 0.f;
_title3Label.preferredMaxLayoutWidth = 0.f;
_title4Label.preferredMaxLayoutWidth = 0.f;
}
[super layoutSubviews];
// We don't want to do this layouting things in 'real' cells
if (self.heightCalculationInProgress) {
// Make sure the contentView does a layout pass here so that its subviews have their frames set, which we
// need to use to set the preferredMaxLayoutWidth below.
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
// Set the preferredMaxLayoutWidth of the mutli-line bodyLabel based on the evaluated width of the label's frame,
// as this will allow the text to wrap correctly, and as a result allow the label to take on the correct height.
_multilineLabel.preferredMaxLayoutWidth = CGRectGetWidth(_multilineLabel.frame);
_title2Label.preferredMaxLayoutWidth = CGRectGetWidth(_title2Label.frame);
_title3Label.preferredMaxLayoutWidth = CGRectGetWidth(title3Label.frame);
_title4Label.preferredMaxLayoutWidth = CGRectGetWidth(_title4Label.frame);
}
if (self.heightCalculationInProgress) {
self.layoutingLock = YES;
}
}
Omg、ちょうど同じ問題がありました。 @Simon McLoughlinの答えは私を助けませんでした。だが
titleLabel.adjustsFontSizeToFitWidth = true
魔法をしました!動作しますが、理由はわかりませんが、動作します。はい、ラベルにも明示的なpreferredMaxLayoutWidth値が必要です。