如何在 ScrollView 中布局 2 个不同大小的 StackView

Posted

技术标签:

【中文标题】如何在 ScrollView 中布局 2 个不同大小的 StackView【英文标题】:How to layout 2 StackView of different sizes in a ScrollView 【发布时间】:2021-12-31 22:52:19 【问题描述】:

我正在尝试将 2 个 stackView 合二为一 ScrollView

我决定创建一个通用的StackView,它将包含另外两个StackViews,并将它放在ScrollView

但我左边的StackView 包含 1 个项目,第二个包含 2 个项目

代码如下:

        let categoryViewsForLeftColumn = categoryViews.split()[0]
        let categoryViewsForRightColumn = categoryViews.split()[1]
        
        let leftColumnStackView = UIStackView(arrangedSubviews: categoryViewsForLeftColumn)
        leftColumnStackView.translatesAutoresizingMaskIntoConstraints = false
        leftColumnStackView.axis = .vertical
        leftColumnStackView.spacing = 20
        leftColumnStackView.distribution = .fillEqually
        
        let rightColumnStackView = UIStackView(arrangedSubviews: categoryViewsForRightColumn)
        rightColumnStackView.translatesAutoresizingMaskIntoConstraints = false
        rightColumnStackView.axis = .vertical
        rightColumnStackView.spacing = 20
        rightColumnStackView.distribution = .fillEqually
        
        let generalStackView = UIStackView(arrangedSubviews: [leftColumnStackView, rightColumnStackView])
        generalStackView.translatesAutoresizingMaskIntoConstraints = false
        generalStackView.axis = .horizontal
        generalStackView.spacing = 20
        generalStackView.distribution = .fillEqually
        
        categoriesScrollView.addSubview(generalStackView)
        
        leftColumnStackView.backgroundColor = .green
        rightColumnStackView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            generalStackView.widthAnchor.constraint(equalTo: categoriesScrollView.widthAnchor, constant: -10),
            generalStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.leadingAnchor, constant: 5),
            generalStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.trailingAnchor, constant: -5),
            generalStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            generalStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5)
        ])

结果如下:

我希望物品大小相同

我试图将 2 StackView 发布到 ScrollView

代码如下:

        categoriesScrollView.addSubview(leftColumnStackView)
        categoriesScrollView.addSubview(rightColumnStackView)
        
        leftColumnStackView.backgroundColor = .green
        rightColumnStackView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            leftColumnStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.leadingAnchor, constant: 5),
            leftColumnStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.centerXAnchor, constant: -5),
            leftColumnStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            leftColumnStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5),
            
            rightColumnStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.centerXAnchor, constant: 5),
            rightColumnStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.trailingAnchor, constant: -5),
            rightColumnStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            rightColumnStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5)
        ])

但它只会变得更糟

结果如下:

我想要达到的结果:

有谁知道如何做到这一点?

【问题讨论】:

使用集合视图 如果没有解决方案,我会的,但我仍然想了解如何使用 ScrollView 做到这一点。为什么左侧 StackView 完全独立时会拉伸以适合右侧,我想不通... @Vladislav - 这当然可以在没有集合视图的情况下完成,但您的问题令人困惑。在显示您所做工作的图像和描述中,左侧应该有一个视图,右侧应该有两个视图...但是在您的 “我想要实现的结果” 图像中,您在左侧有两个视图,在右侧有一个视图? @DonMag 其实左右有多少view都无所谓。最重要的是它们的数量不相等。最后一张图取自界面渲染服务。 【参考方案1】:

如果您想在不使用集合视图的情况下创建这样的“网格”(并且有很多不这样做的充分理由),您可以使用堆栈视图来实现。

但是,不要考虑具有两个垂直“列”堆栈视图的水平堆栈视图,而是考虑具有“行”水平堆栈视图的垂直堆栈视图。

这是一个简单的例子...

首先,一个带有圆形边的自定义视图、一个图像视图和一个标签:

class PillView: UIView 
    
    let imgView = UIImageView()
    let label = UILabel()
    
    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    
    required init?(coder: NSCoder) 
        super.init(coder: coder)
        commonInit()
    
    func commonInit() 
        label.numberOfLines = 0
        imgView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(imgView)
        addSubview(label)
        NSLayoutConstraint.activate([
            imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16.0),
            imgView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 12.0),
            imgView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -12.0),
            imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
            imgView.widthAnchor.constraint(equalToConstant: 36.0),
            imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
            
            label.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
            label.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16.0),

        ])
        layer.borderColor = UIColor.lightGray.cgColor
        layer.borderWidth = 1
    
    
    override var bounds: CGRect 
        get  return super.bounds 
        set(newBounds) 
            super.bounds = newBounds
            layer.cornerRadius = newBounds.size.height / 2.0
        
    


现在,一个示例控制器:

class SampleViewController: UIViewController 
    
    let numViews: Int = 5
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        let outerVerticalStack = UIStackView()
        outerVerticalStack.axis = .vertical
        outerVerticalStack.spacing = 20
        outerVerticalStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(outerVerticalStack)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            outerVerticalStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            outerVerticalStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            outerVerticalStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
        ])
        
        var j: Int = 0
        while j < numViews 
            let rowStack = UIStackView()
            rowStack.axis = .horizontal
            rowStack.spacing = 20
            rowStack.distribution = .fillEqually
            
            let v = PillView()
            if j == 2 
                v.label.text = "View \(j)\ntwo lines"
             else 
                v.label.text = "View \(j)"
            
            if let img = UIImage(systemName: "\(j).square.fill") 
                v.imgView.image = img
            
            rowStack.addArrangedSubview(v)
            
            j += 1
            
            if j < numViews 
                let v = PillView()
                v.label.text = "View \(j)"
                if let img = UIImage(systemName: "\(j).square.fill") 
                    v.imgView.image = img
                
                rowStack.addArrangedSubview(v)
             else 
                let v = UIView()
                rowStack.addArrangedSubview(v)
            
            
            j += 1
            
            outerVerticalStack.addArrangedSubview(rowStack)
        
        
    
    

结果如下:

【讨论】:

以上是关于如何在 ScrollView 中布局 2 个不同大小的 StackView的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 如何对 ScrollView 中的项目使用自动布局?

如何在 ScrollView 底部对齐线性广告布局?

在 ScrollView 的 ContentView 中拉伸 UIView

如何在scrollview的顶部固定一个控件

如何在iphone中使用不同的数组重新加载scrollView

Android 布局问题,一个 ScrollView 中的几个 ListView