web-dev-qa-db-ja.com

UIScrollViewスクロール可能なコンテンツサイズのあいまいさ

開発者の皆さん、私はInterface Builder(Xcode 5/iOS 7)のAutoLayoutで問題を抱えています。それは非常に基本的で重要なので、私は誰もがこれが正しく機能する方法を知っているべきだと思います。これがXcodeのバグであれば、それは重大な問題です。

だから、私がこのようなビュー階層を持っているときはいつでも私は問題にぶつかる:

>UIViewController
>> UIView
>>>UIScrollView
>>>>UILabel (or any other comparable UIKit Element)

UIScrollViewは、例えば、各辺から50px(問題なし)という強固な制約を有する。次にUILabelにTop Space制約を追加します(問題ありません)(ラベルの高さ/幅を固定することもできますが、何も変更されませんが、Labelの固有サイズのため不要になります)。

UILabelに末尾の制約を追加すると問題が発生します。

例:末尾のスペース:スーパービュー= 25

今2つの警告が発生します - そして私は理由がわかりません:

A) スクロール可能なコンテンツサイズのあいまいさ(スクロールビューのスクロール可能なコンテンツの高さ/幅があいまいです)

B) 見当違いのビュー(予想されるラベル:x = -67実際:x = 207)

私はあなたがダウンロードすることができる新しくて新しいプロジェクトでこの最小の例をしました、そして私はスクリーンショットを添付しました。ご覧のとおり、Interface Builderは、LabelがUIScrollViewの境界(オレンジ色の破線の四角形)の外側に配置されることを想定しています。問題解決ツールを使用してラベルのフレームを更新すると、そのフレームはすぐそこに移動します。

注意:UIScrollViewをUIViewに置き換えた場合、動作は予想通りです(ラベルのフレームは正しく、制約に従って)。そのため、UIScrollViewに問題があるか、重要なことを見逃しています。

IBが提案しているようにラベルのフレームを更新せずにアプリケーションを実行すると、それはまっすぐに配置され、UIScrollViewはスクロール可能になります。フレームを更新した場合、ラベルは見えなくなり、UIScrollViewはスクロールしません。

オビ=ワン・ケノービを助けて!なぜあいまいなレイアウトですか?なぜ見当違いのビュー?

ここからサンプルプロジェクトをダウンロードして、何が起こっているのかを理解できるか試してみてください。 https://github.com/Wirsing84/AutoLayoutProblem

Illustration of the problem in Interface Builder

439
Wirsing

注:iOS> 10.0では、これはテストされていません。したがって、特定の状況では解決方法が不完全になる可能性があります。

だから私はちょうどこのように整理しました:

  1. UIScrollViewadd a UIViewの中(我々はそれをcontentViewと呼ぶことができます);

  2. このcontentViewでは、上下左右の余白を0に設定(もちろんscrollViewであるsuperViewから); 縦方向と横方向の中央揃えも設定;

終了

これで、すべてのビューをそのcontentViewに追加でき、contentSizescrollViewcontentViewに従って自動的にサイズ変更されます。

更新:

いくつかの特別な場合は、この video によって投稿された @Sergio によって以下のコメントでカバーされます。

947
Matteo Gobbi

このエラーが原因で追跡に時間がかかりました。最初はScrollViewのサイズを固定しようとしましたが、エラーメッセージには明らかに「コンテンツサイズ」と表示されます。 ScrollViewの上部から固定したものもすべて下部に固定されていることを確認しました。そのようにScrollViewはすべてのオブジェクトと制約の高さを見つけることによってそのコンテンツの高さを計算することができます。これはあいまいなコンテンツの高さを解決し、幅はかなり似ています...最初はScrollViewの中心にXを置いていましたが、オブジェクトをScrollViewの横に固定する必要もありました。 iPhone 6の方が画面が広くなる可能性があるため、これは嫌いですが、「あいまいなコンテンツの幅」エラーは解消されます。

93
self.name

それは私の自己回答式の質問に対する答えです UIScrollView + Centered View + Ambigous Scrollable Content Size +多くのiPhoneサイズ

しかし、それはあなたのケースも完全にカバーしています!

