web-dev-qa-db-ja.com

AutoScrolloutを使用してUIScrollviewに制約を設定するにはどうすればよいですか?

混合および純粋な自動レイアウトアプローチ のさまざまなソリューションを試して2日間を費やして、自動レイアウト前のささいなスクロールビューのセットアップを達成しました。私はこれをほとんどストーリーボードで設定しています(まあ、それはただの方法です)。

だからここに助けを求めます。

ビューツリー:

UIView
-UIView
-UIView
..-UIScrollview
...-UIButton
...-UIButton
...-UIButton

ボタンは水平にスクロールすることになっています(左から右へ、またはその逆)。誰かplease純粋なAutolayoutを使用してこれを達成するための制約の設定方法を教えてください。

-

私は次のような混合アプローチを試みました:

UIView
- UIView
- UIView
..-UIScrollview
...-UIView (contentview)
....-UIButton
....-UIButton
....-UIButton

...およびAppleのTechNoteに従って、contentviewおよびtranslatesAutoresizingMaskIntoConstraints設定の固定幅と高さの制約を設定します。ボタンとスクロールビューは、制約を使用して設定されます。これはスクロールビューのスクロールを取得します(はい)が、残念ながらスクロールしすぎです!私が知る限り、スクロールの幅は、コンテンツビューを設定した値の2倍になります。

contentviewを使用する場合と使用しない場合の両方で、純粋な自動レイアウトアプローチも試しました。 AllビューはtranslatesAutoresizingMaskIntoConstraints=NOです(self.viewを除く)。ボタンには固定幅/高さの制約があり、スクロールビューの4つのすべての端に固定されています。何もスクロールしません。

だから、なぜ私はそれを正しく動作させることができないのか完全に困惑しています。どんな助けでも大歓迎です、そして、あなたが他の情報が必要であるならば、尋ねてください!

更新されたスクリーンショット-buttonZ制約:

enter image description here

EDIT @ Jamie Forrestそのため、ソリューションは最後のボタンの間違ったトレーリング制約であることが判明しました。 6441の代わりに、設定した値は負の-6441でした。トリッキーなことは、ストーリーボードで値を設定するときに、ピンツールバーに2つのオプションがあることです。

enter image description here

現在のキャンバス値は負(スクロールなし)であり、以下のオプションは正(スクロールをアクティブ化)です。これは私が愚かではないことを意味しますが、少なくとも半盲目だと思います。 私の防御のために、XCodeが「誤った」設定に対してエラーを表示しないことは多少不安になりませんか?

EDITED AGAINこれはおかしいです...末尾の値を-6441(スクロールなし)から6441対応スクロールに変更します。しかし、私の旧友である「コンテンツサイズが大きすぎます」が復活し、コンテンツサイズが本来の2倍になりました。正しいコンテンツスクロールを取得するための解決策は、末尾の制約をゼロに設定することでした! Storyboardで作業しているとき、これは明らかではありませんが、@ Infinity Jamesのコードを見れば、それは本来あるべきことです。

173
user1459524

ここに貼り付けたように、制約の正確な値と設定を確認するのは難しいので、間違ったスクリーンショットを見てもわからない。

セットアップの何が問題なのかを説明する代わりに、基本的な サンプルプロジェクト を作成しました。ビュー階層と制約のセットアップは、説明したものと非常によく似ています。水平スクロールは、Appleが テクニカルノート で説明している「Pure AutoLayout」アプローチを使用するサンプルプロジェクトで期待どおりに機能します。

また、元々、自動レイアウトをUIScrollViewname__で動作させるのに苦労しました。スクロールビューのすべてのアイテムをまとめて、最終的にスクロールビューのすべての側面にリンクし、AutoLayoutシステムがcontentSizeを決定できるようにする制約があることを確認することが重要です。フレームより大きいスクロールビュー。あなたのコードでそれをやろうとしているように見えますが、多分あなたはそこにcontentSizeを小さくしすぎている余分な制約がありました。

