UIStackViewがスクロールすることは可能ですか?


210

表示できるビューをUIStackViewに追加したとしましょうUIStackView。スクロールするにはどうすればよいですか?


コンテンツに1つ(または複数)のスタックビューを備えたスクロールビューを使用しないのはなぜですか?
Wain 2015

2
ワンセンテンスソリューションです、あなたが祖父母スクロールビューにドラッグ制御幅のために、NOT親スタックビューを。私の答えで説明しました!
Fattie

iOSのUIScrollView内にUIStackViewを埋め込む方法github.com/onmyway133/blog/issues/324
onmyway133 '24

iOSにおけるこの重要な質問について。 実際には、2つの方法(2つだけ)があります。これが唯一の方法であり、2つの手順のいずれかを正確に実行する必要あります。以下の私の答えは常に更新されます。
Fattie

はい。UIScrollViewのコンテンツビューとして中間UIViewが必要です。次に、UIStackViewをコンテンツビューの子ビューとして配置します。raywenderlich.com/...
poGUIst

回答:


577

コードなしのソリューション探している人のために、自動レイアウトを使用して、ストーリーボードでこれを完全に行う例を作成しました。

githubから入手できます。

基本的に、例を再作成するには(垂直スクロールの場合):

  1. を作成しUIScrollView、その制約を設定します。
  2. にを追加UIStackViewしますUIScrollView
  3. :制約を設定しLeadingTrailingTopBottomからのものと同じでなければなりませんUIScrollView
  4. 同じセットアップWidthの間の制約UIStackViewとをUIScrollView
  5. 軸=垂直、配置=塗りつぶし、分布=等間隔、および間隔= 0を UIStackView
  6. に数を追加UIViewsしますUIStackView
  7. 走る

手順4でを交換WidthHeight、手順5でAxis= を設定Horizontalして、水平方向のUIStackViewを取得します。


55
ありがとう!「4. UIStackViewとUIScrollViewの間に同じ幅制約を設定します。」キーだった;)
空欄

19
6.も重要です。コードですべてのビューを追加したかったが、自動レイアウトでIBに不足している制約が表示された。
前の

11
こんにちは、このソリューションは水平スクロールでは機能しないようです。ステップ4では、同じ幅を設定する代わりに、同じ高さを設定し、Axis = Horizo​​ntalと仮定します。私はそれを試してみましたが、うまくいきません。
Seto Elkahfi、2017

5
stackviewとscrollviewの幅が同じになるように制約を追加すると、インターフェイスビルダーから警告が削除されますが、スクロールは無効になります。スクロールは、scrollview内のコンテンツビューがscrollview より大きい場合にのみ発生します。
Jason Moore

6
ポイント6について詳しく説明します。スタックビューに要素がなく、すべての制約が設定されている場合は、「Y位置または高さのスクロールビューには制約が必要」と表示されます。スタックビューにラベルを追加するだけで、この警告は消えます!
Faisal Memon 2017

45

Appleの自動レイアウトガイドには、スクロールビューの操作に関するセクション全体が含まれています。関連するスニペット:

  1. コンテンツビューの上部、下部、リーディング、トレーリングエッジをスクロールビューの対応するエッジに固定します。コンテンツビューは、スクロールビューのコンテンツ領域を定義するようになりました。
  2. (オプション)水平スクロールを無効にするには、コンテンツビューの幅をスクロールビューの幅に設定します。コンテンツビューがスクロールビューを水平方向に塗りつぶします。
  3. (オプション)垂直スクロールを無効にするには、コンテンツビューの高さをスクロールビューの高さと等しく設定します。コンテンツビューがスクロールビューを水平方向に塗りつぶします。

さらに:

レイアウトでは、コンテンツビューのサイズを完全に定義する必要があります(手順5および6で定義されている場合を除く)。…コンテンツビューがスクロールビューよりも高い場合、スクロールビューは垂直スクロールを可能にします。コンテンツビューがスクロールビューよりも広い場合、スクロールビューは水平スクロールを可能にします。

要約すると、(この場合、スタックビュー)スクロールビューのコンテンツビューは、その端部に固定されなければならないし、その幅及び/又は高さは、さもなければ拘束しています。つまり、スタックビューのコンテンツは、スクロールが必要な方向に(直接的または間接的に)制約される必要があります。これは、たとえば、垂直にスクロールするスタックビュー内の各ビューに高さ制約を追加することを意味します。以下は、スタックビューを含むスクロールビューの垂直スクロールを可能にする方法の例です。

// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true

