tobeiosdev tobeiosdev - 7 months ago 1453
Swift Question

Swift - How creating custom viewForHeaderInSection, Using a XIB file?

I can create simple custom viewForHeaderInSection in programmatically like below. But I want to do much more complex things maybe connection with a different class and reach their properties like a tableView cell. Simply, I want to see what I do.

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
}


Is there anyone to explain how can I create a custom tableView header view using xib? I have encountered with old Obj-C topics but I'm new with Swift language. If someone explain as detailed, It would be great.


1.issue: Button @IBAction doesn't connect with my ViewController.

2.issue: Header height problem


When I added ImageView even same height constraint with this method in viewController, it flow over tableView rows look like picture.

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 120
}


If I use, automaticallyAdjustsScrollViewInsets in viewDidLoad, In this case image flows under navigationBar. I stack with constraints, How Can I fix them?

self.automaticallyAdjustsScrollViewInsets = false

Rob Rob
Answer

The typical process for NIB based headers would be:

  1. Create UITableViewHeaderFooterView subclass with, at the least, an outlet for your label. You might want to also give it some identifier by which you can reverse engineer to which section this header corresponds:

    class CustomHeader: UITableViewHeaderFooterView {
        @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
    }
    
  2. Create NIB. Personally, I give the NIB the same name as the base class to simplify management of my files in my project and avoid confusion. Anyway, the key steps include:

    • Create view NIB, or if you started with an empty NIB, add view to the NIB;

    • Set the base class of the view to be whatever your UITableViewHeaderFooterView subclass was (in my example, CustomHeader);

    • Add your controls and constraints in IB;

    • Hook up @IBOutlet references to outlets in your Swift code.

  3. For your button, decide whether your @IBAction will be in the UITableViewHeaderFooterView subclass or the view controller. I generally put it in the view controller. To do that:

    • Set the class of the NIB's "File Owner" to be that of your view controller;

    • Hook up the @IBAction from the button to the view controller;

    • In order to know which section you tapped on, you can either iterate through the header views and see which matches the superview of the sender, or if you might give your UITableViewHeaderFooterView some information you can use to determine which header button was tapped (in my example above, I have my CustomHeader a sectionNumber property which I can then access:

      @IBAction func didTapButton(sender: AnyObject) {
          print("tapped", terminator:" ")
          if let headerView = sender.superview as? CustomHeader {
              print("in section \(headerView.sectionNumber)")
          }
      }
      
  4. In the viewDidLoad in the view controller, register the NIB:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        tableView.registerNib(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomHeader")
    }
    
  5. In viewForHeaderInSection, dequeue a reusable view using the same identifier you specified in the prior step. Having done that, you can now use your outlet, you don't have to do anything with programmatically created constraints, etc. For example:

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let headerView = tableView.dequeueReusableHeaderFooterViewWithIdentifier("CustomHeader") as! CustomHeader
    
        headerView.customLabel.text = content[section].name  // set this however is appropriate for your app's model
        headerView.sectionNumber = section
    
        return headerView
    }
    

This all sounds confusing when I list all the steps involved, but it's really quite simple once you've done it once or twice. I think it's simpler than building the header view programmatically.