また、AutoLayoutとUIScrollviewで他の人が述べたように、contentSizeを明示的に設定しなくなったことにも注意してください。 AutoLayout Systemは、制約に基づいてcontentSizeを計算します。

また、 この電子ブックの章 は、このすべてがどのように機能するかを理解するのに非常に役立つことがわかりました。これがすべて役立つことを願っています。

65
Jamie Forrest

愚かなクラブへようこそ。私は創立者の一人です。 :D

垂直スクロールの場合:動作させる唯一の方法(iOS 8、Xcode 6、および純粋な自動レイアウト)は、スクロールビューに次の制約を追加することでした(すべてスーパービューに関連):

  • 等しい幅
  • 等しい高さ
  • 中心Yの位置合わせ
  • 中心Xの位置合わせ

私の構造:

  UIView
   - ScrollView
    - Subview
    - Subview
    - Subview
    - Subview
    - ...

これが最終結果です。

Demo

これがセットアップです:

Setup フルスクリーン

そして、これがプロジェクトです

うまくいけば、これで誰かが眠りにつく_AT 5 AMから救われることを願っています。 :D

48
backslash-f

簡単な自己完結型の例

質問に対する投票数が多いことと回答に対する投票数が少ないことから判断すると、人々はここで理解できる迅速な解決策を見つけていません。追加してみましょう。このプロジェクトは、Interface Builderで完全に実行される自己完結型の例です。 10分以内に作業ができるはずです。その後、学習した概念を自分のプロジェクトに適用できます。

enter image description here

元の質問は、スクロールボタンについて尋ねています。ここではUIViewsを使用していますが、好きなビューを表すことができます。ストーリーボードのスクリーンショットはこの形式の方がコンパクトなので、水平スクロールも選択しました。ただし、垂直スクロールの原理は同じです。

キーコンセプト

  • UIScrollViewは1つのサブビューのみを使用する必要があります。これは、スクロールするすべてのものを保持するコンテンツビューとして機能する「UIView」です。
  • コンテンツビューとスクロールビューのparentの水平スクロールの高さを等しくします。 (垂直スクロールに等しい幅)
  • すべてのスクロール可能なコンテンツに設定された幅があり、すべての面で固定されていることを確認してください。

新しいプロジェクトを開始する

単一のビューアプリケーションにできます。

絵コンテ

この例では、水平スクロールビューを作成します。 View Controllerを選択してから、Size InspectorでFreeformを選択します。幅を1,000、高さを300にします。これにより、ストーリーボード上にスクロールするコンテンツを追加する余地ができます。

enter image description here

スクロールビューの追加

UIScrollViewを追加して、4つの側面すべてをView Controllerのルートビューに固定します。

enter image description here

コンテンツビューの追加

UIViewをサブビューとしてスクロールビューに追加します。 これが重要です。スクロールビューに多くのサブビューを追加しようとしないでください。単一のUIViewを追加するだけです。これは、スクロールする他のビューのコンテンツビューになります。コンテンツビューをスクロールビューに4つの側面すべてに固定します。

enter image description here

等しい高さ

ドキュメントの概要で、 Command コンテンツビューとスクロールビュー親ビューの両方をクリックして、両方を選択します。次に、高さが等しくなるように設定します(Control コンテンツビューからスクロールビューにドラッグします)。 これも重要です。水平方向にスクロールしているため、スクロールビューのコンテンツビューは、この方法で設定しない限り、どのくらいの高さになるかわかりません。

enter image description here

注意:

  • コンテンツを垂直にスクロールさせる場合、コンテンツビューの幅をスクロールビューの親の幅に等しく設定します。

コンテンツの追加

3つのUIViewsを追加し、それらにすべての制約を与えます。すべてに8ポイントのマージンを使用しました。

enter image description here

制約:

  • 緑のビュー:上端、左端、下端を固定します。幅を400にします。
  • 赤いビュー:上端、左端、下端を固定します。幅を300にします。
  • 紫色のビュー:4つのエッジすべてを固定します。残りのスペース(この場合は268)に関係なく幅を作成します。

