尝试以编程方式创建具有嵌套UIStack视图的网格

时间:2018-04-17 19:11:48

标签: ios swift

我想创建始终符合其父视图大小的“GridView”。所以我的想法是:

  • 将垂直堆栈视图作为主要容器。
  • 垂直堆栈视图内部将是水平堆栈视图的行。
  • 作为填充,我只需将按钮添加到水平堆栈视图的行中。

所以关于它如何被初始化的一般纲要是这样的:

  1. 初始化垂直堆栈视图并将其约束设置为等于其父视图的前导,尾随,宽度和高度锚点。
  2. 根据网格的给定尺寸(AxB),在这些水平行内创建 A 水平堆栈行和 B 按钮。
    • 将每个水平堆栈的约束设置为等于其父视图的前导,尾随,宽度和高度(主容器堆栈视图)
  3. 将每个按钮的autoresizingMask设置为flexibleWidth和flexibleHeight。
  4. 这是我目前用于初始化“GridView”的逻辑,我正在使用它进行实现。现在我遇到的主要问题是,而不是垂直堆叠的水平堆栈视图,它实际上最终会重叠在彼此之上。

    (1×1) Overlap

    如果我将按钮只是添加到主容器中,它将垂直堆叠而不会出现问题。

    (1×1) Vertical Buttons

    似乎将水平堆栈放在垂直堆栈中会导致问题。有什么想法和建议吗?我已经考虑过只使用表格视图来创建一个网格但这个问题让我很好奇,我真的想知道它为什么会发生。

    这是我的代码实现:

    func createGrid(x: Int, y: Int, rootView: UIView) {
        //Init stack view.
        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.alignment = .fill
        stackView.distribution = .fillEqually
    
    
        for i in (1...x) {
            let hStackView = UIStackView()
            hStackView.axis = .horizontal
            hStackView.alignment = .fill
            hStackView.distribution = .fillEqually
    
            for _ in (1...y) {
                let button = WordBoxView()
                button.setTitle("\(i)", for: .normal)
                hStackView.addArrangedSubview(button)
            }
    
            let button = WordBoxView()
            button.setTitle("\(i)", for:.normal)
            hStackView.addArrangedSubview(button)
    
            //Add horizontal row stack to vertical parent stack.
            stackView.addSubview(hStackView)
            fitParentLayout(hStackView, parentView: stackView)
        }
    
        rootView.addSubview(stackView)
    
        //setup stack view bounds
        stackView.translatesAutoresizingMaskIntoConstraints = false
        fitParentLayout(stackView, parentView: rootView)
    }
    
    func fitParentLayout(_ child: UIView, parentView: UIView) {
    
        child.translatesAutoresizingMaskIntoConstraints = false
    
        NSLayoutConstraint.activate([
            parentView.leadingAnchor.constraint(equalTo: child.leadingAnchor),
            parentView.trailingAnchor.constraint(equalTo: child.trailingAnchor),
            parentView.widthAnchor.constraint(equalTo: child.widthAnchor),
            parentView.heightAnchor.constraint(equalTo: child.heightAnchor)])
    }
    

3 个答案:

答案 0 :(得分:2)

  

我在您的代码中发现了2个问题:

     
      
  1. 您已添加leading, trailing, widthheight限制,但不会确定您的观看次数。所以指定top。 (前导和尾随将自动确定宽度,因此无需给出宽度约束)

  2.   
  3. 您只需要为外部UIStackView提供约束,内部堆栈视图将根据外部堆栈视图的属性自动获取帧。

  4.   

将您的代码更新为:

func createGrid(x: Int, y: Int, rootView: UIView) {
    //Init stack view.
    let stackView = UIStackView()
    stackView.axis = .vertical
    stackView.alignment = .fill
    stackView.distribution = .fillEqually
    stackView.spacing = 10

    for i in 1...x {
        let hStackView = UIStackView()
        hStackView.axis = .horizontal
        hStackView.alignment = .fill
        hStackView.distribution = .fillEqually
        hStackView.spacing = 10

        for _ in 1...y {
            let button = UIButton()
            button.backgroundColor = .red
            button.setTitle("\(i)", for: .normal)
            hStackView.addArrangedSubview(button)
        }

        //Add horizontal row stack to vertical parent stack.
        stackView.addArrangedSubview(hStackView)
    }

    rootView.addSubview(stackView)

    //setup stack view bounds
    fitParentLayout(stackView, parentView: rootView)
}


func fitParentLayout(_ child: UIView, parentView: UIView) {
    child.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        parentView.leadingAnchor.constraint(equalTo: child.leadingAnchor),
        parentView.trailingAnchor.constraint(equalTo: child.trailingAnchor),
        parentView.topAnchor.constraint(equalTo: child.topAnchor),
        parentView.heightAnchor.constraint(equalTo: child.heightAnchor)])
}

答案 1 :(得分:1)

你说

  

将每个水平堆栈的约束设置为等于其父视图(主容器堆栈视图)的前导,尾随,宽度和高度。

您不希望行的高度(水平堆栈视图)等于主容器堆栈视图的高度。设置高度限制可能是造成困境的原因。

删除此约束:

parentView.heightAnchor.constraint(equalTo: child.heightAnchor)

您也不需要父/子宽度约束,但那个是无害的。

答案 2 :(得分:1)

几个"呐喊"错误...

for i in (1...x)循环结束时:

   //Add horizontal row stack to vertical parent stack.
   stackView.addSubview(hStackView)

应该是:

   //Add horizontal row stack to vertical parent stack.
   stackView.addArrangedSubview(hStackView)

并且,您不需要(也不应该)以下行

   fitParentLayout(hStackView, parentView: stackView)  

因为您正在使用垂直堆栈视图的.alignment.distribution来控制子水平堆栈视图的大小/位置。

并且,您的fitParentLayout()已交换视图。它应该是:

func fitParentLayout(_ child:UIView, parentView:UIView)
{
    child.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        child.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
        child.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
        child.topAnchor.constraint(equalTo: parentView.topAnchor),
        child.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)])
}

应该这样做。