Swift - 如何使用XIB文件创建自定义viewForHeaderInSection?

iam*_*rak 33 uitableview xib ios swift

我可以在编程上创建简单的自定义viewForHeaderInSection,如下所示.但是我想要做更复杂的事情,可能是与不同​​类的连接,并像tableView单元一样到达它们的属性.简单地说,我想看看我做了什么.

func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    if(section == 0) {

        let view = UIView() // The width will be the same as the cell, and the height should be set in tableView:heightForRowAtIndexPath:
        let label = UILabel()
        let button   = UIButton(type: UIButtonType.System)

        label.text="My Details"
        button.setTitle("Test Title", forState: .Normal)
        // button.addTarget(self, action: Selector("visibleRow:"), forControlEvents:.TouchUpInside)

        view.addSubview(label)
        view.addSubview(button)

        label.translatesAutoresizingMaskIntoConstraints = false
        button.translatesAutoresizingMaskIntoConstraints = false

        let views = ["label": label, "button": button, "view": view]

        let horizontallayoutContraints = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[label]-60-[button]-10-|", options: .AlignAllCenterY, metrics: nil, views: views)
        view.addConstraints(horizontallayoutContraints)

        let verticalLayoutContraint = NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0)
        view.addConstraint(verticalLayoutContraint)

        return view
    }

    return nil
}


func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 50
}
Run Code Online (Sandbox Code Playgroud)

有没有人解释如何使用xib创建自定义tableView标题视图?我遇到过旧的Obj-C主题,但我是Swift语言的新手.如果有人解释详细,那就太好了.

1.issue:按钮@IBAction不与我的ViewController连接.(固定)

使用File的Owner,ViewController基类解决(点击左侧轮廓菜单.)

2.issue:标题高度问题(固定)

解决了在viewForHeaderInSection:方法中添加headerView.clipsToBounds = true问题.

对于约束警告, 这个答案解决了我的问题:

当我在viewController中使用此方法添加ImageView甚至相同的高度约束时,它流过tableView行看起来像图片.

 func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 120
}
Run Code Online (Sandbox Code Playgroud)

如果我使用,则在viewDidLoad中自动调整ScrollViewInsets,在这种情况下,图像流在navigationBar下.-固定-

self.automaticallyAdjustsScrollViewInsets = false
Run Code Online (Sandbox Code Playgroud)

3.issue:如果按下查看(固定)