1
おかげで、高さの制約を省略すると(垂直スクロールを可能にするため)、ストーリーボードでNeed constraints for: Y position or height.このエラーが発生します。このエラーは、垂直スクロールを無効にするスタックビューの幅と高さの両方の制約を設定した場合にのみ解消されます。このコードはまだ機能しますか?
Crashalot 2018

@Crashalotこのエラーを解消するには、スタックビューにサブビューを追加する必要があります。以下の私の答えを参照してください。
pkamb 2018

16

ここで上位投票された回答の制約がうまくいきました。ストーリーボードで作成したように、以下の制約の画像を貼り付けました。

私は2つの問題にぶつかりましたが、他の人は注意する必要があります:

  1. 承認された回答と同様の制約を追加すると、赤いautolayoutエラーが発生しますNeed constraints for: X position or width。これは、UILabelをスタックビューのサブビューとして追加することで解決しました。

    プログラムでサブビューを追加しているので、ストーリーボードには最初はサブビューがありませんでした。自動レイアウトエラーを取り除くには、サブビューをストーリーボードに追加し、実際のサブビューと制約を追加する前にロード時に削除します。

  2. 私は最初にUIButtonsUIStackView に追加しようとしました。ボタンとビューは読み込まれますが、スクロールビューはスクロールしません。これはUILabels、ボタンの代わりにスタックビューに追加することで解決しました。同じ制約を使用して、UILabelsを持つこのビュー階層はスクロールしますが、UIButtonsはスクロールしません。

    UIButton (スタックビューで使用される)IntrinsicContentSizeを持っているように見えるので、私はこの問題で混乱しています。ボタンが機能しない理由を誰かが知っているなら、私はその理由を知りたいです。

参考のために、ここに私のビュー階層と制約を示します。

スクロールビューのスタックビューの制約[1]


3
頭を壁にぶつけてイライラする時間を何時間も節約してくれました。私はまったく同じ状況にあり、警告の理由とボタンが機能しない理由を理解できませんでした。なぜアップルなのか!?
PakitoV 2017

15

Eikが言うように、UIStackViewそしてUIScrollView一緒に上手に遊ぶには、ここを参照してください

重要なのは、UIStackViewがさまざまなコンテンツの可変の高さ/幅を処理し、UIScrollViewそのコンテンツをそのコンテンツのスクロール/バウンスにうまく機能させることです。

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)       
}

2
スクロール可能なスタックビューを可能にするために、手動レイアウトと自動レイアウトを組み合わせる必要はありません。スタックビューの幅や高さを制限するだけです。詳細と関連するAppleドキュメントへのリンクについては、私の回答を参照してください。
チタニウム

githubの例は完全に機能し、非常に明確でした。共有いただきありがとうございます。
Behr

stackView.translatesAutoresizingMaskIntoConstraints = false、私の場合はこれでうまくいきました
ハウルジェンキンス

10

私は同じことをしようとしていて、この素晴らしい投稿に出会いました。アンカーAPIを使用してプログラムでこれを実行する場合は、これが適切な方法です。

まとめると、をに埋め込み、のアンカー制約をの制約と一致するように設定UIStackViewします。UIScrollViewUIStackViewUIScrollView

stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true

複数の質問に同じ回答を追加しないでください。最良のものに答え、残りを重複としてフラグを立てます。複数の質問に重複した回答を追加することは許容されますか?を
FelixSFD 2017

10
この場合、それらは実際には重複していません。他の質問では、プログラムでそれを行う方法を具体的に尋ねますが、これはそうではありません。実際、この質問の一番上の答えは、結果を達成するためのGUIメソッドを提供します。それにもかかわらず、プログラムによるソリューションは一部のユーザーにとって価値があるかもしれません。
Dalmazio 2017

9

2020年まで最新。

100%ストーリーボードまたは100%コード。


考えられる最も簡単な説明は次のとおりです。

  1. 空白のフルスクリーンシーンがある

  2. スクロールビューを追加します。スクロールビューからベースビューにCtrlキーを押しながらドラッグし、left-right-top-bottomをすべてゼロに追加します。

  3. スクロールビューにスタックビューを追加します。スタックビューからスクロールビューにCtrlキーを押しながらドラッグし、左上、右下、すべてゼロを追加します。

  4. スタックビュー内に2つまたは3つのラベルを配置します。

わかりやすくするために、ラベルの背景色を赤にします。ラベルの高さを100に設定します。

  1. 次に、各UILabel のを設定します。

    驚いたことに、スタックビューではなくUILabel、スクロールビューからCtrlキーを押しながらドラッグして、同じ幅を選択します。

繰り返す:

UILabelからUILabelの親へのドラッグを制御しないでください。祖父母に移動してください。(言い換えれば、スクロールビューに移動し、スタックビューに移動しないでください。)