だから、これは最も簡単なケースのための初期状態です:

  1. すべてのエッジに0の制約を持つscrollView
  2. 幅と高さを固定制約付きのボタン中央揃えの水平方向と垂直方向
  3. そして、もちろん - 迷惑な警告Has ambiguous scrollable content widthHas ambiguous scrollable content height

1

私たちがしなければならないことは、すべてです。

  • 2つの追加の制約を追加します。たとえば、ビューの末尾および/またはスペースに "0"を追加します(下のスクリーンショットの例を見てください)。

重要:あなたは末尾および/または下制約を追加する必要があります。 「一流でトップ」ではない - それはうまくいかない!

2

あなたは私の example project でそれをチェックすることができます。

P.S.

論理によると - このアクションは「矛盾する制約」を引き起こすはずです。しかし、違います。

なぜそれが機能するのか、そしてXcodeがどの制約がより優先順位が高いのかを検出するのかはわかりません(これらの制約を明確にするために優先順位を設定していないため)。誰かが説明してくれてありがたいです、なぜそれが以下のコメントでうまくいくのか。

60
skywinder

下記のようにUIViewname__のサブビューとしてUIScrollViewname__を作成する必要があります。

  • UIViewControllername__
  • UIView'メインビュー'
    • UIScrollViewname __
      • UIView'コンテナビュー'
        • [あなたのコンテンツ]

2番目のステップはUIScrollViewname__制約を作成することです。これをsuperViewの上下左右の制約で行いました。

次にUIScrollViewname__のコンテナーの制約を追加しましたが、ここから問題が始まります。同じ拘束(上、下、前、後)を設定すると、ストーリーボードに警告メッセージが表示されます。

「あいまいなスクロール可能なコンテンツの幅があります」および「あいまいなスクロール可能なコンテンツの高さがあります」

上記と同じ制約を続けて、メインビューとの関係でEqual HeightEqual Widthコンテナビューに追加するだけです。 しない あなたのUIScrollViewname__。つまり、コンテナビューの制約はUIScrollViewname__のスーパービューに関連付けられています。

その後、ストーリーボードに警告が表示されなくなり、サブビューに制約を追加し続けることができます。

47
Thiago Monteiro

iOS 12、Swift 4

autolayoutを使用する最も簡単な方法:

  1. UIScrollViewを追加して0,0,0,0をsuperview(またはご希望のサイズ)に固定します
  2. ScrollViewにUIViewを追加し、0,0,0,0を4辺すべてに固定して、horizontallyverticallyを中央に配置します。
  3. サイズインスペクタで、bottomalign center Yの優先度を250に変更します。(水平スクロールの場合はtrailingalign center Xを変更します)
  4. このビューに必要なすべてのビューを追加してください。 忘れないでください 一番下のビューに下部の制約を設定します。
24

私はついにこれを考え出しました、私はこれが理解しやすいと思います。

前述のようにスクロールビューにベースUIViewを「コンテンツビュー」として追加してスクロールビューと同じサイズにし、この6つのパラメータを設定することで、この警告を回避することができます。

enter image description here

ご覧のとおり、合計6つのパラメーターが必要です。これは..通常の状況下では、2つの制約が重複していますが、これはこのストーリーボードエラーを回避するための方法です。

24
William T.

遅れるかもしれませんが、次の解決策 追加コードなしでこの種の問題を解決します ストーリーボードを使用するだけです。

あなたのコンテンツビューのために、あなたはscrollviewと これはコンテンツビューフレームを変更しないでしょう のために先行/末尾/上/下のスペースに対する制約を設定する必要があります: enter image description here

もちろん、スクロールビューでコンテンツのサイズがわかるように、コンテンツビューに対して追加の制約を作成する必要があります。たとえば、固定の高さと中心のxを設定します。

お役に立てれば。

15
Borzh

スクロールビューを機能させ、自動レイアウトで完全にスクロールできるようにする方法については、このチュートリアルとポイントチュートリアルをご覧ください。今、まだ私を困惑させているのは、スクロールビューのコンテンツサイズが常に必要なサイズよりも大きいためです。

