自动布局 - 可折叠视图(垂直)

时间:2014-11-09 15:28:00

标签: ios autolayout collapsable

我想创建一个“添加新的信用卡viewController”。 我们不希望一次性提供所有必填字段的用户恶化。 此操作包含几个步骤。 在每一步中,视图控制器都会显示一个新的子视图(包含一个或多个文本字段)并折叠旧的子视图(在验证文本后的当前文本字段)。

  1. 我在故事板上创建了ViewController。并将所有子视图放在另一个上面。

  2. 我已经在storyBoard上创建了所有约束,每个子视图都剪辑到上面的子视图等。

  3. 即:

    NSMutableArray *constraints = [[NSLayoutConstraint
        constraintsWithVisualFormat:
            @"V:|[titleView]-[subtitleView]-[amountView]-[cardNumView]-[cardsImagesView]-[mmYYCvvView]-[billingInfoView]-[buttomView]|"
        options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom
        metrics:nil
        views:variableBindings] mutableCopy];
    
    1. 每个子视图都包含一个高度约束。

    2. 在每个步骤中,其中一个高度限制设置为零,另一个高度限制从零更改为所需高度。

    3. 即:

      self.hgtCrtMMYYCvv.constant = showFields? 50 : 0;
      self.hgtCrtBillingInfo.constant = showFields? 140 : 0;
      self.mmYYCvvView.hidden = !showFields;
      self.billingInfoView.hidden = !showFields;
      

      我有两个问题:

      • 在不调用layoutIfNeeded的情况下,初始布局有效,但在更改高度限制后没有更改。

      • 调用layoutIfNeeded没有将底部视图剪切到最后一个可见的视图 - 将其放置在视图的底部,就像所有子视图一次出现一样,但由于某些子视图被隐藏,因此创建了一个间隙。 更改子视图的高度约束已应用于屏幕,但仍然存在差距。

      请告知。

1 个答案:

答案 0 :(得分:2)

  

调用" layoutIfNeeded"未将底部视图剪切到最后一个可见的视图 - 将其放置在视图的底部,就像所有子视图一次出现一样

看看你的约束。您已将底部视图的底部固定在其超视图的底部!所以它的底部必须出现在superview的底部,因为这是你指示它做的。

确实,我很惊讶你的约束根本起作用。你基本上已经超定了它们。如果你给每个字段的高度固定它的顶部和底部,对于每个字段,那么除非你很幸运,否则不可能满足你的约束。超视图的高度是固定的,因此您的约束必须完美地加到该高度。

我将建议一个完整的替代方法,我认为你会发现更容易。不要弄乱个别常量,而是计划每种可能情况的正确(非超定)约束,并将这些约束存储在属性中。现在,当您想要隐藏/显示某个字段时,您只需删除所有约束并在另一个集合中交换。

这也将解决layoutIfNeeded问题。

碰巧我有一个显示如何执行此操作的实际示例。 (它是用Swift编写的,但我确定你可以在心理上进行补偿。)在我的示例代码中,我们有三个矩形;然后我移除一个矩形并关闭其余两个之间的间隙。准备两套约束是乏味但基本的:

    let c1 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v1]) as [NSLayoutConstraint]
    let c2 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v2]) as [NSLayoutConstraint]
    let c3 = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(20)-[v(100)]", options: nil, metrics: nil, views: ["v":v3]) as [NSLayoutConstraint]
    let c4 = NSLayoutConstraint.constraintsWithVisualFormat("V:|-(100)-[v(20)]", options: nil, metrics: nil, views: ["v":v1]) as [NSLayoutConstraint]
    let c5with = NSLayoutConstraint.constraintsWithVisualFormat("V:[v1]-(20)-[v2(20)]-(20)-[v3(20)]", options: nil, metrics: nil, views: ["v1":v1, "v2":v2, "v3":v3]) as [NSLayoutConstraint]
    let c5without = NSLayoutConstraint.constraintsWithVisualFormat("V:[v1]-(20)-[v3(20)]", options: nil, metrics: nil, views: ["v1":v1, "v3":v3]) as [NSLayoutConstraint]

    self.constraintsWith.extend(c1)
    self.constraintsWith.extend(c2)
    self.constraintsWith.extend(c3)
    self.constraintsWith.extend(c4)
    self.constraintsWith.extend(c5with)

    self.constraintsWithout.extend(c1)
    self.constraintsWithout.extend(c3)
    self.constraintsWithout.extend(c4)
    self.constraintsWithout.extend(c5without)

    NSLayoutConstraint.activateConstraints(self.constraintsWith)

但是,当需要将中间视图交换到界面或从界面中取出时,就会得到回报:它是微不足道的。只需删除或插入它,然后删除所有约束,然后插入适合我们已经准备好的情境的全新约束集:

@IBAction func doSwap(sender: AnyObject) {
    if self.v2.superview != nil {
        self.v2.removeFromSuperview()
        NSLayoutConstraint.deactivateConstraints(self.constraintsWith)
        NSLayoutConstraint.activateConstraints(self.constraintsWithout)
    } else {
        self.view.addSubview(v2)
        NSLayoutConstraint.deactivateConstraints(self.constraintsWithout)
        NSLayoutConstraint.activateConstraints(self.constraintsWith)
    }
}

多组约束的准备是繁琐的,但可以通过规则来完成,即约束可以是机器生成的"在一个循环(写这个留给你的练习)。根据一个简单的规则再次交换约束,因为只有一个集合适合您希望显示/隐藏的特定字段集。因此,一旦设置完成,它将比您现在所做的更简单,更易于维护。