幅の制約を設定することも重要です。スクロールビューは、コンテンツビューの幅を知ることができます。

完成した

それで全部です。これでプロジェクトを実行できます。この回答の上部にあるスクロール画像のように動作するはずです。

垂直スクロールの場合、この例のすべての幅と高さの方向を入れ替えるだけです(テスト済みで動作中)。

さらなる研究

30
Suragch

ContentScrollは、UIScrollView内の制約を適用することで暗黙的に設定されます。

たとえば、UIView内にUIScrollViewがある場合、これは次のようになります(ご存じのとおりです)。

    UIView *containerView               = [[UIView alloc] init];
    UIScrollView *scrollView            = [[UIScrollView alloc] init];
    [containerView addSubview:scrollView];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.translatesAutoresizingMaskIntoConstraints    = NO;
    NSDictionary *viewsDictionary       = NSDictionaryOfVariableBindings(containerView, scrollView);

    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil
                                                                            views:viewsDictionary]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil

それにより、scrollViewがcontainerViewのサイズに合わせて設定されます(したがってcontainerViewは特定のサイズである必要があります)。

次に、次のようなボタンを保持するのに十分な大きさに暗黙的に設定することにより、UIScrollViewのcontentSizeを調整できます。

    UIButton *buttonA                   = [[UIButton alloc] init];
    UIButton *buttonB                   = [[UIButton alloc] init];
    UIButton *buttonC                   = [[UIButton alloc] init];
    [scrollView addSubview:buttonA];
    [scrollView addSubview:buttonB];
    [scrollView addSubview:buttonC];
    buttonA.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonB.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonC.translatesAutoresizingMaskIntoConstraints       = NO;

    viewsDictionary                     = NSDictionaryOfVariableBindings(scrollView, buttonA, buttonB, buttonC);

    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonA]-|"
                                                                       options:kNilOptions
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonA]-[buttonB]-[buttonC]-|"
                                                                       options:NSLayoutFormatAlignAllBaseline
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
9
Infinity James

UIScrollViewでAutoLayoutを使用することについて非常に多くの質問がありますが、無視する重要なポイントは、UIScrollViewの内部ビューがContent Viewに対して制約を作成することですが、 UIScrollView自体ではありません。 テクニカルノートTN2154 を参照してください。

UIScrollViewクラスは、境界のOriginを変更してコンテンツをスクロールします。これを自動レイアウトで機能させるために、スクロールビュー内の上端、左端、下端、および右端は、コンテンツビューの端を意味するようになりました。

次の図にそれを示します。 enter image description here

末尾のスペースは500ポイントであることがわかります。制約がUIScrollViewに対して行われた場合、ビューは配置されず、フレームを更新する必要があります。ただし、警告もエラーもありません。すべての制約がコンテンツビューに対するものだからです。