http://samwize.com/2014/03/14/how-to-use-uiscrollview-with-autolayout/ /

6
Mike Simz

下記を参照してください。スクロールビュー内のコンテンツビュー垂直方向と水平方向の中央集中。あいまいさのエラーが発生しました。コンテンツビューからscrollviewに追加された2つが1)コンテナへの.buttonスペースであることを確認してください。

スクロールにおけるこれらの制約は、コンテンツビューの高さまたは幅の後にどれだけスクロールできるかということです。

enter image description here

それはあなたを助けるかもしれません。

3
Ravi Kumar

私にとってcontentViewを追加することは提案されたように実際にはうまくいきませんでした。さらに、ビューを追加したためにオーバーヘッドが発生します(ただし、これは大きな問題ではありません)。私にとって一番効果的だったのは、scrollViewに対して あいまいさチェックを無効にする にすることでした。すべてがうまくレイアウトされているので、私のような単純なケースでは問題ないと思います。しかし、あなたのscrollViewに対する他の制約が壊れた場合、Interface-Builderはnot警告をします)ことに注意してください。

enter image description here

2
Nick Podratz

@Matteo Gobbiの答えは完璧ですが、私の場合はスクロールビューがスクロールできません。 " align center Y "を削除して " height> = 1 "を追加すると、スクロールビューはスクロール可能になります

2
ygweric

選択したビューに[自動レイアウトの問題を解決]> [不足している制約を追加]を使用して、ビューのこの種の問題を解決しました enter image description here

次の2つの制約が私の問題を解決します。

trailing = Stack View.trailing - 10
bottom = Stack View.bottom + 76

uIScrollViewの末尾、末尾は末尾です。

2
Linh Dao

スクロールしないでuiscrollviewに苦しんでいる人はあなたの最後のビューの下のレイアウト(これはあなたのコンテンツビューの内側にある)であなたのコンテンツビューの下の制約を設定するだけです。中心Y拘束を削除することを忘れないでください。

他のすべての制約は上で定義したものと同じです。 Scrollviewは、そのコンテンツビューから最大の高さを取得することだけを考慮しており、最後のビューの下部の制約として設定しています。つまり、Scrollviewは自動的にコンテンツオフセットを変更します。

私の場合、最後のビューはno of linesプロパティ= 0(コンテンツに応じて自動的に高さを調整)でUILableでしたので、uilableの高さは動的に増加し、最終的にuilableの下部レイアウトがコンテンツビューの下部と揃うためスクロール可能領域が増えます。これにより、scrollviewはコンテンツのオフセットを増やすように強制されます。

import UIKit

class ViewController: UIViewController {

    //
    let scrollView: UIScrollView = {

        let sv = UIScrollView()
        sv.translatesAutoresizingMaskIntoConstraints = false
        return sv
    }()

    let lipsumLbl: UILabel = { // you can use here any of your subview

        let lbl = UILabel()
        lbl.translatesAutoresizingMaskIntoConstraints = false
        lbl.text = """
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eleifend nisi sit amet mi aliquet, ut efficitur risus pellentesque. Phasellus nulla justo, scelerisque ut libero id, aliquet ullamcorper lectus. Integer id iaculis nibh. Duis feugiat mi vitae magna tincidunt, vitae consequat dui tempor. Donec blandit urna nec urna volutpat, sit amet sagittis massa fringilla. Pellentesque luctus erat nec dui luctus, sed maximus urna fermentum. Nullam purus nibh, cursus vel ex nec, pulvinar lobortis leo. Proin ac libero rhoncus, bibendum lorem sed, congue sapien. Donec commodo, est non mollis malesuada, nisi massa tempus ipsum, a varius quam lorem vitae nunc.

        Cras scelerisque nisi dolor, in gravida ex ornare a. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas vitae nisl id erat sollicitudin accumsan sit amet viverra sapien. Etiam scelerisque vulputate ante. Donec magna nibh, pharetra sed pretium ac, feugiat sit amet mi. Vestibulum in ipsum vitae dui vehicula pulvinar eget ut lectus. Fusce sagittis a elit ac elementum. Fusce iaculis nunc odio, at fermentum dolor varius et. Suspendisse consectetur congue massa non gravida. Sed id elit vitae nulla aliquam euismod. Pellentesque accumsan risus dolor, eu cursus nibh semper id.

        Vestibulum vel tortor tellus. Suspendisse potenti. Pellentesque id sapien eu augue placerat dictum. Fusce tempus ligula at diam lacinia congue in ut metus. Maecenas volutpat tellus in tellus maximus imperdiet. Integer dignissim condimentum lorem, id luctus nisi maximus at. Nulla pretium, est sit amet mollis eleifend, tellus nulla viverra dolor, ac feugiat massa risus a lectus. Pellentesque ut pulvinar urna, blandit gravida ipsum. Nullam volutpat arcu nec fringilla auctor. Integer egestas enim commodo, faucibus neque ac, rutrum magna. Vivamus tincidunt rutrum auctor. Cras at rutrum felis. Fusce elementum lorem ut pharetra venenatis.
        """
        lbl.textColor = .darkGray
        lbl.font = UIFont.systemFont(ofSize: 16)
        lbl.numberOfLines = 0
        return lbl
    }()