@IBAction func didTapButton(sender: AnyObject) {
    print("tapped")

    if let upView = sender.superview {
        if let headerView = upView?.superview as? CustomHeader {
            print("in section \(headerView.sectionNumber)")
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 84

基于NIB的标头的典型过程是:

  1. 创建UITableViewHeaderFooterView至少包含标签出口的子类.您可能还想给它一些标识符,通过该标识符可以对此标题对应的部分进行反向工程.同样,您可能希望指定一个协议,标头可以通过该协议通知视图控制器事件(如点击按钮).因此,在Swift 3及更高版本中:

    // if you want your header to be able to inform view controller of key events, create protocol
    
    protocol CustomHeaderDelegate: class {
        func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
    }
    
    // define CustomHeader class with necessary `delegate`, `@IBOutlet` and `@IBAction`:
    
    class CustomHeader: UITableViewHeaderFooterView {
        static let reuseIdentifier = "CustomHeader"
    
        weak var delegate: CustomHeaderDelegate?
    
        @IBOutlet weak var customLabel: UILabel!
    
        var sectionNumber: Int!  // you don't have to do this, but it can be useful to have reference back to the section number so that when you tap on a button, you know which section you came from; obviously this is problematic if you insert/delete sections after the table is loaded; always reload in that case
    
        @IBAction func didTapButton(_ sender: AnyObject) {
            delegate?.customHeader(self, didTapButtonInSection: section)
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建NIB.就个人而言,我给NIB提供了与基类相同的名称,以简化我项目中文件的管理,避免混淆.无论如何,关键步骤包括:

    • 创建视图NIB,或者如果以空NIB开始,则向NIB添加视图;

    • 将视图的基类设置为您的UITableViewHeaderFooterView子类(在我的示例中CustomHeader);

    • 在IB中添加控件和约束;

    • 胡克@IBOutlet在您的银行代码网点引用;

    • 把按钮连接到@IBAction; 和

    • 对于NIB中的根视图,请确保将背景颜色设置为"默认",否则您将收到有关更改背景颜色的恼人警告.

  3. viewDidLoad视图控制器中,注册NIB.在Swift 3及更高版本中:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)
    }
    
    Run Code Online (Sandbox Code Playgroud)
  4. viewForHeaderInSection,使用您在上一步中指定的相同标识符使可重用视图出列.完成后,您现在可以使用您的插座,您不必对编程创建的约束等做任何事情.您需要做的唯一事情(对于按钮的协议工作)是指定其委托.例如,在Swift 3中:

    override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeader") as! CustomHeader
    
        headerView.customLabel.text = content[section].name  // set this however is appropriate for your app's model
        headerView.sectionNumber = section
        headerView.delegate = self
    
        return headerView
    }
    
    override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 44  // or whatever
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 显然,如果您要将视图控制器指定delegate为标题视图中的按钮,则必须符合该协议:

    extension ViewController: CustomHeaderDelegate {
        func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int) {
            print("did tap button", section)
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

当我列出所涉及的所有步骤时,这一切听起来都很混乱,但是一旦你做了一次或两次,这真的很简单.我认为它比以编程方式构建标题视图更简单.


亚特的回答中,他抗议:

简单来说,问题在于,你不能通过在身份检查器中声明它而将UIView一个笔尖神奇地变成一个笔尖UITableViewHeaderFooterView.

这根本不正确.如果使用上述基于NIB的方法,则为此标头视图的根视图实例化的类UITableViewHeaderFooterView子类,而不是a UIView.它实例化您为NIB根视图为基类指定的任何类.

但是,正确的是,这个类的某些属性(特别是contentView)在这个基于NIB的方法中没有使用.这真的应该是可选属性,就像textLabeldetailTextLabel是(或更好,还应该加上适当的支持UITableViewHeaderFooterView在IB).我同意这在Apple的设计上是糟糕的设计,但它让我觉得这是一个草率的,特殊的细节,但考虑到表格视图中的所有问题,这是一个小问题.例如,经过这么多年,我们仍然无法在故事板中做原型页眉/页脚视图并且必须完全依赖于这些NIB和类注册技术.

但是,得出一个人不能使用的结论是不正确的,这是一种register(_:forHeaderFooterViewReuseIdentifier:)自iOS 6以来一直在使用的API方法.让我们不要用洗澡水把婴儿扔出去.


请参阅Swift 2再现的此答案的上一版本.

  • 您是否将NIB的文件所有者设置为视图控制器类?如果是这样,当您选择按钮时,视图控制器应该是助理编辑器中"自动"设置下可用的两个类之一. (2认同)
  • 我认为值得一提的是:将'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension'和'self.tableView.estimatedSectionHeaderHeight = 70'添加到'viewDidLoad'.如果您的约束在所有方面都在缩小每个子视图,那么您将获得自定义标题 (2认同)
  • 要重述@Rob在上面的评论中所说的内容,请确保在将IBOutlet挂接到xib时,选择的是自定义类的名称,而不是“文件所有者”。 (2认同)

mat*_*att 31

Rob的回答虽然听起来很有说服力并且经受住了时间的考验,但却是错误的.很难单独反对压倒性的人群"接受的智慧"和无数的赞成,但我会试着鼓起勇气说出真相.

简单来说,问题是你不能仅仅通过在身份检查器中声明它,将nib中的UIView神奇地变成UITableViewHeaderFooterView.UITableViewHeaderFooterView具有对其正确操作至关重要的重要功能,并且无论您如何投射 UIView ,都缺少它们.

  • UITableViewHeaderFooterView有一个contentView,并且必须将所有自定义子视图添加到此,而不是UITableViewHeaderFooterView.

    但是一个UIView神秘地投射为UITableViewHeaderFooterView contentView在笔尖中缺少这个.因此,当Rob说"在IB中添加你的控件和约束"时,他会让你直接将子视图添加到UITableViewHeaderFooterView,而不是contentView.因此,标头最终配置错误.

  • 此问题的另一个迹象是您不允许为UITableViewHeaderFooterView提供背景颜色.如果您这样做,您将在控制台中收到此消息:

    不推荐在UITableViewHeaderFooterView上设置背景颜色.请将具有所需背景颜色的自定义UIView设置为backgroundView属性.

    但是在笔尖中,您无法帮助在UITableViewHeaderFooterView上设置背景颜色,并且您确实在控制台中获得了该消息.

那么这个问题的正确答案是什么?有没有可能的答案.苹果公司在这里做出了巨大的努力.他们提供了一种方法,允许您将nib注册为UITableViewHeaderFooterView的源,但对象库中没有UITableViewHeaderFooterView.因此这种方法毫无用处.在笔尖中正确设计UITableViewHeaderFooterView 是不可能的.

这是Xcode中的一个巨大错误.我在2013年就此问题提交了一份错误报告,它仍然坐在那里,开放.我年复一年地改变了这个漏洞,苹果不断推回,说"尚未确定如何或何时解决问题." 所以他们承认这个错误,但他们对此无动于衷.

但是,你可以做的是在nib中设计一个普通的UIView,然后在代码中(在你的实现中viewForHeaderInSection),从nib手动加载视图并将其填充到contentView标题视图中.

例如,假设我们想要在笔尖中设计标题,并且我们在标题中有一个标签,我们想要连接一个插座lab.然后我们需要一个自定义头类和一个自定义视图类:

class MyHeaderView : UITableViewHeaderFooterView {
    weak var content : MyHeaderViewContent!
}
class MyHeaderViewContent : UIView {
    @IBOutlet weak var lab : UILabel!
}
Run Code Online (Sandbox Code Playgroud)

我们注册了标题视图的,而不是nib:

self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)
Run Code Online (Sandbox Code Playgroud)

在视图xib文件中,我们将视图声明为MyHeaderViewContent - 而不是 MyHeaderView.

viewForHeaderInSection,我们从nib中取出视图,将其填充到contentView标题中,并配置对它的引用:

override func tableView(_ tableView: UITableView, 
    viewForHeaderInSection section: Int) -> UIView? {
    let h = tableView.dequeueReusableHeaderFooterView(
        withIdentifier: self.headerID) as! MyHeaderView
    if h.content == nil {
        let v = UINib(nibName: "MyHeaderView", bundle: nil).instantiate
            (withOwner: nil, options: nil)[0] as! MyHeaderViewContent
        h.contentView.addSubview(v)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.topAnchor.constraint(equalTo: h.contentView.topAnchor).isActive = true
        v.bottomAnchor.constraint(equalTo: h.contentView.bottomAnchor).isActive = true
        v.leadingAnchor.constraint(equalTo: h.contentView.leadingAnchor).isActive = true
        v.trailingAnchor.constraint(equalTo: h.contentView.trailingAnchor).isActive = true
        h.content = v
        // other initializations for all headers go here
    }
    h.content.lab.text = // whatever
    // other initializations for this header go here
    return h
}
Run Code Online (Sandbox Code Playgroud)

这是可怕和烦人的,但它是你能做的最好的.


tri*_*ode 6

创建一个 UITableViewHeaderFooterView 及其对应的 xib 文件。

class BeerListSectionHeader: UITableViewHeaderFooterView {
    @IBOutlet weak var sectionLabel: UILabel!
    @IBOutlet weak var abvLabel: UILabel!
}
Run Code Online (Sandbox Code Playgroud)

注册笔尖与注册表格视图单元格的方式类似。笔尖名称和重用标识符应与您的文件名匹配。(xib 没有重用 ID。)

func registerHeader {
    let nib = UINib(nibName: "BeerListSectionHeader", bundle: nil)
    tableView.register(nib, forHeaderFooterViewReuseIdentifier: "BeerListSectionHeader")
}
Run Code Online (Sandbox Code Playgroud)

出列和使用类似于单元格。标识符是文件名。

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

    let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "BeerListSectionHeader") as! BeerListSectionHeader

    let sectionTitle = allStyles[section].name
    header.sectionLabel.text = sectionTitle
    header.dismissButton?.addTarget(self, action: #selector(dismissView), for: .touchUpInside)
    return header
}
Run Code Online (Sandbox Code Playgroud)

不要忘记标题高度。

 override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
     return BeerListSectionHeader.height
 }
Run Code Online (Sandbox Code Playgroud)