如何制作可扩展的UITableView标头?

时间:2016-02-28 01:51:40

标签: ios swift

我想首先显示标题的一半。当用户点击标题内的其中一个按钮时,标题的其余部分向下滑动并显示自己。当用户再次点击按钮时,标题会滑回原始大小(1/2大小)。

但是,当我尝试扩展标题的高度时,它会覆盖tableView单元格,而不是将它们向下推。

func prepareHeader(){
    let headerHeight = CGFloat(51.0)
    headerView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: headerHeight))
    headerView.backgroundColor = UIColor.whiteColor()
    headerView.layer.addSublayer(bottomBorder)
    headerView.layer.masksToBounds = true


    let toggler = UIButton(type: .Custom)
    toggler.frame = CGRectMake(0, 0, 40, 40)
    toggler.backgroundColor = FlatGreen()
    toggler.addTarget(self, action: "toggleHeader:", forControlEvents: UIControlEvents.TouchUpInside)
    headerView.addSubview(toggler)

    self.tableView.tableHeaderView = headerView

}

func toggleHeader(sender: UIButton){
    print("Pushed")
    var newFrame = self.headerView.frame
    newFrame.size.height = CGFloat(100)
    self.headerView.frame = newFrame
}

2 个答案:

答案 0 :(得分:5)

我们想要什么

可以动态打开和关闭的表格视图标题。

enter image description here

解决方案1:没有界面建设者

class ViewController: UIViewController {

    var headerView = UIView()
    var tableView  = UITableView()
    var isHeaderOpen = false
    let HEADER_CLOSED_HEIGHT: CGFloat = 100
    let HEADER_OPEN_HEIGHT: CGFloat = 200

    var headerClosedHeightConstraint: NSLayoutConstraint!
    var headerOpenHeightConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add the table view to the screen.
        view.addSubview(tableView)

        // Make the table view fill the screen.
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[tableView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView]))
        view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[tableView]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView]))

        // Add the header view to the table view.
        tableView.tableHeaderView = headerView
        headerView.backgroundColor = UIColor.redColor()
        headerView.translatesAutoresizingMaskIntoConstraints = false

        // Add a button to the header.
        let button = UIButton(type: .Custom)
        button.setTitle("Toggle", forState: .Normal)
        headerView.addSubview(button)
        button.translatesAutoresizingMaskIntoConstraints = false
        headerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[button]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["button": button]))
        headerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[button]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["button": button]))
        button.addTarget(self, action: "toggleHeaderAction:", forControlEvents: .TouchUpInside)

        // Make the header full width.
        view.addConstraint(NSLayoutConstraint(item: headerView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0))

        // Create the constraints for the two different header heights.
        headerClosedHeightConstraint = NSLayoutConstraint(item: headerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: HEADER_CLOSED_HEIGHT)

        // Create the height constraint for the header's other height for later use.
        headerOpenHeightConstraint = NSLayoutConstraint(item: headerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: HEADER_OPEN_HEIGHT)

        closeHeader()   // Close header by default.
    }

    func openHeader() {
        headerView.removeConstraint(headerClosedHeightConstraint)
        headerView.addConstraint(headerOpenHeightConstraint)
        updateHeaderSize()
        isHeaderOpen = true
    }

    func closeHeader() {
        headerView.removeConstraint(headerOpenHeightConstraint)
        headerView.addConstraint(headerClosedHeightConstraint)
        updateHeaderSize()
        isHeaderOpen = false
    }

    func updateHeaderSize() {
        // Calculate the header's new size based on its new constraints and set its frame accordingly.
        let size = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
        var frame = headerView.frame
        frame.size.height = size.height
        headerView.frame = frame
        tableView.tableHeaderView = headerView
        self.headerView.layoutIfNeeded()
    }

    func toggleHeader() {
        if isHeaderOpen {
            closeHeader()
        } else {
            openHeader()
        }
    }

    func toggleHeaderAction(sender: AnyObject) {
        toggleHeader()
    }
}

解决方案2:使用界面构建器

要动态设置UITableView标题更改高度,请尝试以下操作。

在故事板中,首先向视图控制器添加UITableView。然后添加UIView作为UITableView的第一个子项(Interface Builder会自动将其解释为UITableView的标题视图。)

enter image description here

将一个插座连接到表格和标题,以便稍后我们可以在代码中访问它们。

@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var headerView: UIView!

接下来,在代码中我们创建了两个约束(打开和关闭),我们稍后会添加/删除这些约束来切换标题的高度。

var headerOpenHeightConstraint: NSLayoutConstraint!
var headerClosedHeightConstraint: NSLayoutConstraint!

让我们创建一个标志来跟踪标题的打开/关闭状态。另外,我们指定开放标题高度。

var isHeaderOpen = false
let HEADER_OPEN_HEIGHT: CGFloat = 200

Interface Builder会自动将我们创建的标题的尺寸转换为自动布局约束。由于我们自己要更改标题的高度,我们需要添加自己的替换约束并删除这些自动约束(NSAutoresizingMaskLayoutConstraints)。 在viewDidLoad()中,添加以下内容(将默认标题高度作为封闭高度,将HEADER_OPEN_HEIGHT作为开放标题高度)。

override func viewDidLoad() {
    super.viewDidLoad()

    // The header needs a new width constraint since it will no longer have the automatically generated width.
    view.addConstraint(NSLayoutConstraint(item: headerView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0))

    // Add the height constraint for the default state (closed header).
    headerClosedHeightConstraint = NSLayoutConstraint(item: headerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: headerView.bounds.height)
    headerView.addConstraint(headerClosedHeightConstraint)

    // Make the constraint we'll use later to open the header.
    headerOpenHeightConstraint = NSLayoutConstraint(item: headerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: HEADER_OPEN_HEIGHT)

    // Finally we disable this so that we don't get Interface Builder's automatically generated constraints
    // messing with our constraints.
    headerView.translatesAutoresizingMaskIntoConstraints = false
}

最后,在Interface Builder中,添加一个按钮并将其连接到视图控制器中的操作。

@IBAction func toggleHeaderAction(sender: AnyObject) {

    // Add/remove the appropriate constraints to toggle the header.
    if isHeaderOpen {
        headerView.removeConstraint(headerOpenHeightConstraint)
        headerView.addConstraint(headerClosedHeightConstraint)
    } else {
        headerView.removeConstraint(headerClosedHeightConstraint)
        headerView.addConstraint(headerOpenHeightConstraint)
    }

    // Calculate the header's new size based on its new constraints.
    let size = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

    // Give the header its new height.
    var frame = headerView.frame
    frame.size.height = size.height
    headerView.frame = frame
    headerView.setNeedsLayout()
    headerView.layoutIfNeeded()
    tableView.tableHeaderView = headerView

    isHeaderOpen = !isHeaderOpen
}

答案 1 :(得分:0)

尝试一些想法

  • 在您设置之后,调用self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition .Top:, animated: true),其中indexPath是以前位于顶部的单元格?您可以尝试将其设为动画
  • 根据您的描述,我无法判断您是否遇到新帧的内容大小偏移问题(也就是说,单元格实际上处于不足状态,您无法向上滚动它们)。如果是这种情况,那么设置self.tableView.headerView会再次提高吗?