ios 如何在Swift中创建3xInfinity项目的网格

gt0wga4j  于 2023-05-23  发布在  iOS
关注(0)|答案(2)|浏览(128)

我试图创建一个网格的3xInfinity项目在Swift与相同大小的项目。项目是方形按钮,最多可以对齐3个项目宽,在Y轴上无限长。我已经创建了一个函数,它需要一个垂直轴的UIStackView,每3个项目我创建一个新的UIStackView,水平轴在另一个项目内。这很好用,但只有当所有按钮都是3的倍数时。每当它们不在时,行使按钮填满所有可用空间。
我想知道如何在Swift中创建一个3xInfinity项目的网格,以便按钮总是均匀间隔,即使有奇数个按钮。

当前如何显示

如何显示

代码

let stackview = UIStackView()
contentView.addSubview(stackview)
stackview.axis = .vertical
stackview.alignment = .fill
stackview.distribution = .fill
stackview.spacing = 10

 for _ in 0...feedButtonRows {
            let hstack = UIStackView()
            hstack.axis = .horizontal
            hstack.alignment = .center
            hstack.distribution = .fillEqually
            hstack.spacing = 16
            hstack.translatesAutoresizingMaskIntoConstraints = false
            hstack.widthAnchor.constraint(equalToConstant: stackview.frame.size.width).isActive = true
            for _ in 0..<3 {
                if(count == buttons.count){
                    
                    break
                }
                let button = FoodButtonComponent(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
                hstack.addArrangedSubview(button)
                button.widthAnchor.constraint(equalToConstant: 100).isActive = true
                button.heightAnchor.constraint(equalToConstant: 100).isActive = true
                button.setup(with: buttons[count])
                count+=1
            }
            stackview.addArrangedSubview(hstack)
        }
hlswsv35

hlswsv351#

您不能在使用distribution = .fillEqually的同时,期望约束视图大小会对使用widthAnchor.constraint(equalToConstant: 100).isActive = true产生影响。您一定收到了一些关于约束冲突的运行时警告。
另外,在使用约束时,请记住禁用将调整大小掩码自动转换为代码内生成的视图的约束。

let button = FoodButtonComponent(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
button.translatesAutoresizingMaskIntoConstraint = false

无论如何,您仍然可以通过简单地添加一个空视图来做您想要做的事情,而不是在index超过3时中断。您实际上可以创建一个独立的方法来布局视图。查看此实现:

func layoutViewsVertically(_ views: [UIView], numberOfItemsPerRow: Int, horizontalSpacing: CGFloat = 0, verticalSpacing: CGFloat = 0) -> UIStackView {
    let verticalStackView = UIStackView()
    verticalStackView.axis = .vertical
    verticalStackView.distribution = .fillEqually
    verticalStackView.spacing = verticalSpacing
    
    var viewsToLayout = views
    while viewsToLayout.isEmpty == false {
        let horizontalStackView = UIStackView()
        horizontalStackView.spacing = horizontalSpacing
        horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
        horizontalStackView.axis = .horizontal
        horizontalStackView.distribution = .fillEqually
        (0..<numberOfItemsPerRow).forEach { _ in
            let view = viewsToLayout.isEmpty ? UIView() : viewsToLayout.removeFirst()
            horizontalStackView.addArrangedSubview(view)
        }
        verticalStackView.addArrangedSubview(horizontalStackView)
    }
    return verticalStackView
}

我现在可以用多种方式使用它。例如,这将填充父级的大小:

let views: [UIView] = (0..<20).map { index in
    let view = [UILabel(), UIButton()].randomElement()!
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = [.red, .blue, .green].randomElement()!
    return view
}
let stackedView = StacksView.layoutViewsVertically(views, numberOfItemsPerRow: 3)
stackedView.frame = view.bounds
view.addSubview(stackedView)

或者,您可以在堆栈视图中有一些由视图定义的隐式大小

let views: [UIView] = (0..<20).map { index in
    let view = [UILabel(), UIButton()].randomElement()!
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = [.red, .blue, .green].randomElement()!
    view.heightAnchor.constraint(equalToConstant: 50).isActive = true
    view.widthAnchor.constraint(equalToConstant: 50).isActive = true
    return view
}
let stackedView = StacksView.layoutViewsVertically(views, numberOfItemsPerRow: 3)
stackedView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackedView)

或者,您可以在两者之间设置一些东西,其中宽度受父项约束,但高度不受父项约束:

let views: [UIView] = (0..<20).map { index in
    let view = [UILabel(), UIButton()].randomElement()!
    view.translatesAutoresizingMaskIntoConstraints = false
    view.backgroundColor = [.red, .blue, .green].randomElement()!
    view.heightAnchor.constraint(equalToConstant: 50).isActive = true
    return view
}
let stackedView = StacksView.layoutViewsVertically(views, numberOfItemsPerRow: 3)
stackedView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackedView)
stackedView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 1.0).isActive = true
bfrts1fy

bfrts1fy2#

如果你设置了垂直堆栈视图的.alignment = .leading,并且给予水平“行”堆栈视图宽度限制,按钮将左对齐,不会被拉伸。
你也可以像这样简化你的代码:

var i: Int = 0
    
    while i < numButtons {
        let hstack = UIStackView()
        hstack.axis = .horizontal
        hstack.spacing = 16
        for _ in 0..<3 {
            if i < numButtons {
                let button = FoodButtonComponent()
                button.setTitle("\(i)", for: [])
                button.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
                button.heightAnchor.constraint(equalTo: button.widthAnchor).isActive = true
                hstack.addArrangedSubview(button)
            }
            i += 1
        }
        stackview.addArrangedSubview(hstack)
    }

请注意,当视图(标签,按钮等)被添加为堆栈视图的arrangedSubviews时,UIKit会自动设置.translatesAutoresizingMaskIntoConstraints = false-因此不需要显式设置。
下面是一个完整的例子-更改顶部的numButtons以查看布局:

class StacksVC: UIViewController {
    
    let numButtons: Int = 7
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        
        let stackview = UIStackView()
        stackview.axis = .vertical
        // this will keep the 1- & 2-button rows left-aligned
        stackview.alignment = .leading
        stackview.distribution = .fill
        stackview.spacing = 10
        
        var i: Int = 0
        
        while i < numButtons {
            let hstack = UIStackView()
            hstack.axis = .horizontal
            hstack.spacing = 16
            for _ in 0..<3 {
                if i < numButtons {
                    let button = FoodButtonComponent()
                    button.setTitle("\(i)", for: [])
                    button.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
                    button.heightAnchor.constraint(equalTo: button.widthAnchor).isActive = true
                    hstack.addArrangedSubview(button)
                }
                i += 1
            }
            stackview.addArrangedSubview(hstack)
        }
        
        stackview.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackview)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            stackview.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stackview.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            // set stackview width to (3 x 100) + (2 x 16)
            //  so if we have only 1 or 2 buttons, they will be "left-aligned"
            stackview.widthAnchor.constraint(equalToConstant: 332.0),
        ])
        
        // let's set the vertical stackview background to light gray
        //  if we want to see the framing
        //stackview.backgroundColor = UIColor(white: 0.9, alpha: 1.0)

    }
    
}

运行时:

相关问题