表示できるビューを UIStackView に追加したとしましょうUIStackView
。スクロールを行うにはどうすればよいですか?
21 に答える
Apple の Auto Layout Guide には、Working with Scroll Views に関するセクション全体が含まれています。関連するスニペット:
- コンテンツ ビューの上端、下端、リーディング エッジ、およびトレーリング エッジを、スクロール ビューの対応するエッジに固定します。コンテンツ ビューは、スクロール ビューのコンテンツ領域を定義するようになりました。
- (オプション) 水平スクロールを無効にするには、コンテンツ ビューの幅をスクロール ビューの幅と同じに設定します。コンテンツ ビューがスクロール ビューを水平方向に埋めるようになりました。
- (オプション) 垂直スクロールを無効にするには、コンテンツ ビューの高さをスクロール ビューの高さと同じに設定します。コンテンツ ビューがスクロール ビューを水平方向に埋めるようになりました。
さらに:
レイアウトでは、コンテンツ ビューのサイズを完全に定義する必要があります (手順 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
2020年最新。
100% ストーリーボードまたは 100% コード。
この例は垂直です。
最も簡単な説明は次のとおりです。
空白のフルスクリーン シーンを用意する
スクロール ビューを追加します。スクロール ビューからベース ビューにControl キーを押しながらドラッグし、すべてゼロの left-right-top-bottom を追加します。
スクロール ビューにスタック ビューを追加します。スタック ビューからスクロール ビューにControl キーを押しながらドラッグし、すべてゼロの left-right-top-bottom を追加します。
スタック ビュー内に 2 つまたは 3 つのラベルを配置します。
わかりやすくするために、ラベルの背景色を赤にします。ラベルの高さを 100 に設定します。
各 UILabelの幅を設定します。
驚くべきことに、スタック ビューではなく
UILabel
スクロール ビューにコントロール ドラッグし、等幅を選択します。
繰り返す:
UILabel から UILabel の親へのドラッグを制御しないでください。祖父母に移動してください。(つまり、スタック ビューには移動せず、スクロール ビューまで移動します。)
それはとても簡単です。それが秘密です。
秘密のヒント - Apple のバグ:
1点だけじゃダメ!いくつかのラベルを追加して、デモを機能させます。
あなたは終わった。
ヒント:すべての新しいアイテムに高さを追加する必要があります。スクロール スタック ビュー内のすべてのアイテムは、固有のサイズ (ラベルなど) を持つか、明示的な高さの制約を追加する必要があります。
別のアプローチ:
上記のアプローチを要約すると、驚くべきことに、ラベルの幅を (スタック ビューではなく) スクロール ビューの幅に設定します。
ここに別のアプローチがあります...
スタック ビューからスクロール ビューにドラッグし、「幅が等しい」という制約を追加します。すでに left-right を固定しているので、これは奇妙に思えますが、それがあなたのやり方です。どんなに奇妙に見えても、それが秘密です。
したがって、次の 2 つのオプションがあります。
- 驚くべきことに、スタック ビューの各項目の幅を、スクロール ビューの親の親 (スタック ビューの親ではなく) の幅に設定します。
また
- 驚くべきことに、スタックビューの左端と右端がスクロールビューに固定されていても、スタックビューの「幅が等しい」をスクロールビューに設定します。
明確にするために、これらの方法のいずれかを実行し、両方を実行しないでください。
ここでの上位投票の回答の制約は私にとってはうまくいきました。ストーリーボードで作成したように、以下の制約の画像を貼り付けました。
他の人が知っておくべき2つの問題に遭遇しました:
受け入れられた回答にあるものと同様の制約を追加した後、赤い autolayout error が発生します
Need constraints for: X position or width
。これは、スタック ビューのサブビューとして UILabel を追加することで解決されました。サブビューをプログラムで追加しているので、もともとストーリーボードにはサブビューがありませんでした。自動レイアウト エラーを取り除くには、サブビューをストーリーボードに追加し、実際のサブビューと制約を追加する前にロード時に削除します。
私はもともと
UIButtons
UIStackViewに追加しようとしました。ボタンとビューはロードされますが、スクロール ビューはスクロールしません。UILabels
これは、ボタンの代わりにスタック ビューに追加することで解決されました。同じ制約を使用すると、UILabels を持つこのビュー階層はスクロールしますが、UIButtons はスクロールしません。UIButtonsには IntrinsicContentSize (スタック ビューで使用される) があるように見えるため、この問題に混乱しています。ボタンが機能しない理由を誰かが知っている場合は、その理由を知りたいです。
参考までに、ビューの階層と制約を次に示します。
Eik が言うように、UIStackView
一緒UIScrollView
にうまくプレイするには、こちらを参照してください。
重要なのは、 がUIStackView
さまざまなコンテンツの可変の高さ/幅を処理し、UIScrollView
そのコンテンツのスクロール/バウンスを適切に行うことです。
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
これをに追加するだけviewdidload
です:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
macOS Catalyst に新しいパースペクティブを追加します。macOS アプリはウィンドウのサイズ変更をサポートしているUIStackView
ため、スクロールできない状態からスクロール可能な状態に、またはその逆に遷移する可能性があります。ここには 2 つの微妙な点があります。
UIStackView
可能な限りすべての領域に適合するように設計されています。- 移行中、 は
UIScrollView
、ナビゲーション バー (または macOS アプリの場合はツールバー) の下に新しく獲得/喪失した領域を考慮して、境界のサイズを変更しようとします。
残念ながら、これは無限ループを作成します。UIScrollView
とそのについてはあまり詳しくありませんadjustedContentInset
が、そのメソッドのログから、layoutSubviews
次の動作が見られます。
- 1 つは、ウィンドウを拡大します。
UIScrollView
その境界を縮小しようとします (ツールバーの下の領域は必要ないため)。UIStackView
続きます。- どういうわけか
UIScrollView
満足できず、より大きな境界に戻すことにしました。ログから見ているのはUIScrollView.bounds.height == UIStackView.bounds.height
. UIStackView
続きます。- その後、ステップ 2 にループします。
次の 2 つの手順で問題が解決するようです。
- に合わせ
UIStackView.top
ますUIScrollView.topMargin
。 - に設定
contentInsetAdjustmentBehavior
し.never
ます。
ここでは、垂直方向に成長する垂直方向にスクロール可能なビューに関心がありますUIStackView
。水平ペアの場合は、それに応じてコードを変更します。
それが将来誰にも役立つことを願っています。インターネット上でこれに言及している人を見つけることができず、何が起こったのかを理解するのにかなりの時間がかかりました.
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];
// ...
スタック ビューをスクロール ビューの垂直方向の中央に配置するという制約がある場合は、それを削除してください。
私の場合、stackView 内のビューの数は可変で、アイテムを中央に配置したいと考えていました。たとえば、stackView の 1 つのビューで、このビューを画面の中央に配置し、すべてのビューが画面内に収まらない場合は、ビューをスクロール可能にしたいと考えました。
これが私の見解の階層です。
そこで、ボタンに固定幅を設定してから、stackViewに次のように設定します。
- ボタンと同じ固定幅ですが、優先度は 650 です。
- X 中心を containerView に揃えます
- 末尾 >= 0 および先頭 >= 0 から containerView
- 下部と上部のスペースを containerView に
containerViewの場合:
- トレーリング、リーディング、ボトム、トップ、等高、等幅 (優先度 250) スーパービューへ
- 高さ固定
scrollViewの場合:
- トレーリング、リーディング、ボトム、トップからスーパービューへ
scrollView は、先頭と末尾の制約を持つビューにも埋め込まれています。
そして、これにアプローチするために使用したコードについて:
for index in 0...array.count - 1 {
if index == 0 {
firstButton.setTitle(title, for: .normal)
} else {
let button = UIButton()
button.setTitle(title, for: .normal)
stackView.addArrangedSubview(button)
stackView.setNeedsLayout()
}
}
containerView.layoutIfNeeded()
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.alignment = .fill
縦軸スクロールと横軸スクロールの両方で動作するサンプル プロジェクトを作成しました。
https://gitlab.com/muhasturk/bmh.example.stackinsidescroll.ios