111

AutoLayoutどこでも使用できることを発見したので、今はtableHeaderView.

必要なものすべて (ラベルなど...) を制約付きで作成し、これを'subclassに追加しました。UIViewCustomViewUITableViewtableHeaderView

UITableViewが常に の上に表示されることCustomView除いCustomViewすべて正常に機能しUITableViewます。

私が何をしてheightも、UITableView'tableHeaderView常に0 のようです (幅、x、y も同様です)。

私の質問:フレームを手動で設定せずにこれを達成することはまったく可能ですか?

編集:私が使用しているCustomView'には、次 の制約があります。subview

_title = [[UILabel alloc]init];
_title.text = @"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:@[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:@[[KeepMin must:5]]]];

便利なライブラリ「KeepLayout」を使用しています。制約を手動で記述すると、1 つの制約に対して非常に多くの行が必要になりますが、メソッドは自明です。

UITableViewは次の制約があります。

_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:@[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:@[[KeepEqual must:0]]]];

_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;

に直接制約を設定する必要があるかどうかはわかりませんCustomView。CustomView の高さは、その中のUILabel「タイトル」の制約によって決定されると思います。

編集 2:別の調査の後、CustomView の高さと幅が正しく計算されているようですが、CustomView の上部は UITableView の上部と同じレベルにあり、スクロールすると一緒に移動します。

4

29 に答える 29

146

ここで同様の質問をして回答しました。要約すると、ヘッダーを一度追加し、それを使用して必要な高さを見つけます。次に、その高さをヘッダーに適用し、変更を反映するためにヘッダーを 2 回設定します。

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.header = [[SCAMessageView alloc] init];
    self.header.titleLabel.text = @"Warning";
    self.header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long.";

    //set the tableHeaderView so that the required height can be determined
    self.tableView.tableHeaderView = self.header;
    [self.header setNeedsLayout];
    [self.header layoutIfNeeded];
    CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    //update the header's frame and set it again
    CGRect headerFrame = self.header.frame;
    headerFrame.size.height = height;
    self.header.frame = headerFrame;
    self.tableView.tableHeaderView = self.header;
}

複数行のラベルがある場合、これは各ラベルの preferredMaxLayoutWidth を設定するカスタム ビューにも依存します。

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}

またはおそらくより一般的に:

override func layoutSubviews() {
    super.layoutSubviews()  
    for view in subviews {
        guard let label = view as? UILabel where label.numberOfLines == 0 else { continue }
        label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame)
    }
}

2015 年 1 月の更新

残念ながら、これはまだ必要なようです。レイアウトプロセスの迅速なバージョンは次のとおりです。

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header

これを UITableView の拡張機能に移動すると便利であることがわかりました。

extension UITableView {
    //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again
    func setAndLayoutTableHeaderView(header: UIView) {
        self.tableHeaderView = header
        self.tableHeaderView?.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            header.widthAnchor.constraint(equalTo: self.widthAnchor)
        ])
        header.setNeedsLayout()
        header.layoutIfNeeded()
        header.frame.size =  header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        self.tableHeaderView = header
    }
}

使用法:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)
于 2015-01-23T02:38:34.157 に答える
26

制約を (コードで) 使用してヘッダー ビューを追加できませんでした。ビューに幅や高さの制約を与えると、次のメッセージが表示されてクラッシュします。

 "terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."

ストーリーボードのビューをテーブル ビューに追加すると、制約が表示されず、ヘッダー ビューとして正常に動作するため、ヘッダー ビューの配置は制約を使用して行われていないと思います。その点で、通常のビューのように動作しないようです。

幅は自動的にテーブル ビューの幅になります。設定する必要があるのは高さだけです。原点の値は無視されるため、それらに何を入力しても問題ありません。たとえば、これは正常に機能しました (rect の 0,0,0,80 と同様):

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;
于 2013-05-09T23:30:08.533 に答える
18

ここでは多くのメソッドが不要なことを行っているのを見ましたが、ヘッダー ビューで自動レイアウトを使用するためにそれほど多くの必要はありません。xib ファイルを作成し、制約を設定して、次のようにインスタンス化するだけです。

func loadHeaderView () {
        guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else {
            return
        }
        headerView.autoresizingMask = .flexibleWidth
        headerView.translatesAutoresizingMaskIntoConstraints = true
        tableView.tableHeaderView = headerView
    }
于 2016-11-21T11:31:20.430 に答える
7

別の解決策は、ヘッダー ビューの作成を次のメイン スレッド呼び出しにディスパッチすることです。

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

    dispatch_async(dispatch_get_main_queue(), ^{
        _profileView = [[MyView alloc] initWithNib:@"MyView.xib"];
        self.tableView.tableHeaderView = self.profileView;
    });
}

注: 読み込まれたビューの高さが固定されている場合のバグを修正しました。ヘッダーの高さがそのコンテンツのみに依存する場合、私は試していません。

編集 :

この関数を実装し、viewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}
于 2015-05-05T13:34:37.317 に答える
5

このソリューションを拡張http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/テーブル フッター ビュー:

@interface AutolayoutTableView : UITableView

@end

@implementation AutolayoutTableView