    //======================================================================//
    //
    // MARK:- viewDidLoad
    //
    override func viewDidLoad() {
        super.viewDidLoad()

        setupViews()
        setupAutoLayout()
    }

    func setupViews() {

        title = "ScrollView Demo"
        view.backgroundColor = .white
        view.addSubview(scrollView)
        scrollView.addSubview(lipsumLbl)
    }

    func setupAutoLayout() {

        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

        lipsumLbl.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
        lipsumLbl.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
        lipsumLbl.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        lipsumLbl.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        lipsumLbl.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    }
}

出力:

enter image description here

1
iAj

ContentView(UView)をUIScrollView内のコンテナーとして使用し、スーパービュー(top)およびbottomのエッジ(trailingleadingUIScrollViewcontentView)に固定すると、equalwidthおよびequalheightがスーパービューになります。これらは制約です。そして、メソッドの呼び出し:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    self.scrollView.contentSize = self.contentView.frame.size;
}

私のために問題を解決しました。

1
Spiridon Kopicl

YouTubeでビデオを作りました
Xcodeのストーリーボードのみを使用してStackViewをスクロールします

ここには2種類のシナリオが現れると思います。

ScrollView内のビュー -

  1. 固有のコンテンツサイズはありません(例:UIView
  2. 固有のコンテンツサイズがありますか(例:UIStackView

どちらの場合も垂直方向にスクロール可能なビューの場合は、次の制約を追加する必要があります。

  1. 上、左、下、右から4つの拘束。

  2. スクロールビューと同じ幅(水平方向のスクロールを停止するため)

あなたは彼自身の本質的な内容の高さを持っているビューに対して他のいかなる制約も必要としません。

enter image description here


本質的なコンテンツの高さがないビューの場合は、高さの制約を追加する必要があります。高さの制約がscrollViewの高さよりも大きい場合にのみ、ビューはスクロールします。

enter image description here

1

Interface BuilderのサイズインスペクタでViewController(UIScrollViewを保持しているもの)のサイズをFreeformに設定すればすべてうまくいくはずです。

含まれるUIViewcontrollerのためのInterface BuilderのSizeインスペクタでのFreeform設定

0
nico

垂直スクロール

  1. スーパービューに、制約0,0,0,0を持つUIScrollViewを追加します。
  2. Superviewに0,0,0,0の制約を付けてScrollViewにUIViewを追加します。
  3. UIViewと同じ幅の制約をUIScrollViewに追加します。
  4. UIViewに高さを追加します。
  5. UIViewに制約付きの要素を追加します。
  6. 一番下の要素に最も近い要素については、UIViewの一番下に制約があることを確認してください。
0
BitCon

スクロールビューの上から下に向かって連続した制約の行を作成する方法があることを確認するだけです。 [-X-X-X]

私の場合 - 水平スクロールスクロールビュー - Appleのテクニカルノート はあなたにこれを教えていませんが、私はスクロールビューのそれぞれの子に幅と高さの制約を追加する必要もありました。

0
johnnyMac

[XCode 7.2およびiOS 9.2用にテスト済み]

ストーリーボードのエラーと警告を抑制したのは、スクロールビューとコンテンツビュー(私の場合はスタックビュー)の 固有のサイズ Placeholder に設定することでした。この設定は、ストーリーボードのサイズインスペクタにあります。そしてそれは言います - Interface Builderで編集している間、デザイン時の本質的なコンテンツサイズを設定することはビューに影響するだけです。実行時には、ビューはこの固有のコンテンツサイズを持ちません。

だから、これを設定しても問題ないと思います。

注:ストーリーボードでは、scrollviewのすべての端をスーパービューに、stackviewのすべての端をscrollviewに固定しました。私のコードでは、スクロールビューとスタックビューの両方で translateAutoresizingMaskIntoConstraints をfalseに設定しました。そして私は内容のサイズについて言及していません。スタックビューが動的に大きくなると、ストーリーボードで設定されている制約によってスタックが確実にスクロール可能になります。

ストーリーボードの警告は私を怒らせていたので、警告を抑えるためだけに物事を水平方向または垂直方向に集中させたくはありませんでした。

0
gtl_pm

Swift 4+アプローチ:

1)UIScrollViewの上下左右の余白を0に設定します。

2)UIScrollView内にUIViewを追加し、上下左右の余白を0に設定します(UIScrollViewの余白と同じ)。

