如何制作可扩展的 UITableView 标头?
Posted
技术标签:
【中文标题】如何制作可扩展的 UITableView 标头?【英文标题】:How do I make an expandable UITableView header? 【发布时间】:2016-02-28 01:51:39 【问题描述】:我想首先显示一半的标题。当用户点击标题内的一个按钮时,标题的其余部分会向下滑动并显示出来。当用户再次点击按钮时,标题会滑回原来的大小(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】:我们想要什么
可以动态打开和关闭的表格视图标题。
解决方案 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:使用 INTERFACE BUILDER
要使UITableView
标头动态更改高度,请尝试以下操作。
在您的故事板中,首先将UITableView
添加到您的视图控制器。然后添加一个UIView
作为UITableView
的第一个孩子(接口生成器自动将其解释为UITableView
的标题视图)。
将插座连接到表格和标题,以便我们稍后在代码中访问它们。
@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 会自动将我们创建的 header 的尺寸转换为 Auto Layout 约束。由于我们要自己更改标题的高度,我们需要添加自己的替换约束并删除这些自动约束(NSAutoresizingMaskLayoutConstraints
)。
在viewDidLoad()
中,添加如下内容(默认header高度为闭合高度,HEADER_OPEN_HEIGHT
为openheader高度)。
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
【讨论】:
谢谢@paulvs。我实际上并没有为此使用 Interface Builder。有没有办法在代码中做到这一点?我在代码中尝试了你的方法,它关闭了,但是当我切换它时它没有打开。 @TIMEX,我用完全不使用 Interface Builder 的替代解决方案更新了我的答案。【参考方案2】:有几个想法可以尝试
设置后立即调用self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition .Top:, animated: true)
,其中 indexPath 是之前位于顶部的单元格?您可以尝试是否设置动画
根据您的描述,我无法判断您是否对新框架的内容大小偏移有问题(也就是单元格实际上在下方,您无法向上滚动到它们)。如果是这样,再次设置self.tableView.headerView
会改善这种情况吗?
【讨论】:
以上是关于如何制作可扩展的 UITableView 标头?的主要内容,如果未能解决你的问题,请参考以下文章
如何制作可滑动的 UItableview 标题以显示像单元格一样的删除按钮
旋转后 UITableView 标头中 UISearchBar 的布局混乱