web-dev-qa-db-ja.com

高さXまでコンテンツの本質的なサイズで展開し、その後スクロールするUIScrollView

基本的に、アラートのタイトルとメッセージセクションの動作を再現しようとしています。

enter image description here

タイトルとメッセージのラベルがスクロールビューに表示されます。ラベルテキストが増加すると、アラートの高さもラベルの固有のコンテンツサイズとともに増加します。ただし、特定の高さになると、アラートの高さが増加しなくなり、タイトルとメッセージテキストがスクロール可能になります。

私が読んだもの:

記事

スタックオーバーフロー

答えはそこにあるかもしれませんが、私はそれを抽象化することができませんでした。

私が試したこと:

2つのラベルが付いたスクロールビューにのみ焦点を当てて、親ビューがスクロールビューの本来の高さに応じてサイズ変更される最小限の例を作成しようとしました。私はたくさんの制約で遊んできました。これが機能しない(多くの中で)1つのコンボです:

enter image description here

自動レイアウト通常の制約 、さらには 固有のコンテンツサイズ で作業しました。また、私は知っています 自動レイアウトで動作する基本的なスクロールビューを取得する方法 。しかし、私は優先順位とコンテンツのハグと圧縮抵抗で何もしていません。私が行った読書から、私はそれらの意味を表面的に理解していますが、この場合にそれらをどのように適用するか途方に暮れています。私の推測では、コンテンツのハグと優先順位を使って何かをする必要があります。

19
Suragch

純粋な自動レイアウトであなたが望んでいたのと同様の効果を達成したと思います。

構造

まず、私の構造をお見せしましょう。

enter image description here

コンテンツビューは背景が白のビューで、発信者ビューと底面ビューの高さは固定されています。底面図にはボタンがあり、発信者ビューにはタイトルがあります。

ソリューション

したがって、基本的な制約を設定した後(スクロールビュー内のビューには、スクロールビューに対して上、左、右、下があり、同じ幅があることに注意してください)、問題は、スクロールビューがどのサイズにするべきかわからないことです。だからここに私がしたことが来ます:

スクロールが最大になるまで成長できるようにしたかったのです。そこで、その最大値を設定するスーパービューに比例した高さを追加しました。

enter image description here

ただし、これには2つの問題があります。スクロールビューはまだ高さを認識していないため、サイズを変更でき、スクロールビューはコンテンツのサイズを渡します(コンテンツが最大サイズよりも小さい場合)。

したがって、両方の問題を解決するために、スクロールビュー内のビューとスクロールビューから同じ高さをより小さな優先度で追加しました

enter image description here

これがお役に立てば幸いです。

8
Tiago Almeida

あなたの問題は制約だけでは解決できません、あなたはいくつかのコードを使わなければなりません。これは、スクロールビューに固有のコンテンツサイズがないためです。

したがって、スクロールビューのサブクラスを作成します。たぶん、プロパティやデリゲートなどを与えて、最大の高さを教えてください。

サブクラスでは、コンテンツサイズが変更されるたびに組み込みコンテンツサイズを無効にし、新しい組み込みサイズをコンテンツサイズの最小値と最大許容サイズとして計算します。

スクロールビューに固有のサイズが設定されると、スクロールビューとそのスーパービューの間の制約により、実際には問題に対して意味のあることが行われます。

12
Wain

純粋な自動レイアウトを介してこのソリューションを実現できるはずです。

通常、コンテンツが垂直方向に大きくなるにつれてラベルを大きくしたい場合は、これを行います

[label setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; 
[label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

スクロールビューを要件に準拠させるには、スクロールビューの上部をラベルからスクロールビューの下部まで結ぶ線を描画して、高さを計算できるようにする必要があります。スクロールビューをその親に限定するために、たとえば0.8のスーパービューの乗数を使用して高さの制約を設定できます。

1
Justin

これは、2つの制約でかなり簡単に行うことができます

  • 制約1:ScrollView.height <= MAX_SIZE。優先度=必須
  • 制約2:ScrollView.height = ScrollView.contentSize.height。優先度= DefaultHigh

AutoLayoutは、scrollViewをcontentSizeに維持しようとしますが、最大の高さに一致すると「あきらめ」、そこで停止します。

唯一注意が必要なのは、制約2の高さを設定することです。

UIStackViewがUIViewControllerにある場合、それはviewWillLayoutSubviewsで行います。

これを実現するためにUIScrollViewをサブクラス化する場合は、updateConstraintsで実行できます。

何かのようなもの

override func viewWillLayoutSubviews() {

    scrollViewHeightConstraint?.constant = scrollView.contentSize.height     

    super.viewWillLayoutSubviews()
}
1
Confused Vorlon

AutoLayout制約のみで、この正確な動作を実現することができました。これを行う方法の一般的なデモを次に示します。適切と思われるビュー階層に適用できます。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let kTestContentHeight: CGFloat = 1200

        // Subview that will shrink to fit content and expand up to 50% of the view controller's height
        let modalView = UIView()
        modalView.backgroundColor = UIColor.gray

        // Scroll view that will facilitate scrolling if the content > 50% of view controller's height
        let scrollView = UIScrollView()
        scrollView.backgroundColor = .yellow

        // Content which has an intrinsic height
        let contentView = UIView()
        contentView.backgroundColor = .green


        // add modal view
        view.addSubview(modalView)
        modalView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([modalView.leftAnchor.constraint(equalTo: view.leftAnchor),
                                     modalView.rightAnchor.constraint(equalTo: view.rightAnchor),
                                     modalView.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor,
                                                                       multiplier: 0.5),
                                     modalView.bottomAnchor.constraint(equalTo: view.bottomAnchor)])

        let expandHeight = modalView.heightAnchor.constraint(equalTo: view.heightAnchor)
        expandHeight.priority = UILayoutPriority.defaultLow
        expandHeight.isActive = true

        // add scrollview to modal view
        modalView.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([scrollView.topAnchor.constraint(equalTo: modalView.topAnchor),
                                     scrollView.leftAnchor.constraint(equalTo: modalView.leftAnchor),
                                     scrollView.rightAnchor.constraint(equalTo: modalView.rightAnchor),
                                     scrollView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)])


        // add content to scrollview
        scrollView.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
                                     contentView.widthAnchor.constraint(equalTo: modalView.widthAnchor),
                                     contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
                                     contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
                                     contentView.heightAnchor.constraint(equalToConstant: kTestContentHeight)])
        let contentBottom = contentView.bottomAnchor.constraint(equalTo: modalView.bottomAnchor)
        contentBottom.priority = .defaultLow
        contentBottom.isActive = true
    }

}
0
Vlad

私のプロジェクトにも同様の問題があります。次の方法を使用して、回避することができます。

まず、タイトルとボトムアクションの高さが固定されています。コンテンツの高さは可変です。 font-sizeを使用してmainViewを1つの子として追加し、layoutIfNeededを呼び出すと、その高さを計算してXXとして保存できます。次に、mainViewから削除しました。

次に、通常の制約を使用してスクロールビューでコンテンツパーツをレイアウトすると、mainViewの高さ制約はXXで、setContentCompressionResistancePriority(.defaultLow、for:.vertical)になります。

最後に、アラートは、コンテンツが短い場合は正確なサイズを表示し、スクロールすると長いサイズの場合は制限されたサイズを表示できます。

0
Jenus Dong