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

我想首先显示一半的标题。 当用户点击标题内的其中一个按钮时,标题的其余部分向下滑动并显示自己。 当用户再次点击按钮时,标题会滑回原始大小(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 } 

我们想要什么

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

在此处输入图像描述

解决方案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的第一个子UITableView (Interface Builder自动将其解释为UITableView的标题视图)。

在此处输入图像描述

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

 @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 } 

一些想法尝试

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