とても簡単です。それが秘密です。

秘密のヒント-Appleバグ:

それは動作しません一つだけのアイテムに!いくつかのラベルを追加して、デモを機能させます。

完了です。

ヒント:すべての新しいアイテムに高さを追加する必要あります。スクロールスタックビューのすべてのアイテムは、固有のサイズ(ラベルなど)を持っているか、明示的な高さ制約を追加する必要があります。


代替アプローチ:

上記の例では、驚くべきことに、UILabelsの幅を(スタックビューではなく)スクロールビューの幅に設定します。

あるいは...

スタックビューからスクロールビューにドラッグし、「幅が等しい」制約を追加します。既に左右を固定しているので、これは奇妙に見えますが、それがその方法です。それがどんなに奇妙に見えても、それは秘密です。

したがって、2つのオプションがあります。

  1. 驚いたことに、スタックビューの各項目の幅を、(スクロールビューの親ではなくスクロールビューの祖父母の幅に設定します。

または

  1. 驚いたことに、スタックビューの左端と右端がスクロールビューに固定されている場合でも、スタックビューの「横幅」をスクロールビューに設定します。

明確にするために、これらの方法のいずれかを実行し、両方を実行しないでください。


8

水平スクロール(UIScrollView内のUIStackView)

水平スクロール用。まず、a UIStackViewとa UIScrollViewを作成し、次の方法でビューに追加します。

let scrollView = UIScrollView()
let stackView = UIStackView()

scrollView.addSubview(stackView)
view.addSubview(scrollView)

設定するには思い出しtranslatesAutoresizingMaskIntoConstraintsまでfalseUIStackViewUIScrollView

stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false

すべてを動作させるには、のトレーリング、リーディング、トップ、ボトムアンカーがアンカーUIStackViewと等しい必要がありますUIScrollView

stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

ただし、のアンカー幅はアンカーの幅UIStackView以上でなければなりませんUIScrollView

stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true

次に、をアンカーしますUIScrollView。例:

scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true 

次に、UIStackView配置と分布について次の設定を試すことをお勧めします。

topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center

topicStackView.spacing = 10

最後に、このaddArrangedSubview:メソッドを使用して、UIStackViewにサブビューを追加する必要があります。

テキストインセット

便利だと思われる追加機能の1つは、UIStackViewが内に保持されているため、UIScrollViewテキストインセットにアクセスして物事を少し見栄えよくすることができることです。

let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset

// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true

7

これを追加するだけviewdidloadです:

let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets

ソース:https : //developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html


これが私のAutolayout + ScrollViewの問題をどれだけ救ったかはわかりません。そのAppleリンクで素晴らしい発見。
DED

何をしますか?これがないとどうなりますか?
ハニー

これは、ステータスバーの下にスクロールビューのコンテンツを配置するだけです。
18年

2

あなたはScrollableStackViewを試すことができますhttps//github.com/gurhub/ScrollableStackView

これは、Objective-CおよびSwift互換ライブラリです。CocoaPodsから利用できます。

サンプルコード(Swift)

import ScrollableStackView

var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)

// add your views with 
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...

サンプルコード(Objective-C)

@import ScrollableStackView

ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];

UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];

// add your views with
[scrollable.stackView addArrangedSubview:rectangle]; 
// ...

こんにちはピーター、実際には素晴らしいアイデアです!貢献はいつでも歓迎です。これについてはプルリクエストを送ってください。ベスト。
mgyky 2018年


0

垂直スタックビュー/スクロールビューの例(EasyPeasyfor autolayout を使用):

let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
    Edges(),
    Width().like(self.view)
]

let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill    
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
    Edges(),
    Width().like(self.view)
]

サブビューのそれぞれの高さが定義されていることを確認してください!


0
  1. 何よりもまず、ビューをデザインします。できればSketchのようなものを使用するか、スクロール可能なコンテンツとして何が必要かを理解してください。

  2. この後、ビューコントローラーをフリーフォームにし(属性インスペクターから選択)、ビューの固有のコンテンツサイズに従って高さと幅を設定します(サイズインスペクターから選択します)。

  3. この後、ビューコントローラーにスクロールビューを配置します。これはロジックです。これは、iOSでほぼ常に機能していることがわかりました(コマンド+クリックで取得できるビュークラスのドキュメントを確認する必要がある場合があります)そのクラスまたはグーグル経由)

    2つ以上のビューで作業している場合は、最初に、以前に導入された、またはより原始的なビューから始め、次に、後で導入された、またはより新しいビューに移動します。したがって、ここでは最初にスクロールビューが導入されているため、最初にスクロールビューから開始して、次にスタックビューに移動します。ここで、スクロールビューの制約を、そのスーパービューに対してすべての方向でゼロにします。すべてのビューをこのスクロールビュー内に配置してから、スタックビューに配置します。