- (void)layoutSubviews {
    [super layoutSubviews];

    // Dynamic sizing for the header view
    if (self.tableHeaderView) {
        CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect headerFrame = self.tableHeaderView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != headerFrame.size.height) {
            headerFrame.size.height = height;
            self.tableHeaderView.frame = headerFrame;
            self.tableHeaderView = self.tableHeaderView;
        }

        [self.tableHeaderView layoutIfNeeded];
    }

    // Dynamic sizing for the footer view
    if (self.tableFooterView) {
        CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
        CGRect footerFrame = self.tableFooterView.frame;

        // If we don't have this check, viewDidLayoutSubviews() will get
        // repeatedly, causing the app to hang.
        if (height != footerFrame.size.height) {
            footerFrame.size.height = height;
            self.tableFooterView.frame = footerFrame;
            self.tableFooterView = self.tableFooterView;
        }

        self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height);
        [self.tableFooterView layoutIfNeeded];
    }
}

@end
于 2016-08-09T07:13:45.213 に答える
4

奇妙なことが起こります。systemLayoutSizeFittingSize は iOS9 ではうまく機能しますが、私の場合は iOS 8 では機能しません。したがって、この問題は非常に簡単に解決します。CGRectGetMaxY(yourview.frame) + パディングとして高さを挿入することにより、スーパー コール更新ヘッダー ビューの境界の後に、ヘッダーと viewDidLayoutSubviews の下部ビューへのリンクを取得するだけです。

UPD:これまでで最も簡単な解決策:したがって、ヘッダービューでサブビューを配置し、に固定します。そのサブビューで、サブビューを自動高さ制約で配置します。その後、すべてのジョブをオートレイアウトに渡します(計算は不要です)

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame);
    self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height);
    self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}

その結果、サブビューは必要に応じて拡大/縮小し、最後にviewDidLayoutSubviewsを呼び出します。ビューの実際のサイズがわかった時点で、headerView の高さを設定し、再割り当てして更新します。魔法のように動作します!

フッタービューでも機能します。

于 2016-06-01T09:23:34.690 に答える
4

systemLayoutSizeFittingSizeメソッドを使用して、autolayout にサイズを提供することができます。

これを使用して、アプリケーションのフレームを作成できます。この手法は、autolayout を内部的に使用するビューのサイズを知る必要がある場合に有効です。

迅速なコードは次のようになります

//Create the view
let tableHeaderView = CustomTableHeaderView()

//Set the content
tableHeaderView.textLabel.text = @"Hello world"

//Ask auto layout for the smallest size that fits my constraints    
let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

//Create a frame    
tableHeaderView.frame = CGRect(origin: CGPoint.zeroPoint, size: size)

//Set the view as the header    
self.tableView.tableHeaderView = self.tableHeaderView

またはObjective-Cで

//Create the view
CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero];

//Set the content
header.textLabel.text = @"Hello world";

//Ask auto layout for the smallest size that fits my constraints
CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];

//Create a frame
header.frame = CGRectMake(0,0,size.width,size.height);

//Set the view as the header  
self.tableView.tableHeaderView = header

この特定の例では、サブクラスで requiresConstraintBasedLayout をオーバーライドするとレイアウト パスが実行されますが、このレイアウト パスの結果は無視され、システム フレームは tableView の幅と高さ 0 に設定されることにも注意してください。

于 2014-11-30T11:45:30.817 に答える
4

以下は私のために働いた。

  1. UIViewヘッダー ビューとしてプレーン オールドを使用します。
  2. それにサブビューを追加しますUIView
  3. サブビューで自動レイアウトを使用する

私が見る主な利点は、フレーム計算を制限することです。UITableViewApple は、これを簡単にするために の API を実際に更新する必要があります。

SnapKit を使用した例:

let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60))
layoutView.backgroundColor = tableView.backgroundColor
tableView.tableHeaderView = layoutView

let label = UILabel()
layoutView.addSubview(label)
label.text = "I'm the view you really care about"
label.snp_makeConstraints { make in
    make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15))
}
于 2016-05-19T17:57:04.143 に答える
0

私のアプローチを共有します。

UITableView+XXXAdditions.m

- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock {
      CGFloat containerViewHeight = 0;
      UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
      [backgroundView addSubview:tableHeaderView];
      layoutBlock(tableHeaderView, &containerViewHeight);

      backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight);

      self.tableHeaderView = backgroundView;
}

使用法。

[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) {
    *containerViewHeight = 170;

    [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) {
      make.top.equalTo(@20);
      make.centerX.equalTo(@0);
      make.size.mas_equalTo(CGSizeMake(130, 130));
    }];
  }];
于 2016-12-10T13:16:51.740 に答える
0

Xamarin ユーザーの場合:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableviewHeader.SetNeedsLayout();
    TableviewHeader.LayoutIfNeeded();

    var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height;
    var frame = TableviewHeader.Frame;
    frame.Height = height;
    TableviewHeader.Frame = frame;
}

テーブルビューのヘッダー ビューに TableviewHeader という名前を付けたとします。

于 2018-02-12T11:29:13.673 に答える
0

これがあなたの中でできる方法ですUIViewController

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if headerView.frame.size.height == 0 {
      headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20
      let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height

      headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height)
    }
  }
于 2018-02-27T14:43:27.587 に答える