UIScrollViewは、内部ビューの制約に従ってコンテンツビューのサイズを計算します。 (例では、コンテンツサイズ:幅= 100(先頭のスペース)+ 200(ビューの幅)+ 500(末尾のスペース)、高さ= 131(上部の間隔)+ 200(高さ)+ 269(下部の間隔)

UIScrollViewでビューに制約を追加する方法:

  1. コンテンツビューでのビューの位置のイメージング。
  2. コンテンツビューの端に上、右、下、左のスペースを追加し、さらにこれらのビューの幅と高さも追加します。

そして、すべて完了です。

ScrollviewでAutoLayoutを処理する簡単な方法は、スクロールビュー内のすべてのサブビューを含むコンテナビューを追加することです。

結論:UIScrollViewを使用したAutoLayoutを理解するための重要なポイントは、内部ビューであり、UIScrollView自体ではなくコンテンツビューに対して制約を作成することです。

添付 サンプルコード

8
Danyun Liu

次のソリューションは、autolayoutを使用しcontentSizeを使用しないscrollViewで機能しました。

  1. ScrollViewをviewControllerにドラッグアンドドロップし、必要なスペースをカバーする制約を適用します。
  2. ドラッグアンドドロップIView scrollView内に配置し、scrollViewのスペース全体をカバーするようにし、scrollViewから上、左、右、下のスペースになるように制約を適用します。
  3. スクロールの必要性に応じて、内部ビューのheight(および水平スクロールが必要な場合は幅)を設定します。この部分は、必要に応じてコードから実行することもできます。
  4. クリティカル。ポイント(3)で高さを大きな値に設定したら、ポイント(2)に戻って、上、左、右、下の値を設定してくださいゼロに戻る Xcode (3)で高さを強制的に変更したときにそれらを変更しました。

これで完了です。これで、このビューに任意の数のコントロールを追加し、相互に関連する制約を適用できます(このビューがないと機能しないようです)。このビューを使用しない場合は、scrollViewに関連する(相互に関連しない)各コントロールに制約を適用する必要があります。


圧倒的なヒント..............

クリティカル。わかりやすくするために、UIScrollViewが1000幅および100の高さであるとします。 (実際、通常、これらの値は、デバイスの幅などに応じて、もちろん動的になります。しかし、今のところは、幅1000と高100とだけ言ってください。)水平スクロールをしているとしましょう。そのため、UIScrollView内にUIViewを配置します。 (これが「コンテンツビュー」です。)コンテンツビューの4つのすべての制約をスクロールビューに設定します。 それが間違っているように見える場合でも、それらをすべてゼロにします。コンテンツUIViewの高さを100に設定し、それを忘れます。さて、水平にスクロールしたいので、コンテンツビューの幅を1225に設定します。

コンテンツビューの幅は、親スクロールビューの幅よりも225大きいことに注意してください。大丈夫です。実際、あなたはそれをしなければなりません。ご了承ください

...するNOT末尾の幅を負の225に設定する...

幅を「一致」させる必要があると思います通常どおり。しかし、それを行うと、まったく機能しません。

先頭と末尾の数字をZEROに設定する必要があります(決して幅が「大きい」としても)

興味深いことに、実際には先頭/末尾の数値を任意の値(「50」と言ってください)に設定できます。これにより、バウンスのマージンが得られます。 (よく見かけます:試してみてください。)どちらかの端に負の値があると、「静かに壊れます」。

腹立たしいことに、Xcode(とにかく7.3.1のように)、

これらの値を「役立つ」ように負の数に設定します!

自動的にそれらを集計しようとするためです。もしそうなら、それは静かに壊れます。最初のインスタンスで4つの値をすべてゼロに設定します。また、コンテンツビューの幅を例の「1000」よりもはるかに広く設定します。


編集:私はほとんどの要件にUIScrollViewの代わりにUITableViewを使用することになりました。 tableViewのほうがはるかに柔軟で動的なようです。

5
zeeawan

私はあなたがthecontentSizeの問題に直面していると思います。 「純粋な」AutoLayoutアプローチを使用する場合のcontentSizeの処理方法について このブログ投稿 を確認してください。その要点は、制約がコンテンツサイズを暗黙的に定義することです。 AutoLayoutを使用する場合、決して明示的に設定しないでください。ブログ投稿の最後にサンプルプロジェクトを添付して、その仕組みを示しています。

4
Edward Huynh

このようにレイアウトを整理する必要があります
ViewControllerViewにはScrollViewが含まれ、ScrollViewにはContainerViewが含まれ、ContainerViewには2つのLabelsが含まれます
enter image description here

次に、ScrollViewがスクロールできるようにするために、3つの手順を実行します

  1. ScrollViewピン(上/右/下/左)をViewControllerViewに設定
    • ContainerViewピン(上/右/下/左)をScrollViewに設定
    • Horizontally in Containerを設定(do n'tVertically in Containerを設定)
    • Label1ピン(top/ right/left)to ContainerView
    • Label1 pin(right/left /bottom)to ContainerView andtopto Label1

enter image description here

ここはデモプロジェクトです

この助けを願っています

3
Phan Van Linh

テクニカルノートには、あなたが見たかもしれない部分があります。スクロールビューの端に固定された制約を使用して、スクロールビューのコンテンツサイズを暗黙的に設定できます。

以下に簡単な例を示します。スクロールビューが1つのビューを1つ持つストーリーボードを作成します。スクロールビューの制約を設定して、配置するビューのサイズに合わせます。

そのスクロールビュー内に単一のビューを追加します。制約を使用してそのビューのサイズを明示的に設定します(サイズがスクロールビューよりも大きいことを確認します)。

次に、内部ビューの4つのエッジをその親スクロールビューにロックする4つの制約をその内部ビューに追加します。これらの4つの制約により、コンテンツサイズが拡大して内部ビューに対応します。

複数のビューをスクロールビューに追加する場合、たとえば水平にレイアウトする場合、最初のサブビューの左側をスクロールビューの左側にロックし、サブビューを互いに水平にロックし、右側をロックしますスクロールビューの右側の最後のサブビューの側面。これらの制約は、スクロールビューのコンテンツサイズを拡張して、すべてのサブビューとその制約に対応させます。

3
Travis

あなたの質問が「フォーカスがあるときにキーボードの邪魔にならないように、垂直スクロールUIScrollViewに多数のUITextFieldsを配置する方法」の場合、最良の答えは次のとおりです。

しないでください。

代わりに、静的セルを含むUITableViewControllerを使用します。

このスクロールアウトの動作は無料で得られます。また、View ControllerがUINavigationController内に表示されている場合、すべてのコンテンツインセットは動作します。

3
Robert Atkins

IOS 8.4、Xcode 6.4で今日抱えている同様の問題

サブビューを含むcontentView(UIView)を含むスクロールビューを含むビューがあります。

すべてが自動レイアウトです。スクロールビューのエッジは、制約付きで親ビューのエッジに固定されます。コンテンツビューの端は、制約付きでスクロールビューの端に固定されます。

もともと、コンテンツビューはスクロールビューの全幅としてのサイズ変更を拒否していました。コンテンツビューに追加の制約を追加して、その幅を親スクロールビューと一致させる必要がありました。または、contentView.centerX == scrollView.centerX制約を設定できます。それらのいずれかがエッジを固定することに加えて、コンテンツビューを突然適切なサイズにしました。

// Either one of these additional constraints are required to get autolayout to correctly layout the contentView. Otherwise contentView size is its minimum required size
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .CenterX, relatedBy: .Equal, toItem: scrollView, attribute: .CenterX, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: scrollView, attribute: .Width, multiplier: 1.0, constant: 0.0))

