【问题标题】:Scroll View height/size is always ambiguous when containing Stack View包含堆栈视图时,滚动视图高度/大小总是不明确
【发布时间】:2017-12-23 19:13:18
【问题描述】:

我的目标是创建一个堆栈视图,它可以容纳任意(约 5-10 个)排列的子视图,并且如果它的高度变得比它包含的视图高,它就会变得可滚动。为此,我使用滚动视图。

我的理解是,如果每个排列的子视图都具有明确的高度约束,则 distribution 设置为 fill 的堆栈视图应该具有固有的内容大小。所以我可以将 Stack View 添加到滚动视图中,并且滚动可以从 Stack View 的内在内容大小中获取其内容大小。

我还试图使这个滚动堆栈视图对帧的变化(来自键盘)具有鲁棒性。

我花了很多时间阅读了很多关于 Scroll View 和 StackView 的长篇文章以及所有的编程指南,但无法让它完美地工作。

下面的代码和它的细微变化总是给出content size is ambiguousHeight is ambiguous for Scroll View。当键盘弹出时(通过点击堆栈视图中的文本视图),我只需从滚动视图的底部约束常量中减去 400。我的想法是滚动视图的框架/边界将变得小于堆栈视图的内在内容高度,并且会发生滚动。但是,屏幕只是一片空白。控制台中也没有约束日志。

我已经花费了大量时间来考虑这个场景中的所有注意事项,但它似乎超出了我的范围。对于滚动视图中堆栈视图主题的任何帮助或指示,我将不胜感激。

这是我目前的实验:

class ViewController: UIViewController {

    let scrollView = UIScrollView()
    var scrollViewBottomConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        // stack view setup (one blue and hellow view at 100 height)
        let stackView = UIStackView()
        stackView.distribution = .fill
        stackView.axis = .vertical
        let v1 = UIView()
        v1.backgroundColor = .blue
        let v2 = UITextView()
        v2.backgroundColor = .yellow
        stackView.addArrangedSubview(v1)
        stackView.addArrangedSubview(v2)


        // scroll
        scrollView.addSubview(stackView)

        view.addSubview(scrollView)

        // constraints for stack view arranged views
        v1.heightAnchor.constraint(equalToConstant: 100).isActive = true
        v2.heightAnchor.constraint(equalToConstant: 100).isActive = true

        // pin scroll view in main view
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
        // pin scroll view to stack view's bottom anchor
        scrollViewBottomConstraint = scrollView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0)
        scrollViewBottomConstraint.isActive = true


        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
        stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
        stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true


        // constrain "content view to main view and not scroll view."
        stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true


        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.handleKeyboard),
                                               name: Notification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.handleKeyboard),
                                               name: Notification.Name.UIKeyboardWillHide,
                                               object: nil)


    }

    func handleKeyboard(notification: Notification) {

        scrollViewBottomConstraint.constant = -400
    }
}

【问题讨论】:

  • 您缺少将滚动视图底部固定到视图底部的约束。例如。 scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true。我还要感谢您提供了一个自包含、易于重现的错误示例。这使得这个问题更容易理解。
  • 附带说明,当显示键盘时,通常的模式是设置滚动视图的contentInset 属性为键盘腾出空间。
  • 但是当我添加该约束时(在第 39 行 - gist.github.com/alexbollbach/38b96dec87f6a04df0eab63c299b56ad),我仍然在可视化调试器中看到 'Scrollable content size is ambiguous for UIScrollView'。此外,如果您运行它并点击黄色文本视图,它会重现我的主要问题。也就是说,当滚动视图的底部约束被动画到滚动视图的边界比堆栈视图内容短的位置时,滚动不起作用。
  • 我查看了要点,您已经修改了设置scrollViewBottomConstraint 的行(现在将滚动视图的底部固定到self.view 而不是stackView)。该更改不是必需的,您只需添加我在第一条评论中放置的代码行即可。
  • 关于键盘,不要调整scrollViewBottomConstraint,我很确定这是您在点击黄色视图时打破约束的原因。只需adjust the contentInset property of the scrollView

标签: uiscrollview uikit uistackview


【解决方案1】:

我在这里添加我的工作示例:

class ViewController: UIViewController {

    let scrollView = UIScrollView()
    var scrollViewBottomConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        // stack view setup (one blue and hellow view at 100 height)
        let stackView = UIStackView()
        stackView.distribution = .fill
        stackView.axis = .vertical
        let v1 = UIView()
        v1.backgroundColor = .blue
        let v2 = UITextView()
        v2.backgroundColor = .yellow
        let v3 = UITextView()
        v3.backgroundColor = .green
        let v4 = UITextView()
        v4.backgroundColor = .brown
        stackView.addArrangedSubview(v1)
        stackView.addArrangedSubview(v2)
        stackView.addArrangedSubview(v3)
        stackView.addArrangedSubview(v4)


        // scroll
        scrollView.addSubview(stackView)

        view.addSubview(scrollView)

        // constraints for stack view arranged views
        v1.heightAnchor.constraint(equalToConstant: 200).isActive = true
        v2.heightAnchor.constraint(equalToConstant: 200).isActive = true
        v3.heightAnchor.constraint(equalToConstant: 180).isActive = true
        v4.heightAnchor.constraint(equalToConstant: 250).isActive = true

        // pin scroll view in main view
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 100).isActive = true
        // pin scroll view to stack view's bottom anchor
        scrollViewBottomConstraint = scrollView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0)
        scrollViewBottomConstraint.isActive = true


        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
        stackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
        stackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true


        // constrain "content view to main view and not scroll view."
        stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1).isActive = true


        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.handleKeyboard),
                                               name: Notification.Name.UIKeyboardWillShow,
                                               object: nil)
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(ViewController.handleKeyboard),
                                               name: Notification.Name.UIKeyboardWillHide,
                                               object: nil)


    }

    func handleKeyboard(notification: Notification) {

        scrollViewBottomConstraint.constant = -400
    }
}

【讨论】:

    猜你喜欢
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 2020-12-05
    • 1970-01-01
    • 2021-05-16
    • 2018-07-28
    • 1970-01-01
    相关资源
    最近更新 更多