web-dev-qa-db-ja.com

スクロールビュー内の動的サイズのコントローラーを使用したコンテナービューのサイズ変更

UIScrollView内に動的な高さを持つコントローラーを使用してコンテナービューを作成し、自動レイアウトを使用して自動的にサイズ変更しようとしています。

Storyboard illustrating the setup

View Controller Aはスクロールビューであり、コンテナビューと以下のコンテンツが含まれています。

View Controller Bは、動的なサイズにしたいView Controllerであり、View Controller AのScroll Viewですべてのコンテンツをフルハイトで表示します。

Aのコンテナビューのサイズを自動的に設定するためにBの動的サイズを取得する際にいくつかの問題があります。ただし、Aのコンテナビューに高さの制約を設定すると Container View in A to for example 250

View Controller Bの高さも250の場合、予想される出力になります。高さ1000でも正常に機能するため、私の知る限り、すべての自動レイアウト制約が適切に設定されています。残念ながら、実際には高さは動的である必要があるため、高さの制約をまったく設定しないようにします。

View Controller Bの設定がコンテンツに応じて自動的に更新されるように設定できるかどうか、または見逃した他のトリックがあるかどうかはわかりません。どんな助けでも大歓迎です!

高さの制約を設定せずに、View Controller Bのサイズに応じてAのContainer Viewのサイズを変更する方法はありますか?

うん、あります。私は自分のプロジェクトの1つでそのような動作を達成することができました。

Interface Builderでルートビューに設定された固定フレームを模倣する制約を追加しないようにシステムに指示するだけです。これを行う最適な場所は、埋め込みセグエがトリガーされたときのコンテナビューコントローラです。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // You might want to check if this is your embed segue here
    // in case there are other segues triggered from this view controller. 
    segue.destinationViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
}

重要:

コンテナにロードするビューが上から下に制約されていることを確認する必要があります。垂直制約のいずれかの優先度を1000未満の値に設定する必要があります(常に下部制約を使用することをお勧めしますこれが必要です。そうしないと、Interface Builderが文句を言うので、正当な理由があります。

設計時には、ルートビューのサイズ(高さ)は固定されています。これで、すべてのサブビューの高さが固定され、すべてが同じ優先度の固定制約に接続されている場合、固定ルートビューの高さがサブビューと垂直制約の合計高さと一致しない限り、これらすべての要件を満たすことはできません。いずれかの制約の優先度を999に下げると、Interface Builderはどの制約を解除するかを認識します。ただし、実行時に-translatesAutoresizingMaskIntoConstraintsプロパティが上記のように設定されている場合、ルートビューの固定フレームはなくなり、システムは代わりに999の優先度制約を使用します。

Interface Builder Screenshot

97
Mischa

@Mischaの答えから、これを行うコンテンツに応じてcontainerViewの高さを動的にすることができました:

ContainerViewのviewControllerに次を記述します。

  override func loadView() {
    super.loadView()
    view.translatesAutoresizingMaskIntoConstraints = false
  }

また、IBの垂直方向の制約がすべて設定されていることに注意してください。これにより、View Controllerの外部からview.translatesAutoresizingMaskIntoConstraints = falseを設定する必要はありません。

私の場合、コンテナのサイズをviewController内のtableViewに変更しようとしました。 tableViewには、そのスーパービューに応じて柔軟な高さがあるため(IBではすべて問題ありません)、これを行うことでコードの垂直方向の制約を完了しました。

  @IBOutlet private var tableView: UITableView! {
    didSet {
      tableView.addConstraint(tableViewHeight)
    }
  }
  private lazy var tableViewHeight: NSLayoutConstraint = NSLayoutConstraint(item: self.tableView, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0)

そして、tableviewのcontentSizeの高さを観察し、必要に応じてtableViewHeight制約の定数をプログラムで調整します。

23
acecilia

Swift 4、Xcode 9

受け入れられた答えだけでは問題を解決できませんでした。

私の階層:ScrollView->コンテンツビュー(UIView)->ビュー|コンテナビュー|その他のビュー。

ScrollViewとContainerの両方を動的に調整するには、次の制約を追加する必要がありました。

  1. ScrollView:トップ、ボトム、リーディング、スーパービューへのトレーリング(セーフエリア)
  2. コンテンツビュー:上部、下部、先頭、末尾、ScrollViewと等しい幅、および同じ高さ(低い優先度の制約:250)
  3. Views:通常の自動レイアウト制約。
  4. コンテナビュー:上部、下部から隣接ビュー、セーフエリアへのリードおよびトレイリング。
  5. コンテナビューの埋め込みVC:すべての制約が垂直方向に接続され、下部の制約が低い優先度に設定されていますが、コンテンツビューの等高のものよりも大きい!この場合、優先順位5がトリックを行いました。
  6. view.translatesAutoresizingMaskIntoConstraints = falseprepareForSegue()またはloadView()に設定します。他の回答が述べられています。

これで、自動サイズ変更スクロールビュー内に動的に調整可能なコンテナビューができました。

13
Teodor Ciuraru

この問題に対する@Mischaの優れたソリューションに基づいて、コンテナが動的にサイズ設定されたUITableViewを埋め込む場合、contentSizeおよび intrinsicContentSize をオーバーライドすること、およびtranslatesAutoresizingMaskIntoConstraints = falseを受け入れられた答え。

親ビューがコンテナービューを読み込んで設定すると、各UITableViewCellsの固有の高さは0であると推測されるため、次のUITableViewDelegateメソッドは:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

呼び出されません。これにより、UITableViewはコンテンツがないように見えます。

固有のコンテンツサイズをオーバーライドして実際のコンテンツサイズを返すということは、テーブルビューがコンテンツに必要なサイズで表示されることを意味します。

EmilioPeláezによる素晴らしい 記事 は、このトピックについてより深く掘り下げています。

2
Stuart Pattison