フォームの視覚的制約を使用して、コンテンツビューの端をスクロールビューに固定します。

let cvConstraints = ["H:|[contentView]|", "V:|[contentView]|"]

ルーチンを使用して配列を反復処理し、scrollViewに追加します。

1
David

同様の問題に直面しました。私はすべてに制約を設定し、なぜそれがまだいくつかのサブビューのサイズを変更するのか疑問に思っていました。私の解決策はclipsToBoundsをYESに設定することでした。

1
i89

純粋な自動レイアウトのアプローチは見事に機能しますが、非自動レイアウトから移行する場合、セットアップするのは非常に苦痛です。私はそれを数回やったことがあり、いくつかの一般的なヒントがあります:

  • 小さく始める:ストーリーボードビューの再作成を意味する場合でも、いくつかの要素から始めてゆっくりとビューを構築し、いくつかの要素を追加した後にスクロールが動作することを確認します。
  • すべてに対してtranslatesAutoresizingMaskIntoConstraintsをオフにします。これが常に制約の競合の原因でした。
  • UIScrollView制約を適切に設定します。スクロールビューがすべての側で親ビューに接続されていることを確認します。そうでない場合は、まったく拡大しません。
1
Nick

私のように、サブビュー内で制約のない静的コンテンツを使用する場合は、次のようにできます。

override func viewDidLayoutSubviews() {
    scrollView.contentSize = CGSizeMake(320, 800)
}
1
Antzi

