【问题标题】:How do I make an expandable UITableView header?如何制作可扩展的 UITableView 标头?
【发布时间】:2023-03-20 06:33:01
【问题描述】:

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

【问题讨论】:

    标签: ios swift


    【解决方案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 会改善这种情况吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-25
      • 2013-06-12
      • 2021-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-10
      相关资源
      最近更新 更多