动画改变UITableView段标题高度的约束

8
我有一个自定义的UIView被用作UITableView的section header。 我想通过更改其高度来展开和折叠标题视图,并以此动画效果呈现。
HeaderView包含两个标签,使用约束排列。当视图折叠时,放置在底部的标签的高度约束将设置为0。
当我按下折叠/展开按钮时,我会更改一个headerHeight变量,该变量由tableView:heightForHeaderInSection:返回。 在此处,我也更改高度约束的值。 我在tableView.beginUpdates()和tableView.endUpdates()之间执行这两个操作,但是HeaderView不会进行动画处理。 它甚至直到我开始滚动表格视图才使用新高度。 或者我使用tableView.reloadSections(NSIndexSet(index:0),withRowAnimation:.Automatic),它确实会以动画方式显示HeaderView的高度,但会破坏其中的子视图(即使具有固定高度的顶部标签也会在整个HeaderView上垂直拉伸)。
是否有人有解决方案,可以使用约束正确地同时动画HeaderView的高度和其子视图?
以下是HeaderView的代码:
class HeaderView: UIView {

    let titleLabel = UILabel()
    let subtitleLabel = UILabel()
    let expandButton = UIButton()

    var subtitleLabelHeightConstraint: NSLayoutConstraint!

    init() {
        super.init(frame: CGRectNull)

        titleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        subtitleLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        expandButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        expandButton.setTitle("Expand / Collapse", forState: .Normal)

        addSubview(titleLabel)
        addSubview(subtitleLabel)
        addSubview(expandButton)

        let views = ["titleLabel": titleLabel, "subtitleLabel": subtitleLabel, "expandButton": expandButton]
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[titleLabel]-[expandButton]|", options: nil, metrics: nil, views: views))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[subtitleLabel]|", options: nil, metrics: nil, views: views))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-20-[titleLabel]-(>=0)-|", options: nil, metrics: nil, views: views))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=0)-[subtitleLabel]|", options: nil, metrics: nil, views: views))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-20-[expandButton]-(>=0)-|", options: nil, metrics: nil, views: views))

        titleLabel.addConstraint(NSLayoutConstraint(item: titleLabel, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 20))
        subtitleLabelHeightConstraint = NSLayoutConstraint(item: subtitleLabel, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 20)
        subtitleLabel.addConstraint(subtitleLabelHeightConstraint)
    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

以下是 TableViewController 的代码:

class TableViewController: UITableViewController {

    let headerView: HeaderView = {
        let view = HeaderView()
        view.titleLabel.text = "Title"
        view.subtitleLabel.text = "Subtitle"
        view.backgroundColor = UIColor.lightGrayColor()
        return view
    }()

    var headerHeight: CGFloat = 60

    override func viewDidLoad() {
        super.viewDidLoad()

        headerView.expandButton.addTarget(self, action: "toggleExpansion", forControlEvents: .TouchUpInside)
    }

    func toggleExpansion() {
        tableView.beginUpdates()

        if headerHeight == 60 {
            headerHeight = 40
            headerView.subtitleLabelHeightConstraint.constant = 0
        } else {
            headerHeight = 60
            headerView.subtitleLabelHeightConstraint.constant = 20
        }

        tableView.endUpdates()

        // Alternatively use tableView.reloadSections instead of begin and end updates:
        // tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Automatic)
    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
        cell.textLabel?.text = "Cell"
        return cell
    }

    override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return headerHeight
    }

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return headerView
    }


}

我还在 https://github.com/lammertw/DynamicSectionHeader 上创建了一个项目,其中包含代码。


你解决问题了吗?我现在也遇到了这个问题。感觉已经快成功了,但是又好像差那么一点点。我尝试了各种方法,包括动画布局约束,但都没有成功。只有滑动屏幕才能看到更新后的变化。 - horseshoe7
我已经在我的一侧记录了这个。 - horseshoe7
我找到了一个解决方法,虽然它有点绕,可能不适合你的目的:https://horseshoe7.wordpress.com/2016/03/17/uitableview-with-animating-section-header/ - horseshoe7
你可以使用UIView.Animate块并调用layoutifneeded方法来为你的更改添加动画效果。 - Devil Decoder
1个回答

1

Try to replace this :

func toggleExpansion() {
    tableView.beginUpdates()

    if headerHeight == 60 {
        headerHeight = 40
        headerView.subtitleLabelHeightConstraint.constant = 0
    } else {
        headerHeight = 60
        headerView.subtitleLabelHeightConstraint.constant = 20
    }

    tableView.endUpdates()

    // Alternatively use tableView.reloadSections instead of begin and end updates:
    // tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Automatic)
}

根据这个:
func toggleExpansion() {

    if headerHeight == 60 {
        headerHeight = 40
        headerView.subtitleLabelHeightConstraint.constant = 0
    } else {
        headerHeight = 60
        headerView.subtitleLabelHeightConstraint.constant = 20
    }

    tableView.beginUpdates()
    tableView.endUpdates()

    // Alternatively use tableView.reloadSections instead of begin and end updates:
    // tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Automatic)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接