AutoLayoutビューの埋め込みScrollviewを使用して、表示画面のスクロールビューを中央に配置する方法の解決策を見つけるために何日も費やしました。

私はAutolayoutのみでそれをしようとして数日を費やし、近づきましたが、決して十分に近づきませんでした。したがって、最終的には、viewDidLoadで、画面ごとに3行のコードも追加する必要がありました。

以下の解決策を参照してください。

  1. スクロールビューを作成し、必要なオブジェクトを入力します
  2. 自動レイアウトをオンにする
  3. 次に、ScrollViewを垂直および水平に中央揃えします Centre ScrollView
  4. Viewを選択してから'欠落している制約を追加'-これを実行します
  5. その結果、多くの制約が生成されます。ビュー用に作成された2つの新しいものがあります。「表示する水平スクロールビュー」と「表示する垂直スクロールビュー」、またはその逆です。
  6. 「ビューへの水平スペーススクロールビュー」を削除して、ビューに3つの制約が残るようにします。ビューでスクロールビューに入るための2と、スクロールビューとビューの間に垂直方向のスペースを設定するための1
  7. 次に、クリックしてCtrlキーを押してヘッダーファイルにドラッグし、NSLayoutConstraint IBOutletを作成してVert制約をコードにリンクします(私はmine constraintVertVtoSVと呼びます)。
  8. ここで、.mファイルに移動して、これらのコード行をviewDidLoadに追加します(正しい頂点のセンタリングを得るためにパディング量で再生します)

    if (IPAD)
    

    {self.constraintVertVtoSV.constant = 150.0; }

  9. これはすべてのデバイスで実行され、適切に中央揃えされ、適切にスクロールされるはずです。

1
Guy

この問題に対処した後、ようやく解決策を見つけました。私はユニバーサルクラスサイズのストーリーボード(600x600)を使用しています。 scrollViewのサイズのUIView(contentView)を作成し、scrollViewのTop、Bottom、Leading、Trailingに制約を作成しました。次に、contentViewのサイズを手動で600x600にクリップしました。ストーリーボードはすべてのサイズを変更しようとするのをやめ、私は仕事をすることができましたが、実際のデバイスまたはシミュレーターではビューがひどく見えました。このクリップサイズの制約アウトレットを2つ作成しました。

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidthConstraint; 
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint;

次に、viewDidLoadで

CGSize viewSize = self.view.frame.size;
self.contentViewWidthConstraint.constant = viewSize.width;
self.contentViewHeightConstraint.constant = viewSize.height;

よく働く。

1
Cristian Pena

Swiftでは、この作業ソリューションを使用できます。

制約

ScrollView:先頭、末尾、上部、下部=スーパービュー

ContentView:先頭、末尾、上部、下部= ScrollView。コンテンツに固定/相対的な高さ。

幅の制約(contentView)をscrollviewsスーパービューに等しく設定できますが、プログラムで制約を追加するため、ビルド時にremove removeを選択します。これは、IBが警告で文句を言わないようにするためです。

extension UIView {

    func setupContentViewForViewWithScroll(contentView vwContent : UIView) {
        //Set constraint for scrollview content
        let constraint = NSLayoutConstraint(item: vwContent, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: self.bounds.size.width)
        vwContent.addConstraint(constraint)
        self.layoutSubviews()
    }

}

そして、View Controller viewDidLayoutSubviewsでこのメソッドを呼び出すだけです:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    self.view.setupContentViewForViewWithScroll(contentView: vwContent)
}
1

これは素人向けのソリューションであり、Appleがドキュメントで提案しているものではないことはわかっていますが、異なるコンテンツで非常に迅速にセットアップできる2回機能しました:ストーリーボードビューコントローラーにUIViewを挿入します。 UIViewに、Table View、Dynamic、0 Prototype cells、Style PlainまたはGroupedを挿入します。テーブルビューでスクロールビューを挿入し、スクロールビューでコンテンツを挿入します。それだけです。カスタムView Controllerの設定はありません。

0
Rinaldo