3)最も重要な部分は幅と高さを設定することです ここで高さの制限は低い優先順位を持つべきです

private func setupConstraints() {
    // Constraints for scrollView
    scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
    scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
    scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
    // Constraints for containerView
    containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
    containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
    containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
    containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
    containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
    let heightConstraint = containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
    heightConstraint.priority = UILayoutPriority(rawValue: 250)
    heightConstraint.isActive = true
}
0
Gentian Sadiku

私は同じエラーを受けていました..私は以下をやっています

  1. 表示(スーパービュー)
  2. スクロールビュー0,0,600,600
  3. ScrollView内のUIView:0,0,600,600
  4. UIViewにはイメージビュー、ラベルが含まれています

ScrollView(2)の先頭/末尾/上/下を追加し、次にUIView(3)を追加します。

表示(1)と表示(3)を選択して、身長と体重を等しく設定します。

私は役立ちますビデオをやっています:

https://www.youtube.com/watch?v=s-CPN3xZS1A

0
Vinod Joshi

ここに示すように、私がしたのは別のコンテンツビューを作成することです。

コンテンツビューはフリーフォームであり、すべてのサブビューを追加することができます。コンテンツビューには独自の制約があります。

UIScrollViewがメインのView Controllerに追加されます。

プログラム的にIBOutletを介してリンクされているcontentViewをクラスに追加し、UIScrollViewのcontentViewを設定します。

UIScrollView with ContentView via Interface Builder

0
Vlad

右側のスクロールバーのスクロールバーに気づいてもコンテンツが移動しないという動作を誰かが受けている場合、この事実はおそらく考慮する価値があります。

スクロールビューのコンテンツとスクロールビューの外側のオブジェクトとの間の制約を使用して、スクロールビューのコンテンツの固定位置を指定し、そのコンテンツがスクロールビューの上に浮かぶように表示することもできます。

それはAppleの ドキュメンテーション からのものです。たとえば、誤って一番上のLabel/Button/Img/Viewをスクロール領域の外側のビュー(おそらくヘッダーまたはscrollViewの真上にあるもの)に固定した場合は、contentView全体を所定の位置に固定します。

0
Dave G

前の回答で述べたように、スクロールビュー内にカスタムビューを追加する必要があります。

The custom view is the ContentView marked in the image

すべてのサブビューをコンテンツビューに追加します。ある時点で、スクロールコンテンツビューにあいまいなコンテンツサイズの警告が表示されます。コンテンツビューを選択して[自動レイアウトの問題を解決]ボタン(IBレイアウトの右下にある)をクリックし、[追加] "オプション".

enter image description here

これからプロジェクトを実行すると、スクロールビューは自動的にそのコンテンツサイズを更新します。追加のコードは必要ありません。

0
supergegs