スタックビューでの作業中

  • 最初にグラウンドボトムアップ(ボトムアップアプローチ)から始めます。つまり、ビューにラベル、テキストフィールド、および画像がある場合は、これらのビューを最初に(スクロールビュー内に)レイアウトし、その後それらをスタックビューに配置します。

  • その後、スタックビューのプロパティを調整します。それでも目的のビューが得られない場合は、別のスタックビューを使用してください。

  • それでも達成されない場合は、耐圧縮性またはコンテンツを優先するコンテンツを試してください。
  • この後、スタックビューに制約を追加します。
  • 上記のすべてが満足のいく結果を与えていない場合は、フィラービューとして空のUIViewを使用することも検討してください。

ビューを作成した後、マザースタックビューとスクロールビューの間に制約を設定し、子スタックビューはマザースタックビューとスタックします。うまくいけば、この時間までにそれがうまく機能するか、提案を与えるXcodeから警告を受け取り、それが言うことを読んでそれらを実装するかもしれません。うまくいけば、今あなたはあなたの期待どおりに実際の見解を持っているはずです:)。


0

ネストされたまたは単一のスタックビューの場合、スクロールビューはルートビューで固定幅を設定する必要があります。スクロールビュー内にあるメインスタックビューは、同じ幅を設定する必要があります。[私のスクロールビューはビューの下にあり、無視してください]

UIStackViewとUIScrollViewの間に同じ幅の制約を設定します。

ここに画像の説明を入力してください


0

水平スクロールビューを探している人がいる場合

func createHorizontalStackViewsWithScroll() {

 self.view.addSubview(stackScrollView)
 stackScrollView.translatesAutoresizingMaskIntoConstraints = false
 stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
 stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
 stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
 stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
 stackScrollView.addSubview(stackView)

 stackView.translatesAutoresizingMaskIntoConstraints = false
 stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
 stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
 stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
 stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
 stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true

 stackView.distribution = .equalSpacing
 stackView.spacing = 5
 stackView.axis = .horizontal
 stackView.alignment = .fill


 for i in 0 ..< images.count {
 let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
 // set button image
 photoView.translatesAutoresizingMaskIntoConstraints = false
 photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
 photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true

 stackView.addArrangedSubview(photoView)
 }
 stackView.setNeedsLayout()

 }

0

シーンにスクロールビューを配置し、シーン全体に収まるようにサイズを変更します。次に、スタックビューをスクロールビュー内に配置し、アイテムの追加ボタンをスタックビュー内に配置します。すべての準備が整ったら、次の制約を設定します。

Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width

ここに画像の説明を入力してください

コード:Stack View.Width = Scroll View.Widthキーです。


0

macOS Catalystに新しい視点を追加します。macOSアプリはウィンドウのサイズ変更をサポートUIStackViewしているため、スクロールできない状態からスクロール可能な状態に、またはその逆に遷移する可能性があります。ここには微妙なことが2つあります。

  • UIStackView それができるすべての領域に合うように設計されています。
  • 移行中に、UIScrollViewナビゲーションバー(またはmacOSアプリの場合はツールバー)の下の新しく取得または失われた領域を考慮して、境界のサイズを変更しようとします。

これにより、残念ながら無限ループが発生します。私は非常に精通していないですUIScrollViewし、そのadjustedContentInsetが、その中に私のログからlayoutSubviewsの方法、私は、次の行動を見ています:

  • ウィンドウを拡大します。
  • UIScrollView 境界を縮小しようとします(ツールバーの下の領域は必要ないため)。
  • UIStackView 続く。
  • どういうわけかUIScrollView不満があり、より大きな境界に復元することにしました。丸太から私が見ているのはそのことなので、これは私には非常に奇妙に感じますUIScrollView.bounds.height == UIStackView.bounds.height
  • UIStackView 続く。
  • 次に、ステップ2にループします。

次の2つの手順で問題が解決するようです。

  • 揃えUIStackView.topUIScrollView.topMargin
  • に設定contentInsetAdjustmentBehavior.neverます。

ここでは、垂直方向に拡大する垂直スクロール可能なビューに関心がありUIStackViewます。水平ペアの場合は、それに応じてコードを変更します。

それが将来誰にでも役立つことを願っています。インターネットでこれについて言及している人を見つけることができず、何が起こったのかを理解するのにかなりの時間がかかりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.