【问题标题】:Swift - How put a stack of views into a scrollviewSwift - 如何将一堆视图放入滚动视图
【发布时间】:2018-11-26 13:24:19
【问题描述】:

C目前我有一个自定义视图(返回 UIStakView),其中包含许多视图(UILabel、UIImageView、...)。它在高度足够的设备上显示良好。

(顺便说一句,这一切都是以编程方式完成的。)

在小屏幕设备上,它只会显示整个视图的顶部。所以我的解决方案是将它放在 UIScrollView 中。 (这应该很简单 - 但它给了我很多悲伤。)

但这根本不会显示,我做错了什么/错过了什么?

部分代码如下:

override init(frame: CGRect)
{
    super.init(frame: frame)

    imageFrame.addSubview(prodImage)
    NSLayoutConstraint.activate([
        prodImage.topAnchor.constraint(equalTo: imageFrame.topAnchor),
        prodImage.trailingAnchor.constraint(equalTo: imageFrame.trailingAnchor),
        prodImage.leadingAnchor.constraint(equalTo: imageFrame.leadingAnchor),
        prodImage.bottomAnchor.constraint(equalTo: imageFrame.bottomAnchor),
        ])

    imageView.addSubview(imageFrame)
    NSLayoutConstraint.activate([
        imageFrame.topAnchor.constraint(equalTo: imageView.topAnchor),
        imageFrame.trailingAnchor.constraint(equalTo: imageView.trailingAnchor),
        imageFrame.leadingAnchor.constraint(equalTo: imageView.leadingAnchor),
        imageFrame.bottomAnchor.constraint(equalTo: imageView.bottomAnchor),
        ])

// 更多浏览量...

    let stack = UIStackView(arrangedSubviews: [imageView, prodName, prodPrice])
    stack.axis = .vertical
    stack.spacing = (self.frame.height > 400) ? (self.frame.height > 800) ? 15 : 10 : 5
    stack.distribution = UIStackViewDistribution.fill

    self.addSubview(stack)
    NSLayoutConstraint.activate([
        stack.leadingAnchor.constraint(equalTo: self.leadingAnchor),
        stack.topAnchor.constraint(equalTo: self.topAnchor),
//      stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -50),
        stack.widthAnchor.constraint(equalTo: self.widthAnchor),
        ])
}

为了做出改变,我替换了底部的小节:

//  self.addSubview(stack)
//  NSLayoutConstraint.activate([
//      stack.leadingAnchor.constraint(equalTo: self.leadingAnchor),
//      stack.topAnchor.constraint(equalTo: self.topAnchor),
////        stack.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -50),
//      stack.widthAnchor.constraint(equalTo: self.widthAnchor),
//      ])
    let scrollView = UIScrollView()
    //      scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(stack)
    NSLayoutConstraint.activate([
        stack.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
        stack.topAnchor.constraint(equalTo: scrollView.topAnchor),
        stack.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -50),
        stack.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
        ])

    self.addSubview(scrollView)
    NSLayoutConstraint.activate([
        scrollView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
        scrollView.topAnchor.constraint(equalTo: self.topAnchor),
        scrollView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -50),
        scrollView.widthAnchor.constraint(equalTo: self.widthAnchor),
        ])

如您所见,我尝试禁用滚动视图的自动约束以使其适合其父级...所有尝试均失败。

如何使这个滚动视图可见?

【问题讨论】:

    标签: swift layout uiscrollview constraints uistackview


    【解决方案1】:

    可能的错误:

    • 您正在将堆栈视图的前导/尾随设置为滚动视图。
    • 如果您打印框架,您可能会理解宽度为零

    这是因为:

    • 无法根据滚动视图确定堆栈视图的宽度。
    • 滚动视图是一种特殊视图,因为它的内容可以比滚动视图大。
    • 因此您需要明确设置内容视图(堆栈视图)的宽度

    可能的修复 1:

    不是根据scrollView 设置它,而是在view 上设置它(假设scrollView 作为子视图添加到viewController 的视图)

        stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        stack.topAnchor.constraint(equalTo: view.topAnchor),
    

    可能的修复 2:

    您明确设置堆栈视图的宽度锚点

    示例:

    下面是一个简单的例子,说明如何将堆栈视图与滚动视图一起使用。

    你的广义想法是正确的。

    • Scroll View 有一个堆栈视图
    • 堆栈视图有几个子视图

    屏幕截图:

    一般说明:

    • 滚动视图很特别,因为滚动视图的内容可以比滚动视图本身更宽更高(允许滚动)
    • 因此内容的宽度和高度不应与滚动视图绑定
    • 内容的宽度和高度应设置为滚动视图没有任何播放部分

    策略

    • 正如您所指出的,我喜欢使用滚动视图和内容视图
    • 将实际内容添加到堆栈视图并让堆栈视图增长
    • 因此,只要正确设置堆栈视图对滚动视图的约束,事情就应该到位。

    调试:

    • 始终打印 viewDidAppear 中的帧值,看看是否符合您的预期

    示例代码:

    class ViewController: UIViewController {
    
        let scrollView  = UIScrollView()
        let contentView = UIStackView()
    
        let redView     = UIView()
        let greenView   = UIView()
        let yellowView  = UIView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            setupScrollView()
            setupContentView()
            setupRedView()
            setupGreenView()
            setupYellowView()
        }
    
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
    
            print("scroll view  = \(scrollView.frame)")
            print("content view = \(contentView.frame)")
            print("red view     = \(redView.frame)")
            print("green view   = \(greenView.frame)")
            print("yellow view  = \(yellowView.frame)")
        }
    
        private func setupScrollView() {
    
            scrollView.backgroundColor = .darkGray
    
            view.addSubview(scrollView)
    
            scrollView.translatesAutoresizingMaskIntoConstraints = false
    
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
            scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        }
    
        private func setupContentView() {
    
            contentView.axis            = .vertical
            contentView.distribution    = .fill
            contentView.alignment       = .fill
    
            scrollView.addSubview(contentView)
    
            contentView.translatesAutoresizingMaskIntoConstraints = false
    
            //Strategy is:
            //content view's leading / trailing anchors are set to view controller's view
            //content view's top / bottom anchors are set to scroll view
    
            contentView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
            contentView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
        }
    
        private func setupRedView() {
    
            redView.backgroundColor = .red
    
            redView.heightAnchor.constraint(equalToConstant: 400).isActive = true
    
            contentView.addArrangedSubview(redView)
        }
    
        private func setupGreenView() {
    
            greenView.backgroundColor = .green
    
            greenView.heightAnchor.constraint(equalToConstant: 400).isActive = true
    
            contentView.addArrangedSubview(greenView)
        }
    
        private func setupYellowView() {
    
            yellowView.backgroundColor = .yellow
    
            yellowView.heightAnchor.constraint(equalToConstant: 400).isActive = true
    
            contentView.addArrangedSubview(yellowView)
        }
    }
    

    【讨论】:

    • 很好的解释!你说的很有道理。我的主要区别是我正在实现一个继承自 UIView 的自定义视图 - 所以我不能使用 view 而我有 self。我替换了您在可能修复 1 中提到的约束,但它会导致崩溃。
    • 如果它崩溃了,看看它为什么会崩溃,通常崩溃会告诉你为什么会崩溃。是因为您添加到约束的视图不是视图层次结构的一部分吗?让父视图/容器视图确定其子视图的位置是一个很好的做法。每个子视图都可以设置自己的约束,但让它不知道自己的父视图
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    相关资源
    最近更新 更多